analysis.lami: Implementation of LAMI plugins
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 26 Apr 2016 22:05:47 +0000 (18:05 -0400)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 17 May 2016 17:59:37 +0000 (13:59 -0400)
If pre-defined scripts are present on the user's system (and on the
$PATH), options under the "External Analyses" trace sub-tree element
become available. Right-clicking on those allows running the specified
analysis, and will import the results as Reports in Trace Compass.

Reports are opened automatically when created, and will initially
display a table of the results returned by the external analysis.
The toolbar menu can then be used to generate charts from this data,
either Histograms or XY scatter plots.

Clicking on chart elemnents highlights the corresponding column in the
table, and vice versa. Also, if the data table rows contain a time
range or timestamp, integration via standard time selection signal
is done.

To not commit to final APIs yet, we've used the "internal.provisional"
naming, as described in [1].

Full user documentation and additional unit tests will follow.

Current limitations:
- The RunAnalysisHandler and others are specific to LAMI analyses,
  which means other types of OnDemandAnalyses would have to define
  their own way of being started (which may not even be through
  a right-click menu option).

[1] https://wiki.eclipse.org/Provisional_API_Guidelines

Change-Id: I188116fa2c0c9a485aabd89d49cae0d6e73136b8
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Signed-off-by: Philippe Proulx <pproulx@efficios.com>
Reviewed-on: https://git.eclipse.org/r/71565
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Tested-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Reviewed-by: Hudson CI
94 files changed:
analysis/org.eclipse.tracecompass.analysis.lami.core/build.properties
analysis/org.eclipse.tracecompass.analysis.lami.core/plugin.properties
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/analysis/lami/core/Activator.java
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/analysis/lami/core/package-info.java
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/LamiStrings.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysis.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysisReport.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiChartModel.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiResultTable.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableClass.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableEntry.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiXYSeriesDescription.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBitrate.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBoolean.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiCPU.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiData.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDisk.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDiskPartition.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDuration.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiEmpty.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiFileDescriptor.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiInteger.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNetworkInterface.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNumber.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiPath.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiProcess.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiRatio.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSize.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiString.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSystemCall.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimeRange.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimestamp.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiUnknown.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiVersion.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/build.properties
analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/histogram.gif [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/table.gif [new file with mode: 0755]
analysis/org.eclipse.tracecompass.analysis.lami.ui/plugin.properties
analysis/org.eclipse.tracecompass.analysis.lami.ui/plugin.xml [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/analysis/lami/ui/Activator.java
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/DeleteReportHandler.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/OpenReportHandler.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/ParameterDialog.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/RunAnalysisHandler.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/LamiSelectionUpdateSignal.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/ILamiViewer.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableContentProvider.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableViewer.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/package-info.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiAxisCheckBoxOption.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportView.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewFactory.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiSeriesDialog.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiViewerControl.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/Messages.java [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/messages.properties [new file with mode: 0644]
analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/package-info.java [new file with mode: 0644]

index 54de195ef996e77ecda5b59f991b9847976a6d12..b7722df4ae0144624a3d85f0c27de5c786438b32 100644 (file)
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2015 EfficiOS Inc. and others
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
 #
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
index 92c619089f7352eea26e7d76bc6d953300b0c05a..3bda8cfe074762dfe9b0eb96f1392300439f3a08 100644 (file)
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2015 EfficiOS Inc. and others
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
 #
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
index 51b4785c8f6420035f2de31594100668790ada10..2ae27202a63ea11d3e967a16fef77be82546f8d5 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 EfficiOS Inc. and others
+ * Copyright (c) 2015, 2016 EfficiOS Inc. and others
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
index 83a6d1a49601d502df0a65927778d73c57656934..f2959ccaf147998fe2047cdeb84de4ac25eaa27c 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 EfficiOS Inc. and others
+ * Copyright (c) 2015, 2016 EfficiOS Inc. and others
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/LamiStrings.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/LamiStrings.java
new file mode 100644 (file)
index 0000000..5455200
--- /dev/null
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core;
+
+/**
+ * Strings used in the LAMI v1.0 protocol.
+ *
+ * The full spec is documented <a href=
+ * "https://github.com/lttng/lami-spec/blob/d6129206184988b3fd7cccb76deace4a69c1443e/lami.md">here</a>.
+ *
+ * @author Alexandre Montplaisir
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+@SuppressWarnings({ "javadoc", "nls" })
+public interface LamiStrings {
+
+    /* Metadata elements */
+    String MI_VERSION = "mi-version";
+    String TITLE = "title";
+    String UNIT = "unit";
+    String TABLE_CLASSES = "table-classes";
+    String COLUMN_DESCRIPTIONS = "column-descriptions";
+    String RESULTS = "results";
+    String TIME_RANGE = "time-range";
+    String CLASS = "class";
+    String DATA = "data";
+    String INHERIT = "inherit";
+
+    /* Data types */
+    String VALUE = "value";
+    String ID = "id";
+    String FD = "fd";
+    String NAME = "name";
+    String PATH = "path";
+
+    /* Time ranges */
+    String BEGIN = "begin";
+    String END = "end";
+
+    /* Process info */
+    String PID = "pid";
+    String TID = "tid";
+
+    /* IRQ stuff */
+    String NR = "nr";
+    String HARD = "hard";
+
+    /* Version object */
+    String MAJOR = "major";
+    String MINOR = "minor";
+    String PATCH = "patch";
+    String EXTRA = "extra";
+
+    /* Data classes */
+    String DATA_CLASS_UNKNOWN = "unknown";
+    String DATA_CLASS_RATIO = "ratio";
+    String DATA_CLASS_TIMESTAMP = "timestamp";
+    String DATA_CLASS_TIME_RANGE = "time-range";
+    String DATA_CLASS_DURATION = "duration";
+    String DATA_CLASS_SIZE = "size";
+    String DATA_CLASS_BITRATE = "bitrate";
+    String DATA_CLASS_SYSCALL = "syscall";
+    String DATA_CLASS_PROCESS = "process";
+    String DATA_CLASS_PATH = "path";
+    String DATA_CLASS_FD = "fd";
+    String DATA_CLASS_IRQ = "irq";
+    String DATA_CLASS_CPU = "cpu";
+    String DATA_CLASS_DISK = "disk";
+    String DATA_CLASS_PART = "part";
+    String DATA_CLASS_NETIF = "netif";
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiDurationAspect.java
new file mode 100644 (file)
index 0000000..069b8f7
--- /dev/null
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiDuration;
+
+/**
+ * Aspect for a time range duration
+ *
+ * @author Michael Jeanson
+ */
+public class LamiDurationAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiDurationAspect(String colName, int colIndex) {
+        super(colName, "ns"); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return true;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeDuration() {
+        return true;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiDuration) {
+            LamiDuration duration = (LamiDuration) data;
+            return String.valueOf(duration.getValue());
+        }
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiDuration) {
+            LamiDuration range = (LamiDuration) data;
+            return Double.valueOf(range.getValue());
+        }
+        return null;
+    }
+
+    @Override
+    public @NonNull Comparator<@NonNull LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiEmptyAspect.java
new file mode 100644 (file)
index 0000000..49004a7
--- /dev/null
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+
+/**
+ * Null aspect for LAMI tables, normally printing nothing in the corresponding
+ * cells. Should be access using {@link #INSTANCE}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiEmptyAspect extends LamiTableEntryAspect {
+
+    /** Singleton instance */
+    public static final LamiEmptyAspect INSTANCE = new LamiEmptyAspect();
+
+    private LamiEmptyAspect() {
+        super("", null); //$NON-NLS-1$
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> 0;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiGenericAspect.java
new file mode 100644 (file)
index 0000000..075bdcc
--- /dev/null
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+
+/**
+ * Base class for LAMI table aspects.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiGenericAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+    private final boolean fIsContinuous;
+    private final boolean fIsTimeStamp;
+
+    /**
+     * Constructor
+     *
+     * @param aspectName
+     *            Name of the aspect (name of the column in the UI)
+     * @param units
+     *            The units of this column
+     * @param colIndex
+     *            Index of this column
+     * @param isContinuous
+     *            If the contents of this column are numbers or not
+     * @param isTimeStamp
+     *            If the contents of this column are numerical timestamp or not
+     */
+    public LamiGenericAspect(String aspectName, @Nullable String units, int colIndex, boolean isContinuous, boolean isTimeStamp) {
+        super(aspectName, units);
+        fColIndex = colIndex;
+        fIsContinuous = isContinuous;
+        fIsTimeStamp = isTimeStamp;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return fIsContinuous;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return fIsTimeStamp;
+    }
+
+    @Override
+    public @Nullable String resolveString(@NonNull LamiTableEntry entry) {
+        return entry.getValue(fColIndex).toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        if (fIsContinuous) {
+            try {
+                if (entry.getValue(fColIndex).toString() != null) {
+                    return Double.parseDouble(entry.getValue(fColIndex).toString());
+                }
+            } catch (NumberFormatException e) {
+                // Fallback to default value below
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        if (isContinuous()) {
+            return (o1, o2) -> {
+                Double dO1 = resolveDouble(o1);
+                Double dO2 = resolveDouble(o2);
+                if (dO1 == null || dO2 == null) {
+                    return 0;
+                }
+
+                return dO1.compareTo(dO2);
+            };
+        }
+
+        /* Use regular string comparison */
+        return (o1, o2) -> {
+            String s1 = resolveString(o1);
+            String s2 = resolveString(o2);
+
+            if (s1 == null || s2 == null) {
+                return 0;
+            }
+
+            return s1.compareTo(s2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNameAspect.java
new file mode 100644 (file)
index 0000000..f4dc201
--- /dev/null
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiIRQ;
+
+/**
+ * Aspect for the IRQ handler names.
+ *
+ * This resolves the interrupt handler name, (like i915) from a given table
+ * entry.
+ *
+ * @author Philippe Proulx
+ */
+public class LamiIRQNameAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiIRQNameAspect(String colName, int colIndex) {
+        super(colName + " (" + Messages.LamiAspect_Name +')', null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiIRQ) {
+            return ((LamiIRQ) data).getName();
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            String s1 = resolveString(o1);
+            String s2 = resolveString(o2);
+
+            if (s1 == null || s2 == null) {
+                return 0;
+            }
+
+            return s1.compareTo(s2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQNumberAspect.java
new file mode 100644 (file)
index 0000000..81da9b3
--- /dev/null
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiIRQ;
+
+/**
+ * Aspect for the IRQ numbers.
+ *
+ * This resolves the IRQ number for a given table, so 0|timer would return 0.
+ *
+ * @author Philippe Proulx
+ */
+public class LamiIRQNumberAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiIRQNumberAspect(String colName, int colIndex) {
+        super(colName + " (#)", null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiIRQ) {
+            return String.valueOf(((LamiIRQ) data).getNumber());
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiIRQ) {
+            return Double.valueOf(((LamiIRQ) data).getNumber());
+        }
+
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiIRQTypeAspect.java
new file mode 100644 (file)
index 0000000..dbd6fcb
--- /dev/null
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiIRQ;
+
+/**
+ * Aspect for IRQ type, indicating if it is a hardware IRQ or software IRQ.
+ *
+ * @author Philippe Proulx
+ */
+public class LamiIRQTypeAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiIRQTypeAspect(String colName, int colIndex) {
+        super(colName + " (" + Messages.LamiAspect_Type +')', null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiIRQ) {
+            LamiIRQ irq = (LamiIRQ) data;
+
+            switch (irq.getType()) {
+            case HARD:
+                return Messages.LamiIRQTypeAspect_HardwareIRQ;
+
+            case SOFT:
+                return Messages.LamiIRQTypeAspect_SoftIRQ;
+
+            default:
+                return "?"; //$NON-NLS-1$
+            }
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            String s1 = resolveString(o1);
+            String s2 = resolveString(o2);
+
+            if (s1 == null || s2 == null) {
+                return 0;
+            }
+
+            return s1.compareTo(s2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiMixedAspect.java
new file mode 100644 (file)
index 0000000..07b745b
--- /dev/null
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData.DataType;
+
+/**
+ * Aspect for LAMI mixed types.
+ *
+ * The data in this column can contain any class of data.
+ *
+ * @author Philippe Proulx
+ */
+public class LamiMixedAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiMixedAspect(String colName, int colIndex) {
+        super(colName, null);
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        Class<? extends LamiData> cls = data.getClass();
+
+        DataType dataType = DataType.fromClass(cls);
+
+        if (dataType == null) {
+            return data.toString();
+        }
+
+        String str = data.toString();
+
+        if (dataType.getUnits() != null) {
+            str += " " + dataType.getUnits(); //$NON-NLS-1$
+        }
+
+        return str;
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            String s1 = resolveString(o1);
+            String s2 = resolveString(o2);
+
+            if (s1 == null || s2 == null) {
+                return 0;
+            }
+
+            return s1.compareTo(s2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessNameAspect.java
new file mode 100644 (file)
index 0000000..c9278b3
--- /dev/null
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiProcess;
+
+/**
+ * Aspect for process names
+ *
+ * @author Philippe Proulx
+ */
+public class LamiProcessNameAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiProcessNameAspect(String colName, int colIndex) {
+        super(colName + " (" + Messages.LamiAspect_Name +')', null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiProcess) {
+            return ((LamiProcess) data).getName();
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            String s1 = resolveString(o1);
+            String s2 = resolveString(o2);
+
+            if (s1 == null || s2 == null) {
+                return 0;
+            }
+
+            return s1.compareTo(s2);
+        };
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessPIDAspect.java
new file mode 100644 (file)
index 0000000..b242fa5
--- /dev/null
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiProcess;
+
+/**
+ * Aspect for process PID
+ *
+ * @author Philippe Proulx
+ */
+public class LamiProcessPIDAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiProcessPIDAspect(String colName, int colIndex) {
+        super(colName + " (PID)", null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiProcess) {
+            Long pid = ((LamiProcess) data).getPID();
+
+            if (pid == null) {
+                return null;
+            }
+
+            return pid.toString();
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiProcess) {
+            Long pid = ((LamiProcess) data).getPID();
+
+            if (pid == null) {
+                return null;
+            }
+
+            return Double.valueOf(pid);
+        }
+
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiProcessTIDAspect.java
new file mode 100644 (file)
index 0000000..5fea94a
--- /dev/null
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiProcess;
+
+/**
+ * Aspect for process TID
+ *
+ * @author Philippe Proulx
+ */
+public class LamiProcessTIDAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param colName
+     *            Column name
+     * @param colIndex
+     *            Column index
+     */
+    public LamiProcessTIDAspect(String colName, int colIndex) {
+        super(colName + " (TID)", null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiProcess) {
+            Long tid = ((LamiProcess) data).getTID();
+
+            if (tid == null) {
+                return null;
+            }
+
+            return tid.toString();
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double  resolveDouble(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiProcess) {
+            Long tid = ((LamiProcess) data).getTID();
+
+            if (tid == null) {
+                return null;
+            }
+
+            return Double.valueOf(tid);
+        }
+
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTableEntryAspect.java
new file mode 100644 (file)
index 0000000..a070562
--- /dev/null
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+
+/**
+ * Aspect for LAMI table entries, which normally correspond to one "row"
+ * of JSON output.
+ *
+ * It is not the same as a "Event aspect" used for trace events, but it is
+ * heavily inspired from it.
+ *
+ * @author Alexandre Montplaisir
+ * @see org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect
+ */
+public abstract class LamiTableEntryAspect {
+
+    private final String fName;
+    private final @Nullable String fUnits;
+
+    /**
+     * Constructor
+     *
+     * @param name
+     *            Aspect name, will be used as column name in the UI
+     * @param units
+     *            The units of the value in this column
+     */
+    protected LamiTableEntryAspect(String name, @Nullable String units) {
+        fUnits = units;
+        fName = name;
+    }
+
+    /**
+     * Get the label of this aspect.
+     *
+     * The label is composed of the name followed by the units in parentheses.
+     *
+     * @return The label
+     */
+    public String getLabel() {
+        if (getUnits() == null) {
+            return getName();
+        }
+        return (getName() + " (" + getUnits() + ')'); //$NON-NLS-1$
+    }
+
+    /**
+     * Get the name of this aspect
+     *
+     * @return The name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Get the units of this aspect.
+     *
+     * @return The units
+     */
+    public @Nullable String getUnits() {
+        return fUnits;
+    }
+
+    /**
+     * Indicate if this aspect is numerical or not. This is used, among other
+     * things, to align the text in the table cells.
+     *
+     * @return If this aspect is numerical or not
+     */
+    public abstract boolean isContinuous();
+
+
+    /**
+     * Indicate if this aspect represent timestamp or not. This can be used in chart
+     * for axis labeling etc.
+     * @return  If this aspect represent a timestamp or not
+     */
+    public abstract boolean isTimeStamp();
+
+    /**
+     * Indicate if this aspect represent a time duration or not. This can be used in
+     * chart for axis labeling etc.
+     * @return  If this aspect represent a time duration or not
+     */
+    public boolean isTimeDuration() {
+        return false;
+    }
+
+    /**
+     * Resolve this aspect for the given entry.
+     *
+     * @param entry
+     *            The table row
+     * @return The string to display for the given cell
+     */
+    public abstract @Nullable String resolveString(LamiTableEntry entry);
+
+    /**
+     * Resolve this aspect double representation for the given entry
+     *
+     * Returned value does not matter if isNumerical() is false.
+     *
+     * @param entry
+     *            The table row
+     * @return The double value for the given cell
+     */
+    public abstract @Nullable Double resolveDouble(LamiTableEntry entry);
+
+    /**
+     * Get the comparator that should be used to compare this entry (or table
+     * row) with the other rows in the table. This will be passed on to the
+     * table's content provider.
+     *
+     * @return The entry comparator
+     */
+    public abstract Comparator<LamiTableEntry> getComparator();
+
+    /**
+     * Check if an aspect have the same properties.
+     *
+     * FIXME:Might want to compare the units if necessary.
+     *
+     * @param aspect
+     *            The aspect to compare to
+     * @return If all aspect's properties are equal
+     */
+    public boolean arePropertiesEqual(LamiTableEntryAspect aspect) {
+        boolean timestamp = (this.isTimeStamp() == aspect.isTimeStamp());
+        boolean numerical = (this.isContinuous() == aspect.isContinuous());
+        return (timestamp && numerical);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeBeginAspect.java
new file mode 100644 (file)
index 0000000..abb160e
--- /dev/null
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
+
+/**
+ * Aspect for beginning timestamp of a timerange
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimeRangeBeginAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param timeRangeName
+     *            Name of the time range
+     * @param colIndex
+     *            Column index
+     */
+    public LamiTimeRangeBeginAspect(String timeRangeName, int colIndex) {
+        super(timeRangeName + " (" + Messages.LamiAspect_TimeRangeBegin + ')', null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return true;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return true;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return TmfTimestampFormat.getDefaulTimeFormat().format(range.getStart());
+        }
+        /* Could be null, unknown, etc. */
+        return data.toString();
+    }
+
+
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return Double.valueOf(range.getStart());
+        }
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeDurationAspect.java
new file mode 100644 (file)
index 0000000..6b1fe35
--- /dev/null
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+
+/**
+ * Aspect for a time range duration
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimeRangeDurationAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param timeRangeName
+     *            Name of the time range
+     * @param colIndex
+     *            Column index
+     */
+    public LamiTimeRangeDurationAspect(String timeRangeName, int colIndex) {
+        super(timeRangeName + " (" + Messages.LamiAspect_TimeRangeDuration + ')', "ns"); //$NON-NLS-1$ //$NON-NLS-2$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return true;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return false;
+    }
+
+    @Override
+    public boolean isTimeDuration() {
+        return true;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return String.valueOf(range.getDuration());
+        }
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return Double.valueOf(range.getDuration());
+        }
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimeRangeEndAspect.java
new file mode 100644 (file)
index 0000000..997989a
--- /dev/null
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
+
+/**
+ * Aspect for a time range duration
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimeRangeEndAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param timeRangeName
+     *            Name of the time range
+     * @param colIndex
+     *            Column index
+     */
+    public LamiTimeRangeEndAspect(String timeRangeName, int colIndex) {
+        super(timeRangeName + " (" + Messages.LamiAspect_TimeRangeEnd + ')', null); //$NON-NLS-1$
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return true;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return TmfTimestampFormat.getDefaulTimeFormat().format(range.getEnd());
+        }
+        return data.toString();
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return true;
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiTimeRange) {
+            LamiTimeRange range = (LamiTimeRange) data;
+            return Double.valueOf(range.getEnd());
+        }
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/LamiTimestampAspect.java
new file mode 100644 (file)
index 0000000..6f41e48
--- /dev/null
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiInteger;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
+
+/**
+ * Aspect for timestamps
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimestampAspect extends LamiTableEntryAspect {
+
+    private final int fColIndex;
+
+    /**
+     * Constructor
+     *
+     * @param timestampName
+     *            Name of the timestamp
+     * @param colIndex
+     *            Column index
+     */
+    public LamiTimestampAspect(String timestampName, int colIndex) {
+        super(timestampName, null);
+        fColIndex = colIndex;
+    }
+
+    @Override
+    public boolean isContinuous() {
+        return true;
+    }
+
+    @Override
+    public boolean isTimeStamp() {
+        return true;
+    }
+
+    @Override
+    public @Nullable String resolveString(LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiInteger) {
+            LamiInteger range = (LamiInteger) data;
+            return TmfTimestampFormat.getDefaulTimeFormat().format(range.getValue());
+        }
+        return data.toString();
+    }
+
+    @Override
+    public @Nullable Double resolveDouble(@NonNull LamiTableEntry entry) {
+        LamiData data = entry.getValue(fColIndex);
+        if (data instanceof LamiInteger) {
+            LamiInteger range = (LamiInteger) data;
+            return Double.valueOf(range.getValue());
+        }
+        return null;
+    }
+
+    @Override
+    public Comparator<LamiTableEntry> getComparator() {
+        return (o1, o2) -> {
+            Double dO1 = resolveDouble(o1);
+            Double dO2 = resolveDouble(o2);
+            if (dO1 == null || dO2 == null) {
+                return 0;
+            }
+
+            return dO1.compareTo(dO2);
+        };
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/Messages.java
new file mode 100644 (file)
index 0000000..e8b0567
--- /dev/null
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiAspect_Name;
+    public static String LamiAspect_Type;
+
+    public static String LamiAspect_TimeRangeBegin;
+    public static String LamiAspect_TimeRangeDuration;
+    public static String LamiAspect_TimeRangeEnd;
+
+    public static String LamiIRQTypeAspect_HardwareIRQ;
+    public static String LamiIRQTypeAspect_SoftIRQ;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/messages.properties
new file mode 100644 (file)
index 0000000..2cd9903
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiAspect_Name = name
+LamiAspect_Type = type
+
+LamiAspect_TimeRangeBegin = begin
+LamiAspect_TimeRangeDuration = duration
+LamiAspect_TimeRangeEnd = end
+
+LamiIRQTypeAspect_HardwareIRQ = Hard
+LamiIRQTypeAspect_SoftIRQ = Soft
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/aspect/package-info.java
new file mode 100644 (file)
index 0000000..1cd8944
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysis.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysis.java
new file mode 100644 (file)
index 0000000..f11f6c9
--- /dev/null
@@ -0,0 +1,848 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNullContents;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Predicate;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.analysis.lami.core.Activator;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.LamiStrings;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiGenericAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiIRQNameAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiIRQNumberAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiIRQTypeAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiMixedAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiProcessNameAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiProcessPIDAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiProcessTIDAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiDurationAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiEmptyAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTimeRangeBeginAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTimeRangeDurationAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTimeRangeEndAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTimestampAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData.DataType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysis;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.OnDemandAnalysisException;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Base class for analysis modules that call external scripts implementing the
+ * LAMI protocol.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiAnalysis implements IOnDemandAnalysis {
+
+    /** Maximum major version of the LAMI protocol we support */
+    private static final int MAX_SUPPORTED_MAJOR_VERSION = 1;
+
+    private static final String DOUBLE_QUOTES = "\""; //$NON-NLS-1$
+
+    /* Flags passed to the analysis scripts */
+    private static final String METADATA_FLAG = "--metadata"; //$NON-NLS-1$
+    private static final String PROGRESS_FLAG = "--output-progress"; //$NON-NLS-1$
+    private static final String BEGIN_FLAG = "--begin"; //$NON-NLS-1$
+    private static final String END_FLAG = "--end"; //$NON-NLS-1$
+
+    /** Log message for commands being run */
+    private static final String RUNNING_MESSAGE = "Running command:"; //$NON-NLS-1$
+
+    private final List<String> fScriptCommand;
+
+    /**
+     * The LAMI analysis is considered initialized after we have read the
+     * script's --metadata once. This will assign the fields below.
+     */
+    private boolean fInitialized = false;
+
+    private boolean fIsAvailable;
+    private final String fName;
+    private final boolean fIsUserDefined;
+    private final Predicate<ITmfTrace> fAppliesTo;
+
+    /* Data defined by the analysis's metadata */
+    private @Nullable String fAnalysisTitle;
+    private @Nullable Map<String, LamiTableClass> fTableClasses;
+    private boolean fUseProgressOutput;
+
+    /**
+     * Constructor. To be called by implementing classes.
+     *
+     * @param name
+     *            Name of this analysis
+     * @param isUserDefined
+     *            {@code true} if this is a user-defined analysis
+     * @param appliesTo
+     *            Predicate to use to check whether or not this analysis applies
+     *            to a given trace
+     * @param args
+     *            Analysis arguments, including the executable name (first
+     *            argument)
+     */
+    public LamiAnalysis(String name, boolean isUserDefined, Predicate<ITmfTrace> appliesTo,
+            List<String> args) {
+        fScriptCommand = ImmutableList.copyOf(args);
+        fName = name;
+        fIsUserDefined = isUserDefined;
+        fAppliesTo = appliesTo;
+    }
+
+    /**
+     * Map of pre-defined charts, for every table class names.
+     *
+     * If a table class is not in this map then it means that table has no
+     * predefined charts.
+     *
+     * @return The chart models, per table class names
+     */
+    protected Multimap<String, LamiChartModel> getPredefinedCharts() {
+        return ImmutableMultimap.of();
+    }
+
+    @Override
+    public boolean appliesTo(ITmfTrace trace) {
+        return fAppliesTo.test(trace);
+    }
+
+    @Override
+    public boolean canExecute(ITmfTrace trace) {
+        initialize();
+        return fIsAvailable;
+    }
+
+    private synchronized void initialize() {
+        if (fInitialized) {
+            return;
+        }
+
+        /* Do the analysis's initialization */
+
+        /* Check if the script's expected executable is on the PATH */
+        String executable = fScriptCommand.get(0);
+        boolean exists = Stream.of(System.getenv("PATH").split(checkNotNull(Pattern.quote(File.pathSeparator)))) //$NON-NLS-1$
+                .map(Paths::get)
+                .anyMatch(path -> Files.exists(path.resolve(executable)));
+        if (!exists) {
+            /* Script is not found */
+            fIsAvailable = false;
+            fInitialized = true;
+            return;
+        }
+
+        fIsAvailable = checkMetadata();
+        fInitialized = true;
+    }
+
+    private boolean checkMetadata() {
+        /*
+         * The initialize() phase of the analysis will be used to check the
+         * script's metadata. Actual runs of the script will use the execute()
+         * method below.
+         */
+        List<String> command = ImmutableList.<@NonNull String> builder()
+                .addAll(fScriptCommand).add(METADATA_FLAG).build();
+
+        Activator.instance().logInfo(RUNNING_MESSAGE + ' ' + command.toString());
+
+        String output = getOutputFromCommand(command);
+        if (output == null || output.isEmpty()) {
+            return false;
+        }
+
+        /*
+         *
+         * Metadata should look this this:
+         *
+         * {
+         *   "version": [1, 5, 2, "dev"],
+         *   "title": "I/O latency statistics",
+         *   "authors": [
+         *     "Julien Desfossez",
+         *     "Antoine Busque"
+         *   ],
+         *   "description": "Provides statistics about the latency involved in various I/O operations.",
+         *   "url": "https://github.com/lttng/lttng-analyses",
+         *   "tags": [
+         *     "io",
+         *     "stats",
+         *     "linux-kernel",
+         *     "lttng-analyses"
+         *   ],
+         *   "table-classes": {
+         *     "syscall-latency": {
+         *       "title": "System calls latency statistics",
+         *       "column-descriptions": [
+         *         {"title": "System call", "type": "syscall"},
+         *         {"title": "Count", "type": "int", "unit": "operations"},
+         *         {"title": "Minimum duration", "type": "duration"},
+         *         {"title": "Average duration", "type": "duration"},
+         *         {"title": "Maximum duration", "type": "duration"},
+         *         {"title": "Standard deviation", "type": "duration"}
+         *       ]
+         *     },
+         *     "disk-latency": {
+         *       "title": "Disk latency statistics",
+         *       "column-descriptions": [
+         *         {"title": "Disk name", "type": "disk"},
+         *         {"title": "Count", "type": "int", "unit": "operations"},
+         *         {"title": "Minimum duration", "type": "duration"},
+         *         {"title": "Average duration", "type": "duration"},
+         *         {"title": "Maximum duration", "type": "duration"},
+         *         {"title": "Standard deviation", "type": "duration"}
+         *       ]
+         *     }
+         *   }
+         * }
+         *
+         */
+
+        try {
+            JSONObject obj = new JSONObject(output);
+            fAnalysisTitle = obj.getString(LamiStrings.TITLE);
+
+            /* Very early scripts may not contain the "mi-version" */
+            JSONObject miVersion = obj.optJSONObject(LamiStrings.MI_VERSION);
+            if (miVersion == null) {
+                /* Before version 0.1 */
+                fUseProgressOutput = false;
+            } else {
+                int majorVersion = miVersion.getInt(LamiStrings.MAJOR);
+                if (majorVersion <= MAX_SUPPORTED_MAJOR_VERSION) {
+                    fUseProgressOutput = true;
+                } else {
+                    /* Unknown version, we do not support it */
+                    return false;
+                }
+            }
+
+            JSONObject tableClasses = obj.getJSONObject(LamiStrings.TABLE_CLASSES);
+            @NonNull String[] tableClassNames = checkNotNullContents(JSONObject.getNames(tableClasses));
+
+            ImmutableMap.Builder<String, LamiTableClass> tablesBuilder = ImmutableMap.builder();
+            for (String tableClassName : tableClassNames) {
+                JSONObject tableClass = tableClasses.getJSONObject(tableClassName);
+
+                final String tableTitle = checkNotNull(tableClass.getString(LamiStrings.TITLE));
+                @NonNull JSONArray columnDescriptions = checkNotNull(tableClass.getJSONArray(LamiStrings.COLUMN_DESCRIPTIONS));
+
+                List<LamiTableEntryAspect> aspects = getAspectsFromColumnDescriptions(columnDescriptions);
+                Collection<LamiChartModel> chartModels = getPredefinedCharts().get(tableClassName);
+
+                tablesBuilder.put(tableClassName, new LamiTableClass(tableClassName, tableTitle, aspects, chartModels));
+            }
+
+            try {
+                fTableClasses = tablesBuilder.build();
+            } catch (IllegalArgumentException e) {
+                /*
+                 * This is thrown if there are duplicate keys in the map
+                 * builder.
+                 */
+                throw new JSONException("Duplicate table class entry in " + fAnalysisTitle); //$NON-NLS-1$
+            }
+
+        } catch (JSONException e) {
+            /* Error in the parsing of the JSON, script is broken? */
+            Activator.instance().logError(e.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    private static List<LamiTableEntryAspect> getAspectsFromColumnDescriptions(JSONArray columnDescriptions) throws JSONException {
+        ImmutableList.Builder<LamiTableEntryAspect> aspectsBuilder = new ImmutableList.Builder<>();
+        for (int j = 0; j < columnDescriptions.length(); j++) {
+            JSONObject column = columnDescriptions.getJSONObject(j);
+            DataType columnDataType;
+            String columnClass = column.optString(LamiStrings.CLASS, null);
+
+            if (columnClass == null) {
+                columnDataType = DataType.MIXED;
+            } else {
+                columnDataType = getDataTypeFromString(columnClass);
+            }
+
+            String columnTitle = column.optString(LamiStrings.TITLE, null);
+
+            if (columnTitle == null) {
+                columnTitle = String.format("%s #%d", columnDataType.getTitle(), j + 1); //$NON-NLS-1$
+            }
+
+            final int colIndex = j;
+            switch (columnDataType) {
+            case TIME_RANGE:
+                /*
+                 * We will add 3 aspects, to represent the start, end and
+                 * duration of this time range.
+                 */
+                aspectsBuilder.add(new LamiTimeRangeBeginAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiTimeRangeEndAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiTimeRangeDurationAspect(columnTitle, colIndex));
+                break;
+
+            case TIMESTAMP:
+                aspectsBuilder.add(new LamiTimestampAspect(columnTitle, colIndex));
+                break;
+
+            case PROCESS:
+                aspectsBuilder.add(new LamiProcessNameAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiProcessPIDAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiProcessTIDAspect(columnTitle, colIndex));
+                break;
+
+            case IRQ:
+                aspectsBuilder.add(new LamiIRQTypeAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiIRQNameAspect(columnTitle, colIndex));
+                aspectsBuilder.add(new LamiIRQNumberAspect(columnTitle, colIndex));
+                break;
+
+            case DURATION:
+                aspectsBuilder.add(new LamiDurationAspect(columnTitle, colIndex));
+                break;
+
+            case MIXED:
+                aspectsBuilder.add(new LamiMixedAspect(columnTitle, colIndex));
+                break;
+
+            // $CASES-OMITTED$
+            default:
+                String units = column.optString(LamiStrings.UNIT, null);
+
+                if (units == null) {
+                    units = columnDataType.getUnits();
+                }
+
+                /* We will add only one aspect representing the element */
+                LamiTableEntryAspect aspect = new LamiGenericAspect(columnTitle,
+                        units, colIndex, columnDataType.isContinuous(), false);
+                aspectsBuilder.add(aspect);
+                break;
+            }
+        }
+        /*
+         * SWT quirk : we need an empty column at the end or else the last data
+         * column will clamp to the right edge of the view if it is
+         * right-aligned.
+         */
+        aspectsBuilder.add(LamiEmptyAspect.INSTANCE);
+
+        return aspectsBuilder.build();
+    }
+
+    private static DataType getDataTypeFromString(String value) throws JSONException {
+        try {
+            return DataType.fromString(value);
+        } catch (IllegalArgumentException e) {
+            throw new JSONException("Unrecognized data type: " + value); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Get the title of this analysis, as read from the script's metadata.
+     *
+     * @return The analysis title. Should not be null after the initialization
+     *         completed successfully.
+     */
+    public @Nullable String getAnalysisTitle() {
+        return fAnalysisTitle;
+    }
+
+    /**
+     * Get the result table classes defined by this analysis, as read from the
+     * script's metadata.
+     *
+     * @return The analysis' result table classes. Should not be null after the
+     *         execution completed successfully.
+     */
+    public @Nullable Map<String, LamiTableClass> getTableClasses() {
+        return fTableClasses;
+    }
+
+    /**
+     * Print the full command that will be run when calling {@link #execute},
+     * with the exception of the 'extraParams' that will be passed to execute().
+     *
+     * This can be used to display the command in the UI before it is actually
+     * run.
+     *
+     * @param trace
+     *            The trace on which to run the analysis
+     * @param range
+     *            The time range to specify. Null will not specify a time range,
+     *            which means the whole trace will be taken.
+     * @return The command as a single, space-separated string
+     */
+    public String getFullCommandAsString(ITmfTrace trace, @Nullable TmfTimeRange range) {
+        String tracePath = checkNotNull(trace.getPath());
+
+        ImmutableList.Builder<String> builder = getBaseCommand(range);
+        /*
+         * We can add double-quotes around the trace path, which could contain
+         * spaces, so that the resulting command can be easily copy-pasted into
+         * a shell.
+         */
+        builder.add(DOUBLE_QUOTES + tracePath + DOUBLE_QUOTES);
+        List<String> list = builder.build();
+        String ret = list.stream().collect(Collectors.joining(" ")); //$NON-NLS-1$
+        return checkNotNull(ret);
+    }
+
+    /**
+     * Get the base part of the command that will be executed to run this
+     * analysis, supplying the given time range. Base part meaning:
+     *
+     * <pre>
+     * [script executable] [statically-defined parameters] [--begin/--end (if applicable)]
+     * </pre>
+     *
+     * Note that it does not include the path to the trace, that is to be added
+     * separately.
+     *
+     * @param range
+     *            The time range that will be passed
+     * @return The elements of the command
+     */
+    private ImmutableList.Builder<String> getBaseCommand(@Nullable TmfTimeRange range) {
+        long begin = 0;
+        long end = 0;
+        if (range != null) {
+            begin = range.getStartTime().getValue();
+            end = range.getEndTime().getValue();
+        }
+
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+        builder.addAll(fScriptCommand);
+
+        if (fUseProgressOutput) {
+            builder.add(PROGRESS_FLAG);
+        }
+
+        if (range != null) {
+            builder.add(BEGIN_FLAG).add(String.valueOf(begin));
+            builder.add(END_FLAG).add(String.valueOf(end));
+        }
+        return builder;
+    }
+
+    /**
+     * Call the currently defined LAMI script with the given arguments.
+     *
+     * @param timeRange
+     *            The time range. Null for the whole trace.
+     * @param monitor
+     *            The progress monitor used to report progress
+     * @return The script's output, formatted into {@link LamiTableEntry}'s.
+     * @throws OnDemandAnalysisException
+     *             If execution did not terminate normally
+     */
+    @Override
+    public List<LamiResultTable> execute(ITmfTrace trace, @Nullable TmfTimeRange timeRange,
+            String extraParams, IProgressMonitor monitor) throws OnDemandAnalysisException {
+        /* Should have been called already, but in case it was not */
+        initialize();
+
+        final @NonNull String tracePath = checkNotNull(trace.getPath());
+        final @NonNull String[] splitParams = extraParams.trim().split(" "); //$NON-NLS-1$
+
+        ImmutableList.Builder<String> builder = getBaseCommand(timeRange);
+
+        if (!extraParams.trim().equals("")) { //$NON-NLS-1$
+            builder.addAll(Arrays.asList(splitParams));
+        }
+        builder.add(tracePath);
+        List<String> command = builder.build();
+
+        Activator.instance().logInfo(RUNNING_MESSAGE + ' ' + command.toString());
+        String output = getResultsFromCommand(command, monitor);
+
+        if (output.isEmpty()) {
+            throw new OnDemandAnalysisException(Messages.LamiAnalysis_NoResults);
+        }
+
+        /*
+         * {
+         *   "results": [
+         *     {
+         *       "time-range": {
+         *         "type": "time-range",
+         *         "begin": 1444334398154194201,
+         *         "end": 1444334425194487548
+         *       },
+         *       "class": "syscall-latency",
+         *       "data": [
+         *         [
+         *           {"type": "syscall", "name": "open"},
+         *           45,
+         *           {"type": "duration", "value": 5562},
+         *           {"type": "duration", "value": 13835},
+         *           {"type": "duration", "value": 77683},
+         *           {"type": "duration", "value": 15263}
+         *         ],
+         *         [
+         *           {"type": "syscall", "name": "read"},
+         *           109,
+         *           {"type": "duration", "value": 316},
+         *           {"type": "duration", "value": 5774},
+         *           {"type": "duration", "value": 62569},
+         *           {"type": "duration", "value": 9277}
+         *         ]
+         *       ]
+         *     },
+         *     {
+         *       "time-range": {
+         *         "type": "time-range",
+         *         "begin": 1444334425194487549,
+         *         "end": 1444334425254887190
+         *       },
+         *       "class": "syscall-latency",
+         *       "data": [
+         *         [
+         *           {"type": "syscall", "name": "open"},
+         *           45,
+         *           {"type": "duration", "value": 1578},
+         *           {"type": "duration", "value": 16648},
+         *           {"type": "duration", "value": 15444},
+         *           {"type": "duration", "value": 68540}
+         *         ],
+         *         [
+         *           {"type": "syscall", "name": "read"},
+         *           109,
+         *           {"type": "duration", "value": 78},
+         *           {"type": "duration", "value": 1948},
+         *           {"type": "duration", "value": 11184},
+         *           {"type": "duration", "value": 94670}
+         *         ]
+         *       ]
+         *     }
+         *   ]
+         * }
+         *
+         */
+
+        ImmutableList.Builder<LamiResultTable> resultsBuilder = new ImmutableList.Builder<>();
+
+        try {
+            JSONObject obj = new JSONObject(output);
+            JSONArray results = obj.getJSONArray(LamiStrings.RESULTS);
+
+            if (results.length() == 0) {
+                /*
+                 * No results were reported. This may be normal, but warn the
+                 * user why a report won't be created.
+                 */
+                throw new OnDemandAnalysisException(Messages.LamiAnalysis_NoResults);
+            }
+
+            for (int i = 0; i < results.length(); i++) {
+                JSONObject result = results.getJSONObject(i);
+
+                /* Parse the time-range */
+                JSONObject trObject = result.getJSONObject(LamiStrings.TIME_RANGE);
+                long start = trObject.getLong(LamiStrings.BEGIN);
+                long end = trObject.getLong(LamiStrings.END);
+                LamiTimeRange tr = new LamiTimeRange(start, end);
+
+                /* Parse the table's class */
+                LamiTableClass tableClass;
+                JSONObject tableClassObject = result.optJSONObject(LamiStrings.CLASS);
+                if (tableClassObject == null) {
+                    /*
+                     * "class" is just a standard string, indicating we use a
+                     * metadata-defined table class as-is
+                     */
+                    @NonNull String tableClassName = checkNotNull(result.getString(LamiStrings.CLASS));
+                    tableClass = getTableClassFromName(tableClassName);
+
+                    // FIXME Rest will become more generic eventually in the LAMI format.
+                } else if (tableClassObject.has(LamiStrings.INHERIT)) {
+                    /*
+                     * Dynamic title: We reuse an existing table class but
+                     * override the title.
+                     */
+                    String baseTableName = checkNotNull(tableClassObject.getString(LamiStrings.INHERIT));
+                    LamiTableClass baseTableClass = getTableClassFromName(baseTableName);
+                    String newTitle = checkNotNull(tableClassObject.getString(LamiStrings.TITLE));
+
+                    tableClass = new LamiTableClass(baseTableClass, newTitle);
+                } else {
+                    /*
+                     * Dynamic column descriptions: we implement a new table
+                     * class entirely.
+                     */
+                    String title = checkNotNull(tableClassObject.getString(LamiStrings.TITLE));
+                    JSONArray columnDescriptions = checkNotNull(tableClassObject.getJSONArray(LamiStrings.COLUMN_DESCRIPTIONS));
+                    List<LamiTableEntryAspect> aspects = getAspectsFromColumnDescriptions(columnDescriptions);
+
+                    tableClass = new LamiTableClass(nullToEmptyString(Messages.LamiAnalysis_DefaultDynamicTableName), title, aspects, Collections.EMPTY_SET);
+                }
+
+                /* Parse the "data", which is the array of rows */
+                JSONArray data = result.getJSONArray(LamiStrings.DATA);
+                ImmutableList.Builder<LamiTableEntry> dataBuilder = new ImmutableList.Builder<>();
+
+                for (int j = 0; j < data.length(); j++) {
+                    /* A row is an array of cells */
+                    JSONArray row = data.getJSONArray(j);
+                    ImmutableList.Builder<LamiData> rowBuilder = ImmutableList.builder();
+
+                    for (int k = 0; k < row.length(); k++) {
+                        Object cellObject = checkNotNull(row.get(k));
+                        LamiData cellValue = LamiData.createFromObject(cellObject);
+                        rowBuilder.add(cellValue);
+
+                    }
+                    dataBuilder.add(new LamiTableEntry(rowBuilder.build()));
+                }
+                resultsBuilder.add(new LamiResultTable(tr, tableClass, dataBuilder.build()));
+            }
+
+        } catch (JSONException e) {
+            /* Error parsing the output */
+            Activator.instance().logError(nullToEmptyString(e.getMessage()));
+            return Collections.EMPTY_LIST;
+        }
+
+        return resultsBuilder.build();
+    }
+
+    private LamiTableClass getTableClassFromName(String tableClassName) throws JSONException {
+        Map<String, LamiTableClass> map = checkNotNull(fTableClasses);
+        LamiTableClass tableClass = map.get(tableClassName);
+        if (tableClass == null) {
+            throw new JSONException("Table class " + tableClassName + //$NON-NLS-1$
+                    " was not declared in the metadata"); //$NON-NLS-1$
+        }
+        return tableClass;
+    }
+
+    /**
+     * Get the output of an external command, used for getting the metadata.
+     * Cannot be cancelled, and will not report errors, simply returns null if
+     * the process ended abnormally.
+     */
+    private static @Nullable String getOutputFromCommand(List<String> command) {
+        try {
+            ProcessBuilder builder = new ProcessBuilder(command);
+            builder.redirectErrorStream(true);
+
+            Process p = builder.start();
+            try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));) {
+                int ret = p.waitFor();
+                String output = br.lines().collect(Collectors.joining());
+
+                return (ret == 0 ? output : null);
+            }
+        } catch (IOException | InterruptedException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Get the results of invoking the specified command.
+     *
+     * The result should start with '{"results":...', as specified by the
+     * LAMI JSON protocol. The JSON itself may be split over multiple lines.
+     *
+     * @param command
+     *            The command to run (program and its arguments)
+     * @return The analysis results
+     */
+    private static String getResultsFromCommand(List<String> command, IProgressMonitor monitor)
+            throws OnDemandAnalysisException {
+
+        final int scale = 1000;
+        double workedSoFar = 0.0;
+
+        ProcessCanceller cancellerRunnable = null;
+        Thread cancellerThread = null;
+
+        try {
+            monitor.beginTask(Messages.LamiAnalysis_MainTaskName, scale);
+
+            ProcessBuilder builder = new ProcessBuilder(command);
+            builder.redirectErrorStream(false);
+
+            Process p = checkNotNull(builder.start());
+
+            cancellerRunnable = new ProcessCanceller(p, monitor);
+            cancellerThread = new Thread(cancellerRunnable);
+            cancellerThread.start();
+
+            List<String> results = new ArrayList<>();
+
+            try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));) {
+                String line = in.readLine();
+                while (line != null && !line.matches("\\s*\\{.*")) { //$NON-NLS-1$
+                    /*
+                     * This is a line indicating progress, it has the form:
+                     *
+                     * 0.123 3000 of 5000 events processed
+                     *
+                     * The first part indicates the estimated fraction (out of
+                     * 1.0) of work done. The second part is status text.
+                     */
+
+                    // Trim the line first to make sure the first character is
+                    // significant
+                    line = line.trim();
+
+                    // Split at the first space
+                    String[] elems = line.split(" ", 2); //$NON-NLS-1$
+
+                    if (elems[0].matches("\\d.*")) { //$NON-NLS-1$
+                        // It looks like we have a progress indication
+                        try {
+                            // Try parsing the number
+                            double cumulativeWork = Double.parseDouble(elems[0]) * scale;
+                            double workedThisLoop = cumulativeWork - workedSoFar;
+
+                            // We're going backwards? Do not update the
+                            // monitor's value
+                            if (workedThisLoop > 0) {
+                                monitor.internalWorked(workedThisLoop);
+                                workedSoFar = cumulativeWork;
+                            }
+
+                            // There is a message: update the monitor's task name
+                            if (elems.length >= 2) {
+                                monitor.setTaskName(elems[1].trim());
+                            }
+                        } catch (NumberFormatException e) {
+                            // Continue reading progress lines anyway
+                        }
+                    }
+
+                    line = in.readLine();
+                }
+                while (line != null) {
+                    /*
+                     * We have seen the first line containing a '{', this is our
+                     * JSON output!
+                     */
+                    results.add(line);
+                    line = in.readLine();
+                }
+            }
+            int ret = p.waitFor();
+
+            if (monitor.isCanceled()) {
+                /* We were interrupted by the canceller thread. */
+                throw new OnDemandAnalysisException(null);
+            }
+
+            if (ret != 0) {
+                /*
+                 * Something went wrong running the external script. We will
+                 * gather the stderr and report it to the user.
+                 */
+                BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+                String stdErrOutput = br.lines().collect(Collectors.joining("\n")); //$NON-NLS-1$
+                throw new OnDemandAnalysisException(stdErrOutput);
+            }
+
+            /* External script ended successfully, all is fine! */
+            String resultsStr = results.stream().collect(Collectors.joining());
+            return checkNotNull(resultsStr);
+
+        } catch (IOException | InterruptedException e) {
+            throw new OnDemandAnalysisException(Messages.LamiAnalysis_ExecutionInterrupted);
+
+        } finally {
+            if (cancellerRunnable != null) {
+                cancellerRunnable.setFinished();
+            }
+            if (cancellerThread != null) {
+                try {
+                    cancellerThread.join();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            monitor.done();
+        }
+    }
+
+    private static class ProcessCanceller implements Runnable {
+
+        private final Process fProcess;
+        private final IProgressMonitor fMonitor;
+
+        private boolean fIsFinished = false;
+
+        public ProcessCanceller(Process process, IProgressMonitor monitor) {
+            fProcess = process;
+            fMonitor = monitor;
+        }
+
+        public void setFinished() {
+            fIsFinished = true;
+        }
+
+        @Override
+        public void run() {
+            try {
+                while (!fIsFinished) {
+                    Thread.sleep(500);
+                    if (fMonitor.isCanceled()) {
+                        fProcess.destroy();
+                        return;
+                    }
+                }
+            } catch (InterruptedException e) {
+            }
+        }
+
+    }
+
+    @Override
+    public @NonNull String getName() {
+        return fName;
+    }
+
+    @Override
+    public boolean isUserDefined() {
+        return fIsUserDefined;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysisReport.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiAnalysisReport.java
new file mode 100644 (file)
index 0000000..92ed096
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.util.List;
+
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysisReport;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Report generated by LAMI analyses.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiAnalysisReport implements IOnDemandAnalysisReport {
+
+    private final String fReportName;
+    private final List<LamiResultTable> fTables;
+
+    /**
+     * Constructor
+     *
+     * @param reportName
+     *            Name of the report (to be shown in the UI)
+     * @param tables
+     *            The result tables that are part of this report
+     */
+    public LamiAnalysisReport(String reportName, List<LamiResultTable> tables) {
+        fReportName = reportName;
+        fTables = ImmutableList.copyOf(tables);
+    }
+
+    @Override
+    public String getName() {
+        return fReportName;
+    }
+
+    /**
+     * Get the result tables of this report
+     *
+     * @return The result tables
+     */
+    public List<LamiResultTable> getTables() {
+        return fTables;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiChartModel.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiChartModel.java
new file mode 100644 (file)
index 0000000..c6cba40
--- /dev/null
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * UI Model of a LAMI chart. This object should contain all the information
+ * needed to create a chart in the GUI, independently of the actual chart
+ * implementation.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiChartModel {
+
+    /**
+     * Supported types of charts
+     */
+    public enum ChartType {
+        /** Bar chart */
+        BAR_CHART("Bar"), //$NON-NLS-1$
+
+        /** XY scatter chart */
+        XY_SCATTER("Scatter"), //$NON-NLS-1$
+
+        /**
+         * Pie chart
+         * FIXME NYI
+         */
+        PIE_CHART("Pie"); //$NON-NLS-1$
+
+        private final String fText;
+
+        private ChartType(final String text) {
+             fText = text;
+        }
+
+        @Override
+        public String toString() {
+            return fText;
+        }
+    }
+
+    private final ChartType fType;
+    private final String fName;
+    private final List<String> fXSeriesColumns;
+    private final List<String> fYSeriesColumns;
+    private final boolean fXAxisIsLog;
+    private final boolean fYAxisIsLog;
+
+
+    /**
+     * Constructor
+     *
+     * @param type
+     *            The type of chart
+     * @param name
+     *            The name of the chart
+     * @param xSeriesColumn
+     *            The title of column used for the X axis
+     * @param ySeriesColumns
+     *            The titles of the columns used for the series
+     * @param xAxisIsLog
+     *            If the X-axis is log scale or not
+     * @param yAxisIsLog
+     *            If the Y-axis is log scale or not
+     */
+    public LamiChartModel(ChartType type, String name, List<String> xSeriesColumn, List<String> ySeriesColumns,
+            boolean xAxisIsLog, boolean yAxisIsLog) {
+        fType = type;
+        fName = name;
+        fXSeriesColumns = ImmutableList.copyOf(xSeriesColumn);
+        fYSeriesColumns = ImmutableList.copyOf(ySeriesColumns);
+        fXAxisIsLog = xAxisIsLog;
+        fYAxisIsLog = yAxisIsLog;
+    }
+
+    /**
+     * Get the chart type.
+     *
+     * @return The chart type
+     */
+    public ChartType getChartType() {
+        return fType;
+    }
+
+    /**
+     * Get the chart's name.
+     *
+     * @return The chart name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Get the names of the columns used for the X part of a series.
+     *
+     * @return The columns used for the X-axis
+     */
+    public List<String> getXSeriesColumns() {
+        return fXSeriesColumns;
+    }
+
+    /**
+     * Get the names of the columns used for the Y part of a series.
+     *
+     * @return The columns used for the series
+     */
+    public List<String> getYSeriesColumns() {
+        return fYSeriesColumns;
+    }
+
+    /**
+     * Return if the X-axis should use a log scale.
+     *
+     * @return If the X-axis is log scale
+     */
+    public boolean xAxisIsLog() {
+        return fXAxisIsLog;
+    }
+
+    /**
+     * Return if the Y-axis should use a log scale.
+     *
+     * @return If Y-axis is log scale
+     */
+    public boolean yAxisIsLog() {
+        return fYAxisIsLog;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiLabelFormat.java
new file mode 100644 (file)
index 0000000..11d8be1
--- /dev/null
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.util.Map.Entry;
+
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.common.collect.BiMap;
+
+/**
+ * Format label based on a given Map<String, Integer>
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+public class LamiLabelFormat extends Format {
+
+    private static final long serialVersionUID = 4939553034329681316L;
+
+    private static final String SWTCHART_EMPTY_LABEL = " "; //$NON-NLS-1$
+    private static final String UNKNOWN_REPRESENTATION = "?"; //$NON-NLS-1$
+    private final BiMap<@Nullable String, Integer> fMap;
+
+    /**
+     * Constructor
+     *
+     * @param map
+     *            Map of indices to labels
+     */
+    public LamiLabelFormat(BiMap<@Nullable String, Integer> map) {
+        super();
+        fMap = map;
+    }
+
+    @Override
+    public @Nullable StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) {
+        if (obj == null || toAppendTo == null) {
+            return new StringBuffer(SWTCHART_EMPTY_LABEL);
+        }
+
+        Double doubleObj = (Double) obj;
+
+        /*
+         * Return a string buffer with a space in it since SWT does not like to
+         * draw empty strings.
+         */
+        if ((doubleObj % 1 != 0) || !fMap.containsValue((doubleObj.intValue()))) {
+            return new StringBuffer(SWTCHART_EMPTY_LABEL);
+        }
+
+        for (Entry<@Nullable String, Integer> entry : fMap.entrySet()) {
+            /*
+             * FIXME: Find if the elements are the same, based on their double
+             * value, because SWTChart uses double values so we do the same
+             * check. The loss of precision could lead to false positives.
+             */
+            if (Double.compare(entry.getValue().doubleValue(), doubleObj.doubleValue()) == 0) {
+                if (entry.getKey() == null) {
+                    return new StringBuffer(UNKNOWN_REPRESENTATION);
+                }
+                return toAppendTo.append(entry.getKey());
+            }
+        }
+        return new StringBuffer(SWTCHART_EMPTY_LABEL);
+    }
+
+    @Override
+    public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) {
+        return fMap.get(source);
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiResultTable.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiResultTable.java
new file mode 100644 (file)
index 0000000..73ee821
--- /dev/null
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.util.List;
+
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Class holding the results contained in one table outputted by a LAMI analysis.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiResultTable {
+
+    private final LamiTimeRange fTimeRange;
+    private final LamiTableClass fTableClass;
+    private final List<LamiTableEntry> fEntries;
+
+    /**
+     * Construct a new table from its components.
+     *
+     * @param timeRange
+     *            The time range represented by this table. Some analyses
+     *            separate tables by time range "buckets".
+     * @param tableClass
+     *            The class of this table
+     * @param entries
+     *            The list of entries, or rows, contained in this table
+     */
+    public LamiResultTable(LamiTimeRange timeRange, LamiTableClass tableClass,
+            Iterable<LamiTableEntry> entries) {
+        fTimeRange = timeRange;
+        fTableClass = tableClass;
+        fEntries = ImmutableList.copyOf(entries);
+    }
+
+    /**
+     * Get the time range of this table.
+     *
+     * @return The time range
+     */
+    public LamiTimeRange getTimeRange() {
+        return fTimeRange;
+    }
+
+    /**
+     * Get the class of this table.
+     *
+     * @return The table class
+     */
+    public LamiTableClass getTableClass() {
+        return fTableClass;
+    }
+
+    /**
+     * Get the list of entries contained in this table.
+     *
+     * @return The table entries
+     */
+    public List<LamiTableEntry> getEntries() {
+        return fEntries;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableClass.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableClass.java
new file mode 100644 (file)
index 0000000..030def8
--- /dev/null
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * The model of a table element in a LAMI analysis script output.
+ *
+ * Contains all the required information to build the actual UI table layout.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTableClass {
+
+    private final String fTableClassName;
+    private final String fTableTitle;
+    private final List<LamiTableEntryAspect> fAspects;
+    private final Collection<LamiChartModel> fPredefinedViews;
+
+    /**
+     * Standard constructor. Build a new table class by specifying all
+     * parameters.
+     *
+     * @param tableClassName
+     *            The name of the table's class
+     * @param tableTitle
+     *            The title of this table
+     * @param columnAspects
+     *            The list of aspects representing the columsn of this table
+     * @param predefinedViews
+     *            The pre-defined views of this analysis. Viewers will be
+     *            created for these views by default.
+     */
+    public LamiTableClass(String tableClassName, String tableTitle,
+            List<LamiTableEntryAspect> columnAspects, Collection<LamiChartModel> predefinedViews) {
+        fTableClassName = tableClassName;
+        fTableTitle = tableTitle;
+        fAspects = checkNotNull(ImmutableList.copyOf(columnAspects));
+        fPredefinedViews = ImmutableList.copyOf(predefinedViews);
+    }
+
+    /**
+     * "Extension" constructor. Use an existing table class but override the
+     * table name.
+     *
+     * @param baseClass
+     *            The base table class
+     * @param replacementTitle
+     *            The new title to use instead
+     */
+    public LamiTableClass(LamiTableClass baseClass, String replacementTitle) {
+        fTableClassName = Messages.LamiAnalysis_ExtendedTableNamePrefix + ' ' + baseClass.fTableClassName;
+        fTableTitle = replacementTitle;
+        fAspects = baseClass.fAspects; // We know it's an immutable list
+        fPredefinedViews = baseClass.fPredefinedViews; // idem
+    }
+
+    /**
+     * Get the name of the table's class.
+     *
+     * @return The table class name
+     */
+    public String getTableClassName() {
+        return fTableClassName;
+    }
+
+    /**
+     * Get the title of this table.
+     *
+     * @return The table title
+     */
+    public String getTableTitle() {
+        return fTableTitle;
+    }
+
+    /**
+     * Get the aspects of this table's columns.
+     *
+     * @return The table aspects
+     */
+    public List<LamiTableEntryAspect> getAspects() {
+        return fAspects;
+    }
+    /**
+     * Get the pre-defined views of this table.
+     *
+     * @return The predefined views
+     */
+    public Collection<LamiChartModel> getPredefinedViews() {
+        return fPredefinedViews;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableEntry.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTableEntry.java
new file mode 100644 (file)
index 0000000..f2b1dba
--- /dev/null
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimestamp;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Entry of a LAMI output. Usually corresponds to one row in a JSON LAMI table
+ * output.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTableEntry {
+
+    private final List<LamiData> fValues;
+
+    /**
+     * Constructor
+     *
+     * @param values Values contained in this row
+     */
+    public LamiTableEntry(List<LamiData> values) {
+        fValues = checkNotNull(ImmutableList.copyOf(values));
+    }
+
+    /**
+     * Get the value at a given index
+     *
+     * @param index
+     *            Index to look at
+     * @return The value at this index
+     */
+    public LamiData getValue(int index) {
+        return fValues.get(index);
+    }
+
+    /**
+     * Get the time range represented by this row.
+     *
+     * If more than one exists, one of them (usually the first) is returned.
+     *
+     * If there are no time ranges in this row, null is returned.
+     *
+     * @return The time range of this row
+     */
+    public @Nullable LamiTimeRange getCorrespondingTimeRange() {
+        /*
+         * If there is one or more time range(s) in the values, return the first
+         * one we find directly.
+         */
+        Optional<LamiTimeRange> oTimerange = fValues.stream()
+                .filter(data -> (data instanceof LamiTimeRange))
+                .<@NonNull LamiTimeRange> map(data -> (LamiTimeRange) data)
+                .findFirst();
+        if (oTimerange.isPresent()) {
+            return oTimerange.get();
+        }
+
+        /* Look for individual timestamps instead  */
+        List<LamiTimestamp> timestamps = fValues.stream()
+            .filter(data -> (data instanceof LamiTimestamp))
+            .<@NonNull LamiTimestamp> map(data -> (LamiTimestamp) data)
+            .collect(Collectors.toList());
+
+        if (timestamps.size() > 1) {
+            /* We can try using the first two timestamps to create a range (making sure it's valid) */
+            long first = timestamps.get(0).getValue();
+            long second = timestamps.get(1).getValue();
+            if (second >= first) {
+                return new LamiTimeRange(first, second);
+            }
+        }
+
+        if (!timestamps.isEmpty()) {
+            /* If there is only one timestamp, use it to create a punctual range */
+            long ts = timestamps.get(0).getValue();
+            return new LamiTimeRange(ts, ts);
+        }
+
+        /* Didn't find any timestamp we can't use */
+        return null;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiTimeStampFormat.java
new file mode 100644 (file)
index 0000000..49c13a8
--- /dev/null
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc. and others
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
+
+/**
+ * Formatter for time stamps
+ */
+public class LamiTimeStampFormat extends SimpleDateFormat {
+
+    private static final long serialVersionUID = 4285447886537779762L;
+
+    private final TmfTimestampFormat fFormat;
+
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * The default constructor (uses the default time format)
+     */
+    public LamiTimeStampFormat() {
+        fFormat = TmfTimestampFormat.getDefaulTimeFormat();
+    }
+
+    /**
+     * The normal constructor
+     *
+     * @param pattern the format pattern
+     */
+    public LamiTimeStampFormat(String pattern) {
+        fFormat = new TmfTimestampFormat(pattern);
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    @Override
+    public StringBuffer format(@Nullable Date date, @Nullable StringBuffer toAppendTo,
+            @Nullable FieldPosition fieldPosition) {
+        if (date != null && toAppendTo != null) {
+            long time = date.getTime();
+            toAppendTo.append(fFormat.format(time));
+            return toAppendTo;
+        }
+        return new StringBuffer();
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiXYSeriesDescription.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/LamiXYSeriesDescription.java
new file mode 100644 (file)
index 0000000..db6228f
--- /dev/null
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+
+/**
+ * Description of a series to use in LAMI charts.
+ *
+ * Contains the aspects to use on the X and Y axes (one per axis).
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+public class LamiXYSeriesDescription {
+
+    private final LamiTableEntryAspect fXAspect;
+    private final LamiTableEntryAspect fYAspect;
+
+    /**
+     * Constructor
+     *
+     * @param xAspect
+     *            The aspect to use on the X axis
+     * @param yAspect
+     *            The aspect to use on the Y axis
+     */
+    public LamiXYSeriesDescription(LamiTableEntryAspect xAspect, LamiTableEntryAspect yAspect) {
+        fXAspect = xAspect;
+        fYAspect = yAspect;
+    }
+
+    /**
+     * Get the aspect corresponding to the X axis.
+     *
+     * @return The X-axis aspect
+     */
+    public LamiTableEntryAspect getXAspect() {
+        return fXAspect;
+    }
+
+    /**
+     * Get the aspect corresponding to the Y axis.
+     *
+     * @return The Y-axis aspect
+     */
+    public LamiTableEntryAspect getYAspect() {
+        return fYAspect;
+    }
+
+
+    @Override
+    public String toString() {
+        return "x:" + fXAspect.getLabel() + " y:" + fYAspect.getLabel(); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fXAspect, fYAspect);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        LamiXYSeriesDescription other = (LamiXYSeriesDescription) obj;
+        if (!fXAspect.equals(other.fXAspect)) {
+            return false;
+        }
+        if (!fYAspect.equals(other.fYAspect)) {
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/Messages.java
new file mode 100644 (file)
index 0000000..1ca275a
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiAnalysis_DefaultDynamicTableName;
+
+    public static String LamiAnalysis_MainTaskName;
+    public static String LamiAnalysis_NoResults;
+    public static String LamiAnalysis_ExecutionInterrupted;
+
+    public static String LamiAnalysis_ExtendedTableNamePrefix;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/messages.properties
new file mode 100644 (file)
index 0000000..c91b0a7
--- /dev/null
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiAnalysis_DefaultDynamicTableName = Dynamic Table
+
+LamiAnalysis_MainTaskName = Invoking external analysis script
+LamiAnalysis_NoResults = No results were returned.
+LamiAnalysis_ExecutionInterrupted = Execution was interrupted.
+
+LamiAnalysis_ExtendedTableNamePrefix = Extended
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/module/package-info.java
new file mode 100644 (file)
index 0000000..c1f056e
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/package-info.java
new file mode 100644 (file)
index 0000000..7f5ed8e
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBitrate.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBitrate.java
new file mode 100644 (file)
index 0000000..6f49059
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiBitrate extends LamiInteger {
+    public LamiBitrate(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBoolean.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiBoolean.java
new file mode 100644 (file)
index 0000000..a23b8ba
--- /dev/null
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+class LamiBoolean extends LamiData {
+
+    private static final LamiBoolean TRUE = new LamiBoolean(true);
+    private static final LamiBoolean FALSE = new LamiBoolean(false);
+
+    public static LamiBoolean instance(boolean value) {
+        return (value ? TRUE : FALSE);
+    }
+
+    private final boolean fValue;
+
+    private LamiBoolean(boolean value) {
+        fValue = value;
+    }
+
+    public boolean getValue() {
+        return fValue;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return (fValue ?
+                nullToEmptyString(Messages.LamiBoolean_Yes) :
+                nullToEmptyString(Messages.LamiBoolean_No));
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiCPU.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiCPU.java
new file mode 100644 (file)
index 0000000..c66b3ff
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiCPU extends LamiInteger {
+    public LamiCPU(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiData.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiData.java
new file mode 100644 (file)
index 0000000..2442d10
--- /dev/null
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc. and others
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.Map;
+import java.util.function.Function;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.LamiStrings;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Base class for data types allowed in LAMI analysis scripts JSON output.
+ *
+ * @author Alexandre Montplaisir
+ * @author Philippe Proulx
+ */
+public abstract class LamiData {
+
+    /**
+     * Enum of all the valid data types
+     */
+    @SuppressWarnings("javadoc")
+    public enum DataType {
+
+        /* Generic JSON types */
+        STRING("string", "Value", false, null, LamiString.class), //$NON-NLS-1$ //$NON-NLS-2$
+        INT("int", "Value", true, null, LamiInteger.class), //$NON-NLS-1$ //$NON-NLS-2$
+        FLOAT("float", "Value", true, null, LamiNumber.class), //$NON-NLS-1$ //$NON-NLS-2$
+        NUMBER("number", "Value", true, null, LamiNumber.class), //$NON-NLS-1$ //$NON-NLS-2$
+        BOOL("bool", "Value", false, null, LamiBoolean.class), //$NON-NLS-1$ //$NON-NLS-2$
+
+        /* Lami-specific data types */
+        RATIO("ratio", "Ratio", true, "%", LamiRatio.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        TIMESTAMP("timestamp", "Timestamp", true, "ns", LamiTimestamp.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        TIME_RANGE("time-range", "Time range", true, null, LamiTimeRange.class), //$NON-NLS-1$ //$NON-NLS-2$
+        DURATION("duration", "Duration", true, "ns", LamiDuration.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        SIZE("size", "Size", true, Messages.LamiData_UnitBytes, LamiSize.class), //$NON-NLS-1$ //$NON-NLS-2$
+        BITRATE("bitrate", "Bitrate", true, Messages.LamiData_UnitBitsPerSecond, LamiBitrate.class), //$NON-NLS-1$ //$NON-NLS-2$
+        SYSCALL("syscall", "System call", false, null, LamiSystemCall.class), //$NON-NLS-1$ //$NON-NLS-2$
+        PROCESS("process", "Process", false, null, LamiProcess.class), //$NON-NLS-1$ //$NON-NLS-2$
+        PATH("path", "Path", false, null, LamiPath.class), //$NON-NLS-1$ //$NON-NLS-2$
+        FD("fd", "File descriptor", false, null, LamiFileDescriptor.class), //$NON-NLS-1$ //$NON-NLS-2$
+        IRQ("irq", "IRQ", false, null, LamiIRQ.class), //$NON-NLS-1$ //$NON-NLS-2$
+        CPU("cpu", "CPU", false, null, LamiCPU.class), //$NON-NLS-1$ //$NON-NLS-2$
+        DISK("disk", "Disk", false, null, LamiDisk.class), //$NON-NLS-1$ //$NON-NLS-2$
+        PART("part", "Disk partition", false, null, LamiDiskPartition.class), //$NON-NLS-1$ //$NON-NLS-2$
+        NETIF("netif", "Network interface", false, null, LamiNetworkInterface.class), //$NON-NLS-1$ //$NON-NLS-2$
+        UNKNOWN("unknown", "Value", false, null, LamiUnknown.class), //$NON-NLS-1$ //$NON-NLS-2$
+        MIXED("mixed", "Value", false, null, null); //$NON-NLS-1$ //$NON-NLS-2$
+
+        private final String fName;
+        private final String fTitle;
+        private final boolean fIsContinuous;
+        private final @Nullable String fUnits;
+        private final @Nullable Class<?> fClass;
+
+        private DataType(String name, String title, boolean isContinous, @Nullable String units, @Nullable Class<?> cls) {
+            fName = name;
+            fTitle = title;
+            fIsContinuous = isContinous;
+            fUnits = units;
+            fClass = cls;
+        }
+
+        /**
+         * Indicates if this data type represents a continuous numerical value.
+         *
+         * For example, time or bitrates are continuous values, but CPU or IRQ
+         * numbers are not (you can't have CPU 1.5!)
+         *
+         * @return If this aspect is continuous
+         */
+        public boolean isContinuous() {
+            return fIsContinuous;
+        }
+
+        /**
+         * Get the units of this data type, if any.
+         *
+         * @return The units, or <code>null</code> if there are no units
+         */
+        public @Nullable String getUnits() {
+            return fUnits;
+        }
+
+        /**
+         * The default title for columns containing these units.
+         *
+         * @return The data type's column title
+         */
+        public String getTitle() {
+            return fTitle;
+        }
+
+        /**
+         * Get the data type from its JSON string representation.
+         *
+         * @param value
+         *            The string
+         * @return The corresponding data type
+         */
+        public static DataType fromString(String value) {
+            for (DataType type : DataType.values()) {
+                if (type.fName.equals(value)) {
+                    return type;
+                }
+            }
+            throw new IllegalArgumentException("Unrecognized type: " + value);
+        }
+
+        /**
+         * Get the date type enum element from its implementation Class.
+         *
+         * @param cls
+         *            The data type class
+         * @return The data type
+         */
+        public static @Nullable DataType fromClass(Class<? extends LamiData> cls) {
+            for (DataType type : DataType.values()) {
+                if (cls.equals(type.fClass)) {
+                    return type;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    @Override
+    public abstract @Nullable String toString();
+
+    // ------------------------------------------------------------------------
+    // Convenience methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Convenience method to get the "value" field from a JSON object. Many LAMI
+     * types have a "value" field.
+     *
+     * @param obj
+     *            The JSON object
+     * @return The read value
+     * @throws JSONException
+     *             If the object does not actually have a "value" field.
+     */
+    private static final long getJSONObjectLongValue(JSONObject obj) throws JSONException {
+        return obj.getLong(LamiStrings.VALUE);
+    }
+
+    /**
+     * Convenience method to get the "name" field from a JSON object. Many LAMI
+     * types have a "nam" field.
+     *
+     * @param obj
+     *            The JSON object
+     * @return The read name
+     * @throws JSONException
+     *             If the object does not actually have a "name" field.
+     */
+    private static final String getJSONObjectStringName(JSONObject obj) throws JSONException {
+        return checkNotNull(obj.getString(LamiStrings.NAME));
+    }
+
+    // ------------------------------------------------------------------------
+    // "Factory" methods and helpers
+    // ------------------------------------------------------------------------
+
+    /**
+     * Factory method to build a new LamiData object from either a
+     * {@link JSONObject} or a standard Java {@link Object} representing a
+     * primitive type.
+     *
+     * @param obj
+     *            The source object
+     * @return The corresponding LamiData object
+     * @throws JSONException
+     *             If the object type is not supported
+     */
+    public static LamiData createFromObject(Object obj) throws JSONException {
+        if (obj instanceof JSONObject) {
+            return createFromJsonObject((JSONObject) obj);
+        } else if (obj.equals(JSONObject.NULL)) {
+            return LamiEmpty.INSTANCE;
+        } else {
+            return createFromPrimitiveObject(obj);
+        }
+    }
+
+    @FunctionalInterface
+    private static interface CheckedJSONExceptionFunction<T, R> {
+       R apply(T t) throws JSONException;
+    }
+
+    /**
+     * Map returning the Functions to build new LAMI objects for JSON primitive
+     * types
+     */
+    private static final Map<Class<?>, Function<Object, LamiData>> PRIMITIVE_TYPE_GENERATOR;
+    static {
+        ImmutableMap.Builder<Class<?>, Function<Object, LamiData>> primitiveTypeGenBuilder = ImmutableMap.builder();
+        primitiveTypeGenBuilder.put(Boolean.class, (o) -> LamiBoolean.instance((Boolean) o));
+        primitiveTypeGenBuilder.put(Integer.class, (o) -> new LamiInteger(((Integer) o).longValue()));
+        primitiveTypeGenBuilder.put(Long.class, (o) -> new LamiInteger((Long) o));
+        primitiveTypeGenBuilder.put(Double.class, (o) -> new LamiNumber((Double) o));
+        primitiveTypeGenBuilder.put(String.class, (o) -> new LamiString((String) o));
+        PRIMITIVE_TYPE_GENERATOR = primitiveTypeGenBuilder.build();
+    }
+
+    /**
+     * Map returning the Functions to build new LAMI objects for LAMI-specific
+     * types
+     */
+    private static final Map<String, CheckedJSONExceptionFunction<JSONObject, LamiData>> COMPLEX_TYPE_GENERATOR;
+    static {
+        ImmutableMap.Builder<String, CheckedJSONExceptionFunction<JSONObject, LamiData>> complexTypeGenBuilder = ImmutableMap.builder();
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_BITRATE, (obj) -> new LamiBitrate(getJSONObjectLongValue(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_CPU, (obj) -> new LamiCPU(obj.getLong(LamiStrings.ID)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_DISK, (obj) -> new LamiDisk(getJSONObjectStringName(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_DURATION, (obj) -> new LamiDuration(getJSONObjectLongValue(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_PART, (obj) -> new LamiDiskPartition(getJSONObjectStringName(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_FD, (obj) -> new LamiFileDescriptor(obj.getLong(LamiStrings.FD)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_NETIF, (obj) -> new LamiNetworkInterface(getJSONObjectStringName(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_PATH, (obj) -> new LamiPath(checkNotNull(obj.getString(LamiStrings.PATH))));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_PROCESS, (obj) -> {
+            String name = obj.optString(LamiStrings.NAME);
+            Long pid = (obj.has(LamiStrings.PID) ? obj.getLong(LamiStrings.PID) : null);
+            Long tid = (obj.has(LamiStrings.TID) ? obj.getLong(LamiStrings.TID) : null);
+
+            return new LamiProcess(name, pid, tid);
+        });
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_RATIO, (obj) -> new LamiRatio(obj.getDouble(LamiStrings.VALUE)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_IRQ, (obj) -> {
+            LamiIRQ.Type irqType = LamiIRQ.Type.HARD;
+
+            if (obj.has(LamiStrings.HARD)) {
+                boolean isHardIrq = obj.getBoolean(LamiStrings.HARD);
+                irqType = (isHardIrq ? LamiIRQ.Type.HARD : LamiIRQ.Type.SOFT);
+            }
+
+            int nr = obj.getInt(LamiStrings.NR);
+            String name = obj.optString(LamiStrings.NAME);
+
+            return new LamiIRQ(irqType, nr, name);
+        });
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_SIZE, (obj) -> new LamiSize(getJSONObjectLongValue(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_SYSCALL, (obj) -> new LamiSystemCall(getJSONObjectStringName(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_TIME_RANGE, (obj) -> {
+            long begin = obj.getLong(LamiStrings.BEGIN);
+            long end = obj.getLong(LamiStrings.END);
+
+            return new LamiTimeRange(begin, end);
+        });
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_TIMESTAMP, (obj) -> new LamiTimestamp(getJSONObjectLongValue(obj)));
+        complexTypeGenBuilder.put(LamiStrings.DATA_CLASS_UNKNOWN, (obj) -> LamiUnknown.INSTANCE);
+        COMPLEX_TYPE_GENERATOR = complexTypeGenBuilder.build();
+    }
+
+    /**
+     * Create a new LamiData for a primitive type (Integer, String, etc.)
+     *
+     * @param obj
+     *            The source object
+     * @return A new corresponding LamiData object
+     * @throws JSONException
+     *             If the object type is not supported
+     */
+    private static LamiData createFromPrimitiveObject(Object obj) throws JSONException {
+        Function<Object, LamiData> func = PRIMITIVE_TYPE_GENERATOR.get(obj.getClass());
+        if (func == null) {
+            throw new JSONException("Unhandled type: " + obj.toString() + " of type " + obj.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        /* We never return null in the implementations */
+        return checkNotNull(func.apply(obj));
+    }
+
+    /**
+     * Create a new LamiData for a LAMI-specific type from a {@link JSONObject}.
+     *
+     * @param obj
+     *            The source object
+     * @return A new corresponding LamiData object
+     * @throws JSONException
+     *             If the object type is not supported
+     */
+    private static LamiData createFromJsonObject(JSONObject obj) throws JSONException {
+        String dataClass = obj.optString(LamiStrings.CLASS);
+
+        if (dataClass == null) {
+            throw new JSONException("Cannot find data class"); //$NON-NLS-1$
+        }
+
+        CheckedJSONExceptionFunction<JSONObject, LamiData> func = COMPLEX_TYPE_GENERATOR.get(dataClass);
+
+        if (func == null) {
+            throw new JSONException(String.format("Unsupported data class \"%s\"", dataClass)); //$NON-NLS-1$
+        }
+
+        return func.apply(obj);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDisk.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDisk.java
new file mode 100644 (file)
index 0000000..85152dd
--- /dev/null
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+/**
+ * Lami 'disk' value.
+ *
+ * A disk can be "sda". It may contain partitions.
+ *
+ * @author Philippe Proulx
+ */
+class LamiDisk extends LamiString {
+
+    public LamiDisk(String value) {
+        super(value);
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDiskPartition.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDiskPartition.java
new file mode 100644 (file)
index 0000000..72f0d17
--- /dev/null
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+/**
+ * Lami 'disk partition' value.
+ *
+ * A disk partition is something like "sda2".
+ *
+ * @author Philippe Proulx
+ */
+class LamiDiskPartition extends LamiString {
+
+    public LamiDiskPartition(String value) {
+        super(value);
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDuration.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiDuration.java
new file mode 100644 (file)
index 0000000..73f2ac2
--- /dev/null
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+/**
+ * Duration data element
+ *
+ * @author Philippe Proulx
+ */
+public class LamiDuration extends LamiInteger {
+
+    /**
+     * Constructor
+     *
+     * @param value
+     *            The duration value (as a long)
+     */
+    public LamiDuration(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiEmpty.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiEmpty.java
new file mode 100644 (file)
index 0000000..07d03ad
--- /dev/null
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+class LamiEmpty extends LamiData {
+
+    public static final LamiEmpty INSTANCE = new LamiEmpty();
+
+    private LamiEmpty() {}
+
+    @Override
+    public @Nullable String toString() {
+        return null;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiFileDescriptor.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiFileDescriptor.java
new file mode 100644 (file)
index 0000000..6915dd2
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiFileDescriptor extends LamiInteger {
+    public LamiFileDescriptor(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiIRQ.java
new file mode 100644 (file)
index 0000000..ce71dc0
--- /dev/null
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Lami IRQ data type.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiIRQ extends LamiData {
+
+    /**
+     * IRQ type
+     */
+    public enum Type {
+        /** Hardware IRQ */
+        HARD,
+        /** Software IRQ */
+        SOFT
+    }
+
+    private final Type fType;
+    private final int fNumber;
+    private final @Nullable String fName;
+
+    /**
+     * Constructor
+     *
+     * @param irqType
+     *            IRQ type
+     * @param nb
+     *            IRQ number
+     * @param name
+     *            IRQ name, null if not available
+     */
+    public LamiIRQ(Type irqType, int nb, @Nullable String name) {
+        fType = irqType;
+        fNumber = nb;
+        fName = name;
+    }
+
+    /**
+     * Get this IRQ's name. May be null if unavailable.
+     *
+     * @return The IRQ name
+     */
+    public @Nullable String getName() {
+        return fName;
+    }
+
+    /**
+     * Get this IRQ's type
+     *
+     * @return The IRQ type
+     */
+    public Type getType() {
+        return fType;
+    }
+
+    /**
+     * Get this IRQ's number.
+     *
+     * @return The IRQ number
+     */
+    public int getNumber() {
+        return fNumber;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        StringBuilder sb = new StringBuilder();
+        switch (fType) {
+        case SOFT:
+            sb.append(Messages.LamiIRQ_SoftIRQ).append(' ');
+            break;
+        case HARD:
+        default:
+            sb.append(Messages.LamiIRQ_HardwareIRQ).append(' ');
+            break;
+        }
+
+        sb.append(String.valueOf(fNumber));
+
+        if (fName != null) {
+            sb.append(" (" + fName + ')'); //$NON-NLS-1$
+        }
+        return sb.toString();
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiInteger.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiInteger.java
new file mode 100644 (file)
index 0000000..648b217
--- /dev/null
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Integer data element
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiInteger extends LamiData {
+
+    private final long fValue;
+
+    /**
+     * Constructor
+     *
+     * @param value The integer value (as a long)
+     */
+    public LamiInteger(long value) {
+        fValue = value;
+    }
+
+    /**
+     * Return the value
+     *
+     * @return The value
+     */
+    public long getValue() {
+        return fValue;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return String.valueOf(fValue);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNetworkInterface.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNetworkInterface.java
new file mode 100644 (file)
index 0000000..62661b6
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiNetworkInterface extends LamiString {
+    public LamiNetworkInterface(String value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNumber.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiNumber.java
new file mode 100644 (file)
index 0000000..b0ca2e9
--- /dev/null
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+class LamiNumber extends LamiData {
+
+    private final double fValue;
+
+    public LamiNumber(double value) {
+        fValue = value;
+    }
+
+    public double getValue() {
+        return fValue;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return Double.toString(fValue);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiPath.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiPath.java
new file mode 100644 (file)
index 0000000..32dc542
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiPath extends LamiString {
+    public LamiPath(String value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiProcess.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiProcess.java
new file mode 100644 (file)
index 0000000..705ec2c
--- /dev/null
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import java.util.StringJoiner;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Class defining a LAMI 'process' type.
+ *
+ * This is the representation of an operating system process.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiProcess extends LamiData {
+
+    private final @Nullable String fName;
+    private final @Nullable Long fPid;
+    private final @Nullable Long fTid;
+
+    private final String fString;
+
+    /**
+     * Constructor
+     *
+     * All parameters are optional, but realistically at least one should be
+     * provided!
+     *
+     * @param name
+     *            The process name
+     * @param pid
+     *            The process's PID
+     * @param tid
+     *            The process's TID
+     */
+    public LamiProcess(@Nullable String name, @Nullable Long pid, @Nullable Long tid) {
+        fName = name;
+        fPid = pid;
+        fTid = tid;
+
+        fString = generateString();
+    }
+
+    /**
+     * Get this process's name, null if unavailable.
+     *
+     * @return The process name
+     */
+    public @Nullable String getName() {
+        return fName;
+    }
+
+    /**
+     * Get this process's PID, null if unavailable.
+     *
+     * @return The process PID
+     */
+    public @Nullable Long getPID() {
+        return fPid;
+    }
+
+    /**
+     * Get this process's TID, null if unavailable.
+     *
+     * @return The process TID
+     */
+    public @Nullable Long getTID() {
+        return fTid;
+    }
+
+    private String generateString() {
+        Long pid = fPid;
+        Long tid = fTid;
+
+        StringBuilder sb = new StringBuilder();
+        if (fName != null) {
+            sb.append(fName);
+        }
+
+        if (pid != null || tid != null) {
+            sb.append(' ');
+            StringJoiner sj = new StringJoiner(", ", "(", ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+            if (pid != null) {
+                sj.add("pid=" + pid.toString()); //$NON-NLS-1$
+            }
+            if (tid != null) {
+                sj.add("tid=" + tid.toString()); //$NON-NLS-1$
+            }
+            sb.append(sj.toString());
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return fString;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiRatio.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiRatio.java
new file mode 100644 (file)
index 0000000..39d8df5
--- /dev/null
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+class LamiRatio extends LamiNumber {
+    public LamiRatio(double value) {
+        super(value);
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return String.format("%.2f", getValue() * 100); //$NON-NLS-1$
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSize.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSize.java
new file mode 100644 (file)
index 0000000..0e4d956
--- /dev/null
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+class LamiSize extends LamiInteger {
+    public LamiSize(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiString.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiString.java
new file mode 100644 (file)
index 0000000..ed6defa
--- /dev/null
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+class LamiString extends LamiData {
+
+    private final String fValue;
+
+    public LamiString(String value) {
+        fValue = value;
+    }
+
+    public String getValue() {
+        return fValue;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return fValue;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSystemCall.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiSystemCall.java
new file mode 100644 (file)
index 0000000..b20111a
--- /dev/null
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Philippe Proulx
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Data element for LAMI system call objects.
+ *
+ * @author Philippe Proulx
+ */
+public class LamiSystemCall extends LamiString {
+
+    private final String fString;
+
+    /**
+     * Constructor
+     *
+     * @param value
+     *            The 'value' field of the system call, typically its name
+     */
+    public LamiSystemCall(String value) {
+        super(value);
+
+        if (value.endsWith("()")) { //$NON-NLS-1$
+            fString = value;
+        } else {
+            fString = value + "()"; //$NON-NLS-1$
+        }
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return fString;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimeRange.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimeRange.java
new file mode 100644 (file)
index 0000000..8dfc536
--- /dev/null
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Lami time range data type
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimeRange extends LamiData {
+
+    private final long fStart;
+    private final long fEnd;
+    private final long fDuration;
+
+    /**
+     * Construct a new time range
+     *
+     * @param start
+     *            Start time
+     * @param end
+     *            End time
+     */
+    public LamiTimeRange(long start, long end) {
+        fStart = start;
+        fEnd = end;
+        fDuration = fEnd - fStart;
+    }
+
+    /**
+     * Get the start time of this time range.
+     *
+     * @return The start time
+     */
+    public long getStart() {
+        return fStart;
+    }
+
+    /**
+     * Get the end time of this time range.
+     *
+     * @return The end time
+     */
+    public long getEnd() {
+        return fEnd;
+    }
+    /**
+     * Get the duration of this time range.
+     *
+     * @return The duration
+     */
+    public long getDuration() {
+        return fDuration;
+    }
+
+    @Override
+    public @Nullable String toString() {
+        return "[" + String.valueOf(fStart) + " - " + String.valueOf(fEnd) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimestamp.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiTimestamp.java
new file mode 100644 (file)
index 0000000..a9d041f
--- /dev/null
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+/**
+ * Lami timestamp data type
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiTimestamp extends LamiInteger {
+
+    /**
+     * Construct a time stamp from a value in ns.
+     *
+     * @param value
+     *            The value
+     */
+    public LamiTimestamp(long value) {
+        super(value);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiUnknown.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiUnknown.java
new file mode 100644 (file)
index 0000000..32a016f
--- /dev/null
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Lami 'unknown' value.
+ *
+ * The special unknown object represents an unknown value. It is typically used
+ * in result table cells where a given computation cannot produce a result for
+ * some reason.
+ *
+ * @author Alexandre Montplaisir
+ */
+class LamiUnknown extends LamiData {
+
+    public static final LamiUnknown INSTANCE = new LamiUnknown();
+
+    private LamiUnknown() {}
+
+    @Override
+    public @Nullable String toString() {
+        return null;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiVersion.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/LamiVersion.java
new file mode 100644 (file)
index 0000000..9f0135f
--- /dev/null
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Lami 'version' data type
+ *
+ * @author Alexandre Montplaisir
+ */
+public class LamiVersion {
+
+    private final int fMajor;
+    private final int fMinor;
+    private final int fPatchLevel;
+    private final @Nullable String fExtra;
+
+    /**
+     * Construct a new version number. Normally to be show as:
+     *
+     * major.minor.patchlevel.extra
+     *
+     * @param major
+     *            Major version number
+     * @param minor
+     *            Minor version number
+     * @param patchLevel
+     *            Patch version number
+     * @param extra
+     *            Extra version number
+     */
+    public LamiVersion(int major, int minor, int patchLevel, @Nullable String extra) {
+        fMajor = major;
+        fMinor = minor;
+        fPatchLevel = patchLevel;
+        fExtra = extra;
+    }
+
+    /**
+     * @return The major version number
+     */
+    public int getMajor() {
+        return fMajor;
+    }
+
+    /**
+     * @return The minor version number
+     */
+    public int getMinor() {
+        return fMinor;
+    }
+
+    /**
+     * @return The patchlevel version number
+     */
+    public int getPatchLevel() {
+        return fPatchLevel;
+    }
+
+    /**
+     * @return The extra version number. May be a string, and may be null.
+     */
+    public @Nullable String getExtra() {
+        return fExtra;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/Messages.java
new file mode 100644 (file)
index 0000000..107dd25
--- /dev/null
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiBoolean_Yes;
+    public static String LamiBoolean_No;
+
+    public static String LamiData_Value;
+    public static String LamiData_UnitBytes;
+    public static String LamiData_UnitBitsPerSecond;
+
+    public static String LamiIRQ_SoftIRQ;
+    public static String LamiIRQ_HardwareIRQ;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/messages.properties
new file mode 100644 (file)
index 0000000..d327c49
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiBoolean_Yes = Yes
+LamiBoolean_No = No
+
+LamiData_Value = Value
+LamiData_UnitBytes = bytes
+LamiData_UnitBitsPerSecond = bps
+
+LamiIRQ_SoftIRQ = SoftIRQ
+LamiIRQ_HardwareIRQ = IRQ
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.core/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/core/types/package-info.java
new file mode 100644 (file)
index 0000000..5d2d55e
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types;
index 54de195ef996e77ecda5b59f991b9847976a6d12..5f75cd0b6b09aaf9d73a0a01a0828a86b6d58df4 100644 (file)
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2015 EfficiOS Inc. and others
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
 #
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
@@ -12,7 +12,8 @@ output.. = bin/
 bin.includes = META-INF/,\
                .,\
                about.html,\
-               plugin.properties
+               plugin.properties,\
+               plugin.xml
 src.includes = about.html
 additional.bundles = org.eclipse.jdt.annotation
 jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/histogram.gif b/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/histogram.gif
new file mode 100644 (file)
index 0000000..dd2dda5
Binary files /dev/null and b/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/histogram.gif differ
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/table.gif b/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/table.gif
new file mode 100755 (executable)
index 0000000..d11c996
Binary files /dev/null and b/analysis/org.eclipse.tracecompass.analysis.lami.ui/icons/table.gif differ
index 1a2be40f9c0655ab83f6999e5abf0af147ebf1b7..31bd0be0719ca49cb274cf6d7649e262f4e8c346 100644 (file)
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2015 EfficiOS Inc. and others
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
 #
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/plugin.xml b/analysis/org.eclipse.tracecompass.analysis.lami.ui/plugin.xml
new file mode 100644 (file)
index 0000000..a8a026f
--- /dev/null
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+    Copyright (c) 2016 EfficiOS Inc. and others
+
+    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
+ -->
+
+<plugin>
+   <extension
+         point="org.eclipse.ui.handlers">
+      <handler
+            class="org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler.RunAnalysisHandler"
+            commandId="org.eclipse.tracecompass.tmf.ui.command.analysis_run">
+         <activeWhen>
+            <and>
+               <count
+                     value="1">
+               </count>
+               <iterate
+                     operator="and">
+                  <instanceof
+                        value="org.eclipse.tracecompass.tmf.ui.project.model.TmfOnDemandAnalysisElement">
+                  </instanceof>
+               </iterate>
+            </and>
+         </activeWhen>
+      </handler>
+      <handler
+            class="org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler.OpenReportHandler"
+            commandId="org.eclipse.tracecompass.tmf.ui.command.report_open">
+         <activeWhen>
+            <and>
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+                  <instanceof
+                        value="org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement">
+                  </instanceof>
+               </iterate>
+            </and>
+         </activeWhen>
+      </handler>
+      <handler
+            class="org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler.DeleteReportHandler"
+            commandId="org.eclipse.tracecompass.tmf.ui.command.report_delete">
+         <activeWhen>
+            <and>
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+                  <instanceof
+                        value="org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement">
+                  </instanceof>
+               </iterate>
+            </and>
+         </activeWhen>
+      </handler>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commands">
+      <command
+            categoryId="org.eclipse.linuxtools.tmf.ui.commands.category"
+            description="%command.analysis_run.description"
+            id="org.eclipse.tracecompass.tmf.ui.command.analysis_run"
+            name="%command.analysis_run">
+      </command>
+      <command
+            categoryId="org.eclipse.linuxtools.tmf.ui.commands.category"
+            description="%command.report_open.description"
+            id="org.eclipse.tracecompass.tmf.ui.command.report_open"
+            name="%command.report_open">
+      </command>
+      <command
+            categoryId="org.eclipse.linuxtools.tmf.ui.commands.category"
+            description="%command.report_delete.description"
+            id="org.eclipse.tracecompass.tmf.ui.command.report_delete"
+            name="%command.report_delete">
+      </command>
+   </extension>
+   <extension
+         point="org.eclipse.ui.menus">
+      <menuContribution
+            locationURI="popup:org.eclipse.ui.popup.any?after=additions">
+         <command
+               commandId="org.eclipse.tracecompass.tmf.ui.command.analysis_run"
+               label="%command.analysis_run"
+               mnemonic="%command.analysis_run.mnemonic"
+               style="push"
+               tooltip="%command.analysis_run.description">
+            <visibleWhen
+                  checkEnabled="false">
+               <with
+                     variable="selection">
+                  <count
+                        value="1">
+                  </count>
+                  <iterate>
+                     <instanceof
+                           value="org.eclipse.tracecompass.tmf.ui.project.model.TmfOnDemandAnalysisElement">
+                     </instanceof>
+                  </iterate>
+               </with>
+            </visibleWhen>
+         </command>
+      </menuContribution>
+      <menuContribution
+            allPopups="false"
+            locationURI="popup:org.eclipse.ui.popup.any?after=additions">
+         <command
+               commandId="org.eclipse.tracecompass.tmf.ui.command.report_open"
+               label="%command.report_open"
+               mnemonic="%command.report_open.mnemonic"
+               style="push"
+               tooltip="%command.report_open.description">
+            <visibleWhen
+                  checkEnabled="false">
+               <with
+                     variable="selection">
+                  <iterate
+                        ifEmpty="false">
+                     <instanceof
+                           value="org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement">
+                     </instanceof>
+                  </iterate>
+               </with>
+            </visibleWhen>
+         </command>
+         <command
+               commandId="org.eclipse.tracecompass.tmf.ui.command.report_delete"
+               label="%command.report_delete"
+               mnemonic="%command.report_delete.mnemonic"
+               style="push"
+               tooltip="%command.report_delete.description">
+            <visibleWhen
+                  checkEnabled="false">
+               <with
+                     variable="selection">
+                  <iterate
+                        ifEmpty="false">
+                     <instanceof
+                           value="org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement">
+                     </instanceof>
+                  </iterate>
+               </with>
+            </visibleWhen>
+         </command>
+      </menuContribution>
+   </extension>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            allowMultiple="true"
+            category="org.eclipse.linuxtools.tmf.ui.views.category"
+            class="org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportView"
+            id="org.eclipse.tracecompass.analysis.lami.views.reportview"
+            name="%analysis.report.view"
+            restorable="false">
+      </view>
+   </extension>
+
+</plugin>
index f3f2b1a6a67cfa229e32de6ec0f338b5c7d78c5d..2e5303976c8a2ce986e3a30b3a8e14ffec846da4 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/DeleteReportHandler.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/DeleteReportHandler.java
new file mode 100644 (file)
index 0000000..dc61925
--- /dev/null
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
+
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysisReport;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The command handler for the "Delete Report" menu option for Report project
+ * model elements.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class DeleteReportHandler extends AbstractHandler {
+
+    @Override
+    public @Nullable Object execute(@Nullable ExecutionEvent event) throws ExecutionException {
+        /* Types should have been checked by the plugin.xml already */
+        ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
+        List<?> elements = ((IStructuredSelection) selection).toList();
+
+        /* Ask the parent element to remove each corresponding report. */
+        elements.stream()
+            .filter(elem -> elem instanceof TmfReportElement)
+            .map(elem -> (TmfReportElement) elem)
+            .forEach(reportElem -> {
+                IOnDemandAnalysisReport report = reportElem.getReport();
+                reportElem.getParent().removeReport(report);
+            });
+
+        return null;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/Messages.java
new file mode 100644 (file)
index 0000000..31d460b
--- /dev/null
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiAnalysis_MainTaskName;
+
+    public static String ParameterDialog_BaseCommand;
+    public static String ParameterDialog_ExternalParameters;
+    public static String ParameterDialog_ExternalParametersDescription;
+    public static String ParameterDialog_StringValidatorMessage;
+    public static String ParameterDialog_ReportNameSuffix;
+    public static String ParameterDialog_Error;
+    public static String ParameterDialog_ErrorMessage;
+
+    static {
+        // initialize resource bundle
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/OpenReportHandler.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/OpenReportHandler.java
new file mode 100644 (file)
index 0000000..9bceec7
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewFactory;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysisReport;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfReportElement;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The command handler for the "Open Report" menu option for Report project
+ * model elements.
+ *
+ * Double-clicking should also call this handler.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class OpenReportHandler extends AbstractHandler {
+
+    @Override
+    public @Nullable Object execute(@Nullable ExecutionEvent event) throws ExecutionException {
+
+        /* Types should have been checked by the plugin.xml already */
+        ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
+        List<?> elements = ((IStructuredSelection) selection).toList();
+        List<TmfReportElement> reportElements = elements.stream()
+            .filter(elem -> elem instanceof TmfReportElement)
+            .map(elem -> (TmfReportElement) elem)
+            .collect(Collectors.toList());
+
+        for (TmfReportElement reportElem : reportElements) {
+            IOnDemandAnalysisReport report = reportElem.getReport();
+            if (!(report instanceof LamiAnalysisReport)) {
+                /* This handler deals with LAMI reports only */
+                continue;
+            }
+            LamiAnalysisReport lamiReport = (LamiAnalysisReport) report;
+
+            Display.getDefault().syncExec(() -> {
+                try {
+                    LamiReportViewFactory.createNewViews(lamiReport);
+                } catch (PartInitException e) {
+                }
+            });
+        }
+
+        return null;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/ParameterDialog.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/ParameterDialog.java
new file mode 100644 (file)
index 0000000..9fc7dd7
--- /dev/null
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.resource.StringConverter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Adaptation of {@link InputDialog}, to show the command in a grayed out,
+ * read-only input field, and a editable input field for the user to add extra
+ * parameters.
+ *
+ * @author Alexandre Montplaisir
+ */
+@NonNullByDefault({})
+class ParameterDialog extends Dialog {
+
+    private static final Color GRAY_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY);
+
+    private String fTitle;
+    private String fMessage;
+    private String fValue = "";//$NON-NLS-1$
+    private IInputValidator fValidator;
+    private Button fOkButton;
+    private Text fText;
+    private Text fErrorMessageText;
+    private String fErrorMessage;
+
+    private Text fBaseCommandText;
+    private final String fBaseCommand;
+
+    public ParameterDialog(Shell parentShell,
+            String dialogTitle,
+            String dialogMessage,
+            String baseCommand,
+            IInputValidator validator) {
+        super(parentShell);
+        fTitle = dialogTitle;
+        fMessage = dialogMessage;
+        fValue = "";//$NON-NLS-1$
+        fValidator = validator;
+        fBaseCommand = baseCommand;
+    }
+
+    @Override
+    protected void buttonPressed(int buttonId) {
+        if (buttonId == IDialogConstants.OK_ID) {
+            fValue = fText.getText();
+        } else {
+            fValue = null;
+        }
+        super.buttonPressed(buttonId);
+    }
+
+    @Override
+    protected void configureShell(Shell shell) {
+        super.configureShell(shell);
+        if (fTitle != null) {
+            shell.setText(fTitle);
+        }
+    }
+
+    @Override
+    protected void createButtonsForButtonBar(Composite parent) {
+        fOkButton = createButton(parent, IDialogConstants.OK_ID,
+                IDialogConstants.OK_LABEL, true);
+        createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, false);
+        fText.setFocus();
+        if (fValue != null) {
+            fText.setText(fValue);
+            fText.selectAll();
+        }
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        // create composite
+        Composite composite = (Composite) super.createDialogArea(parent);
+
+        Label label = new Label(composite, SWT.WRAP);
+        label.setText(Messages.ParameterDialog_BaseCommand + ':');
+
+        fBaseCommandText = new Text(composite, getInputTextStyle() | SWT.READ_ONLY);
+        fBaseCommandText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        fBaseCommandText.setText(fBaseCommand);
+        fBaseCommandText.setForeground(GRAY_COLOR);
+
+        // create message
+        if (fMessage != null) {
+            label = new Label(composite, SWT.WRAP);
+            label.setText(fMessage);
+            GridData data = new GridData(GridData.GRAB_HORIZONTAL
+                    | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL
+                    | GridData.VERTICAL_ALIGN_CENTER);
+            data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
+            label.setLayoutData(data);
+            label.setFont(parent.getFont());
+        }
+
+
+
+        fText = new Text(composite, getInputTextStyle());
+        fText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        fText.addModifyListener(e -> validateInput());
+        fErrorMessageText = new Text(composite, SWT.READ_ONLY | SWT.WRAP);
+        fErrorMessageText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        fErrorMessageText.setBackground(fErrorMessageText.getDisplay()
+                .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+        setErrorMessage(fErrorMessage);
+
+        applyDialogFont(composite);
+        return composite;
+    }
+
+    /**
+     * Returns the ok button.
+     *
+     * @return the ok button
+     */
+    protected Button getOkButton() {
+        return fOkButton;
+    }
+
+    /**
+     * Returns the string typed into this input dialog.
+     *
+     * @return the input string
+     */
+    public String getValue() {
+        return fValue;
+    }
+
+    /**
+     * Validates the input.
+     * <p>
+     * The default implementation of this framework method delegates the request
+     * to the supplied input validator object; if it finds the input invalid,
+     * the error message is displayed in the dialog's message line. This hook
+     * method is called whenever the text changes in the input field.
+     * </p>
+     */
+    protected void validateInput() {
+        String errMsg = null;
+        if (fValidator != null) {
+            errMsg = fValidator.isValid(fText.getText());
+        }
+        setErrorMessage(errMsg);
+    }
+
+    /**
+     * Sets or clears the error message.
+     * If not <code>null</code>, the OK button is disabled.
+     *
+     * @param errorMessage
+     *            the error message, or <code>null</code> to clear
+     */
+    private void setErrorMessage(String errorMessage) {
+        this.fErrorMessage = errorMessage;
+        if (fErrorMessageText != null && !fErrorMessageText.isDisposed()) {
+            fErrorMessageText.setText(errorMessage == null ? " \n " : errorMessage); //$NON-NLS-1$
+            boolean hasError = errorMessage != null && (StringConverter.removeWhiteSpaces(errorMessage)).length() > 0;
+            fErrorMessageText.setEnabled(hasError);
+            fErrorMessageText.setVisible(hasError);
+            fErrorMessageText.getParent().update();
+            Control button = getButton(IDialogConstants.OK_ID);
+            if (button != null) {
+                button.setEnabled(errorMessage == null);
+            }
+        }
+    }
+
+    /**
+     * Returns the style bits that should be used for the input text field.
+     * Defaults to a single line entry. Subclasses may override.
+     *
+     * @return the integer style bits that should be used when creating the
+     *         input text
+     */
+    protected int getInputTextStyle() {
+        return SWT.SINGLE | SWT.BORDER;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/RunAnalysisHandler.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/RunAnalysisHandler.java
new file mode 100644 (file)
index 0000000..8ad78e9
--- /dev/null
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysis;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewFactory;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysis;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.IOnDemandAnalysisReport;
+import org.eclipse.tracecompass.tmf.core.analysis.ondemand.OnDemandAnalysisException;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfCommonProjectElement;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfOnDemandAnalysisElement;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfReportsElement;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * The command handler for the "Run External Analysis" menu option.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class RunAnalysisHandler extends AbstractHandler {
+
+    @Override
+    public boolean isEnabled() {
+        // Check if we are closing down
+        final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        if (window == null) {
+            return false;
+        }
+
+        // Get the selection
+        final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        final IWorkbenchPart part = page.getActivePart();
+        if (part == null) {
+            return false;
+        }
+        final ISelectionProvider selectionProvider = part.getSite().getSelectionProvider();
+        if (selectionProvider == null) {
+            return false;
+        }
+        final ISelection selection = selectionProvider.getSelection();
+
+        /*
+         * plugin.xml should have done type verifications already
+         */
+        final Object element = ((IStructuredSelection) selection).getFirstElement();
+        TmfOnDemandAnalysisElement elem = (TmfOnDemandAnalysisElement) element;
+        if (elem.getAnalysis() instanceof LamiAnalysis && elem.canRun()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public @Nullable Object execute(@Nullable ExecutionEvent event) throws ExecutionException {
+
+        /* Types should have been checked by the plugin.xml already */
+        ISelection selection = HandlerUtil.getCurrentSelectionChecked(event);
+        Object element = ((IStructuredSelection) selection).getFirstElement();
+        final TmfOnDemandAnalysisElement analysisElem = (TmfOnDemandAnalysisElement) element;
+
+        TmfCommonProjectElement traceElem = analysisElem.getParent().getParent();
+        ITmfTrace trace = traceElem.getTrace();
+        if (trace == null) {
+            /* That trace is not currently opened */
+            return null;
+        }
+
+        /* Retrieve and initialize the analysis module, aka read the script's metadata */
+        IOnDemandAnalysis ondemandAnalysis = analysisElem.getAnalysis();
+        if (!(ondemandAnalysis instanceof LamiAnalysis)) {
+            return null;
+        }
+        LamiAnalysis analysis = (LamiAnalysis) ondemandAnalysis;
+
+        /* Retrieve the current time range, will be used as parameters to the analysis */
+        TmfTraceManager tm = TmfTraceManager.getInstance();
+        TmfTimeRange timeRange = tm.getCurrentTraceContext().getSelectionRange();
+        if (timeRange.getStartTime().equals(timeRange.getEndTime())) {
+            timeRange = null;
+        }
+        /* Job below needs a final reference... */
+        final TmfTimeRange tr = timeRange;
+
+        /* Pop the dialog to ask for extra parameters */
+        String baseCommand = analysis.getFullCommandAsString(trace, tr);
+
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        ParameterDialog dialog = new ParameterDialog(shell, Messages.ParameterDialog_ExternalParameters,
+                Messages.ParameterDialog_ExternalParametersDescription,
+                        baseCommand,
+                PARAM_STRING_VALIDATOR);
+        if (dialog.open() != Window.OK) {
+            /* User clicked Cancel, don't run */
+            return null;
+        }
+        String extraParams = nullToEmptyString(dialog.getValue());
+
+        /* Execute the analysis and produce the reports */
+        Job job = new Job(Messages.LamiAnalysis_MainTaskName) {
+            @Override
+            protected @Nullable IStatus run(@Nullable IProgressMonitor monitor) {
+                IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
+                try {
+                    List<LamiResultTable> results = analysis.execute(trace, tr, extraParams, mon);
+
+                    String reportName = analysis.getName() +' ' + Messages.ParameterDialog_ReportNameSuffix;
+                    LamiAnalysisReport report = new LamiAnalysisReport(reportName, results);
+                    registerNewReport(analysisElem, report);
+
+                    /* Automatically open the report for convenience */
+                    Display.getDefault().syncExec(() -> {
+                        try {
+                            LamiReportViewFactory.createNewViews(report);
+                        } catch (PartInitException e) {
+                        }
+                    });
+                    return Status.OK_STATUS;
+
+                } catch (OnDemandAnalysisException e) {
+                    String errMsg = e.getMessage();
+
+                    if (errMsg != null) {
+                        /* The analysis execution yielded an error */
+                        Display.getDefault().asyncExec(() -> {
+                            MessageDialog.openError(shell,
+                                    /* Dialog title */
+                                    Messages.ParameterDialog_Error,
+                                    /* Dialog message */
+                                    Messages.ParameterDialog_ErrorMessage + ":\n\n" + //$NON-NLS-1$
+                                            errMsg);
+                        });
+                    }
+
+                    return Status.CANCEL_STATUS;
+                }
+            }
+        };
+        job.schedule();
+
+        return null;
+    }
+
+    private static final IInputValidator PARAM_STRING_VALIDATOR = text -> {
+        if (text.isEmpty() || text.matches("[a-zA-Z0-9\\,\\-\\s]+")) { //$NON-NLS-1$
+            return null;
+        }
+        return Messages.ParameterDialog_StringValidatorMessage;
+    };
+
+    /**
+     * Register a new report
+     *
+     * @param analysisElem
+     *            The analysis's project element
+     * @param report
+     *            The report to add
+     */
+    public void registerNewReport(TmfOnDemandAnalysisElement analysisElem, IOnDemandAnalysisReport report) {
+        /* For now the TmfProjectReportsElement manages the reports. */
+        TmfReportsElement reportsElement = analysisElem
+                .getParent()
+                .getParent()
+                .getChildElementReports();
+
+        reportsElement.addReport(report);
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/messages.properties
new file mode 100644 (file)
index 0000000..113dbb7
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiAnalysis_MainTaskName = Invoking external analysis script
+
+ParameterDialog_BaseCommand = Base command
+ParameterDialog_ExternalParameters = External Analysis Parameters
+ParameterDialog_ExternalParametersDescription = Extra parameters to pass to the external analysis. Leave empty for none.
+ParameterDialog_StringValidatorMessage = Allowed characters are letters, numbers, ',' and '-'
+ParameterDialog_ReportNameSuffix = Report
+ParameterDialog_Error = Error running external script
+ParameterDialog_ErrorMessage = The script terminated abnormally
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/handler/package-info.java
new file mode 100644 (file)
index 0000000..a9a5464
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.handler;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/LamiSelectionUpdateSignal.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/LamiSelectionUpdateSignal.java
new file mode 100644 (file)
index 0000000..69dbb0f
--- /dev/null
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
+
+/**
+ * Enable signal sending on selection inside a LamiViewer implementation.
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+public class LamiSelectionUpdateSignal extends TmfSignal {
+
+    private final Set<Integer> fEntryIndexes;
+    /*
+     * TODO: replace this with an object to equals. A signalHash can only
+     * guaranty that objects are different, not that they are the same. Using
+     * this is looking for trouble.
+     */
+    private final int fSignalHash;
+
+    /**
+     * Constructor for a new signal.
+     *
+     * @param source
+     *            The object sending this signal
+     * @param entryIndexList
+     *            The list of selected indices
+     * @param signalHash
+     *            The hash for exclusivity signaling
+     */
+    public LamiSelectionUpdateSignal(Object source, Set<Integer> entryIndexList, int signalHash) {
+        super(source);
+        fEntryIndexes = new HashSet<>(entryIndexList);
+        fSignalHash = signalHash;
+    }
+
+
+    @Override
+    public String toString() {
+        return "[" + this.getClass().getSimpleName() + " (" + fEntryIndexes + ")]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+    /**
+     * Getter for the entryIndex
+     *
+     * @return
+     *          The new selected entry
+     */
+    public Set<Integer> getEntryIndex() {
+        return fEntryIndexes;
+    }
+
+
+    /**
+     * Getter for the exclusivity hash
+     *
+     * @return
+     *          The exclusivity hash
+     */
+    public int getSignalHash() {
+        return fSignalHash;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/signals/package-info.java
new file mode 100644 (file)
index 0000000..4a22c89
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/ILamiViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/ILamiViewer.java
new file mode 100644 (file)
index 0000000..ab7977a
--- /dev/null
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+
+/**
+ * Common interface for all Lami viewers.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ILamiViewer {
+
+    /**
+     * Dispose the viewer widget.
+     */
+    void dispose();
+
+    /**
+     * Factory method to create a new Table viewer.
+     *
+     * @param parent
+     *            The parent composite
+     * @param resultTable
+     *            The result table to display
+     * @return The new viewer
+     */
+    static ILamiViewer createLamiTable(Composite parent, LamiResultTable resultTable) {
+        TableViewer tableViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL);
+        return new LamiTableViewer(tableViewer, resultTable);
+    }
+
+    /**
+     * Factory method to create a new chart viewer. The chart type is specified
+     * by the 'chartModel' parameter.
+     *
+     * @param parent
+     *            The parent composite
+     * @param resultTable
+     *            The result table to use as a data source
+     * @param chartModel
+     *            The information about the chart to display
+     * @return The new viewer
+     */
+    static ILamiViewer createLamiChart(Composite parent, LamiResultTable resultTable, LamiChartModel chartModel) {
+        switch (chartModel.getChartType()) {
+        case BAR_CHART:
+            return new LamiBarChartViewer(parent, resultTable, chartModel);
+        case XY_SCATTER:
+            return new LamiScatterViewer(parent, resultTable, chartModel);
+        case PIE_CHART:
+        default:
+            throw new UnsupportedOperationException("Unsupported chart type: " + chartModel.toString()); //$NON-NLS-1$
+        }
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiBarChartViewer.java
new file mode 100644 (file)
index 0000000..bd48497
--- /dev/null
@@ -0,0 +1,450 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.swtchart.IAxis;
+import org.swtchart.IAxisTick;
+import org.swtchart.IBarSeries;
+import org.swtchart.ISeries;
+import org.swtchart.ISeries.SeriesType;
+import org.swtchart.Range;
+
+import com.google.common.collect.Iterators;
+
+/**
+ * Bar chart Viewer for LAMI views.
+ *
+ * @author Alexandre Montplaisir
+ * @author Jonathan Rajotte-Julien
+ * @author Mathieu Desnoyers
+ */
+public class LamiBarChartViewer extends LamiXYChartViewer {
+
+    private static final double LOGSCALE_EPSILON_FACTOR = 100.0;
+
+    private class Mapping {
+        final private @Nullable Integer fInternalValue;
+        final private @Nullable Integer fModelValue;
+
+        public Mapping(@Nullable Integer internalValue, @Nullable Integer modelValue) {
+            fInternalValue = internalValue;
+            fModelValue = modelValue;
+        }
+
+        public @Nullable Integer getInternalValue() {
+            return fInternalValue;
+        }
+
+        public @Nullable Integer getModelValue() {
+            return fModelValue;
+        }
+    }
+
+    private final String[] fCategories;
+    private final Map<ISeries, List<Mapping>> fIndexPerSeriesMapping;
+    private final Map<LamiTableEntry, Mapping> fEntryToCategoriesMap;
+
+    /**
+     * Creates a bar chart Viewer instance based on SWTChart.
+     *
+     * @param parent
+     *            The parent composite to draw in.
+     * @param resultTable
+     *            The result table containing the data from which to build the
+     *            chart
+     * @param chartModel
+     *            The information about the chart to build
+     */
+    public LamiBarChartViewer(Composite parent, LamiResultTable resultTable, LamiChartModel chartModel) {
+        super(parent, resultTable, chartModel);
+
+        List<LamiTableEntryAspect> xAxisAspects = getXAxisAspects();
+        List<LamiTableEntryAspect> yAxisAspects = getYAxisAspects();
+
+        /* bar chart cannot deal with multiple X series */
+        if (getChartModel().getChartType() != ChartType.BAR_CHART && xAxisAspects.size() != 1) {
+            throw new IllegalArgumentException("Invalid configuration passed to a bar chart."); //$NON-NLS-1$
+        }
+
+        /* Enable categories */
+        getChart().getAxisSet().getXAxis(0).enableCategory(true);
+
+        LamiTableEntryAspect xAxisAspect = xAxisAspects.get(0);
+        List<LamiTableEntry> entries = getResultTable().getEntries();
+        boolean logscale = chartModel.yAxisIsLog();
+        fIndexPerSeriesMapping = new HashMap<>();
+        fEntryToCategoriesMap = new HashMap<>();
+
+        /* Categories index mapping */
+        List<@Nullable String> xCategories = new ArrayList<>();
+        for (int i = 0; i < entries.size(); i++) {
+            String string = xAxisAspect.resolveString(entries.get(i));
+            if (string == null) {
+                fEntryToCategoriesMap.put(entries.get(i), new Mapping(null, i));
+                continue;
+            }
+            fEntryToCategoriesMap.put(entries.get(i), new Mapping(xCategories.size(), i));
+            xCategories.add(string);
+
+        }
+        fCategories = xCategories.toArray(new String[0]);
+
+        /*
+         * Log scale magic course 101:
+         *
+         * It uses the relative difference divided by a factor
+         * (100) to get as close as it can to the actual minimum but still a
+         * little bit smaller. This is used as a workaround of SWTCHART
+         * limitations regarding custom scale drawing in log scale mode, bogus
+         * representation of NaN double values and limited support of multiple
+         * size series.
+         *
+         * This should be good enough for most users.
+         */
+        double min = Double.MAX_VALUE;
+        double max = Double.MIN_VALUE;
+        double logScaleEpsilon = ZERO;
+        if (logscale) {
+
+            /* Find minimum and maximum values */
+            for (LamiTableEntryAspect aspect : yAxisAspects) {
+                for (LamiTableEntry entry : entries) {
+                    Double value = aspect.resolveDouble(entry);
+                    if (value == null || value <= 0) {
+                        continue;
+                    }
+                    min = Math.min(min, value);
+                    max = Math.max(max, value);
+                }
+            }
+
+            double delta = max - min;
+            logScaleEpsilon = min - ((min * delta) / (LOGSCALE_EPSILON_FACTOR * max));
+        }
+
+        for (LamiTableEntryAspect yAxisAspect : yAxisAspects) {
+            if (!yAxisAspect.isContinuous() || yAxisAspect.isTimeStamp()) {
+                /* Only plot continuous aspects */
+                continue;
+            }
+
+            List<Double> validXValues = new ArrayList<>();
+            List<Double> validYValues = new ArrayList<>();
+            List<Mapping> indexMapping = new ArrayList<>();
+
+            for (int i = 0; i < entries.size(); i++) {
+                Integer categoryIndex = checkNotNull(fEntryToCategoriesMap.get(checkNotNull(entries.get(i)))).fInternalValue;
+                Double yValue = yAxisAspect.resolveDouble(entries.get(i));
+                if (categoryIndex == null) {
+                    /* Invalid value do not show */
+                    continue;
+                }
+
+                if (yValue == null) {
+                    /*
+                     * Null value for y is the same as zero since this is a bar
+                     * chart
+                     */
+                    yValue = Double.valueOf(ZERO);
+                }
+
+                if (logscale && yValue <= ZERO) {
+                    /*
+                     * Less or equal to 0 values can't be plotted on a log
+                     * scale. We map them to the mean of the >=0 minimal value
+                     * and the calculated log scale magic epsilon.
+                     */
+                    yValue = (min + logScaleEpsilon) / 2.0;
+                }
+
+                validXValues.add(checkNotNull(categoryIndex).doubleValue());
+                validYValues.add(yValue.doubleValue());
+                indexMapping.add(new Mapping(categoryIndex, checkNotNull(fEntryToCategoriesMap.get(checkNotNull(entries.get(i)))).fModelValue));
+            }
+
+            String name = yAxisAspect.getLabel();
+
+            if (validXValues.isEmpty() || validYValues.isEmpty()) {
+                /* No need to plot an empty series */
+                continue;
+            }
+
+            IBarSeries barSeries = (IBarSeries) getChart().getSeriesSet().createSeries(SeriesType.BAR, name);
+            barSeries.setXSeries(validXValues.stream().mapToDouble(Double::doubleValue).toArray());
+            barSeries.setYSeries(validYValues.stream().mapToDouble(Double::doubleValue).toArray());
+            fIndexPerSeriesMapping.put(barSeries, indexMapping);
+        }
+
+        setBarSeriesColors();
+
+        /* Set all y axis logscale mode */
+        Stream.of(getChart().getAxisSet().getYAxes()).forEach(axis -> axis.enableLogScale(logscale));
+
+        /* Set the formatter on the Y axis */
+        IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick();
+        yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, entries));
+        yTick.setTickLabelAngle(1);
+
+        /* Adjust the chart range */
+        getChart().getAxisSet().adjustRange();
+        if (logscale) {
+            getChart().getAxisSet().getYAxis(0).setRange(new Range(logScaleEpsilon, max));
+        }
+
+        /* Once the chart is filled, refresh the axis labels */
+        refreshDisplayLabels();
+
+        /* Add mouse listener */
+        getChart().getPlotArea().addMouseListener(new LamiBarChartMouseDownListener());
+
+        /* Custom Painter listener to highlight the current selection */
+        getChart().getPlotArea().addPaintListener(new LamiBarChartPainterListener());
+    }
+
+    private final class LamiBarChartMouseDownListener extends MouseAdapter {
+
+        @Override
+        public void mouseDown(@Nullable MouseEvent event) {
+            if (event == null || event.button != 1) {
+                return;
+            }
+
+            boolean ctrlMode = false;
+            int xMouseLocation = event.x;
+            int yMouseLocation = event.y;
+
+            Set<Integer> selections;
+            if ((event.stateMask & SWT.CTRL) != 0) {
+                ctrlMode = true;
+                selections = getSelection();
+            } else {
+                /* Reset selection state */
+                unsetSelection();
+                selections = new HashSet<>();
+            }
+
+            ISeries[] series = getChart().getSeriesSet().getSeries();
+
+            /*
+             * Iterate over all series, get the rectangle bounds for each
+             * category, and find the category index under the mouse.
+             *
+             * Since categories map directly to the index of the fResultTable
+             * and that this table is immutable the index of the entry
+             * corresponds to the categories index. Signal to all LamiViewer and
+             * LamiView the update of selection.
+             */
+            for (ISeries oneSeries : series) {
+                IBarSeries barSerie = ((IBarSeries) oneSeries);
+                Rectangle[] recs = barSerie.getBounds();
+
+                for (int j = 0; j < recs.length; j++) {
+                    Rectangle rectangle = recs[j];
+                    if (rectangle.contains(xMouseLocation, yMouseLocation)) {
+                        int index = getTableEntryIndexFromGraphIndex(checkNotNull(oneSeries), j);
+                        if (!ctrlMode || (index >= 0 && !selections.remove(index))) {
+                            selections.add(index);
+                        }
+                    }
+                }
+            }
+
+            /* Save the current selection internally */
+            setSelection(selections);
+            /* Signal all Lami viewers & views of the selection */
+            LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(this,
+                    selections, getResultTable().hashCode());
+            TmfSignalManager.dispatchSignal(signal);
+            redraw();
+        }
+    }
+
+    @Override
+    protected void redraw() {
+        setBarSeriesColors();
+        super.redraw();
+    }
+
+    /**
+     * Set the chart series colors according to the selection state. Use light
+     * colors when a selection is present.
+     */
+    private void setBarSeriesColors() {
+        Iterator<Color> colorsIt;
+
+        if (isSelected()) {
+            colorsIt = Iterators.cycle(LIGHT_COLORS);
+        } else {
+            colorsIt = Iterators.cycle(COLORS);
+        }
+
+        for (ISeries series : getChart().getSeriesSet().getSeries()) {
+            ((IBarSeries) series).setBarColor(colorsIt.next());
+        }
+    }
+
+    private final class LamiBarChartPainterListener implements PaintListener {
+        @Override
+        public void paintControl(@Nullable PaintEvent e) {
+            if (e == null || !isSelected()) {
+                return;
+            }
+
+            Iterator<Color> colorsIt = Iterators.cycle(COLORS);
+            GC gc = e.gc;
+
+            for (ISeries series : getChart().getSeriesSet().getSeries()) {
+                Color color = colorsIt.next();
+                for (int index : getSelection()) {
+                    int graphIndex = getGraphIndexFromTableEntryIndex(series, index);
+                    if (graphIndex < 0) {
+                        /* Invalid index */
+                        continue;
+                    }
+
+                    Rectangle[] bounds = ((IBarSeries) series).getBounds();
+                    if (bounds.length != fCategories.length) {
+                        /*
+                         * The plot is too cramped and SWTChart currently does
+                         * its best on rectangle drawing and returns the
+                         * rectangle that it is able to draw.
+                         *
+                         * For now we simply do not draw since it is really hard
+                         * to see anyway. A better way to visualize the value
+                         * would be a full cross for each selection based on
+                         * their coordinates.
+                         */
+                        continue;
+                    }
+                    Rectangle rectangle = bounds[graphIndex];
+                    gc.setBackground(color);
+                    gc.fillRectangle(rectangle);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void refreshDisplayLabels() {
+        /* Only if we have at least 1 category */
+        if (fCategories.length == 0) {
+            return;
+        }
+
+        /* Only refresh if labels are visible */
+        IAxis xAxis = getChart().getAxisSet().getXAxis(0);
+        if (!xAxis.getTick().isVisible() || !xAxis.isCategoryEnabled()) {
+            return;
+        }
+
+        /*
+         * Shorten all the labels to 5 characters plus "…" when the longest
+         * label length is more than 50% of the chart height.
+         */
+
+        Rectangle rect = getChart().getClientArea();
+        int lengthLimit = (int) (rect.height * 0.40);
+
+        GC gc = new GC(fParent);
+        gc.setFont(xAxis.getTick().getFont());
+
+        /* Find the longest category string */
+        String longestString = Arrays.stream(fCategories).max(Comparator.comparingInt(String::length)).orElse(fCategories[0]);
+
+        /* Get the length and height of the longest label in pixels */
+        Point pixels = gc.stringExtent(longestString);
+
+        // Completely arbitrary
+        int cutLen = 5;
+
+        String[] displayCategories = new String[fCategories.length];
+        if (pixels.x > lengthLimit) {
+            /* We have to cut down some strings */
+            for (int i = 0; i < fCategories.length; i++) {
+                if (fCategories[i].length() > cutLen) {
+                    displayCategories[i] = fCategories[i].substring(0, cutLen) + ELLIPSIS;
+                } else {
+                    displayCategories[i] = fCategories[i];
+                }
+            }
+        } else {
+            /* All strings should fit */
+            displayCategories = Arrays.copyOf(fCategories, fCategories.length);
+        }
+        xAxis.setCategorySeries(displayCategories);
+
+        /* Cleanup */
+        gc.dispose();
+    }
+
+    private int getTableEntryIndexFromGraphIndex(ISeries series, int index) {
+        List<Mapping> indexes = fIndexPerSeriesMapping.get(series);
+        if (indexes == null || index > indexes.size() || index < 0) {
+            return -1;
+        }
+
+        Mapping mapping = indexes.get(index);
+        Integer modelValue = mapping.getModelValue();
+        if (modelValue != null) {
+            return modelValue.intValue();
+        }
+        return -1;
+    }
+
+    private int getGraphIndexFromTableEntryIndex(ISeries series, int index) {
+        List<Mapping> indexes = fIndexPerSeriesMapping.get(series);
+        if (indexes == null || index < 0) {
+            return -1;
+        }
+
+        int internalIndex = -1;
+        for (Mapping mapping : indexes) {
+            if (mapping.getModelValue() == index) {
+                Integer internalValue = mapping.getInternalValue();
+                if (internalValue != null) {
+                    internalIndex = internalValue.intValue();
+                    break;
+                }
+            }
+        }
+        return internalIndex;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiScatterViewer.java
new file mode 100644 (file)
index 0000000..5f8b9ec
--- /dev/null
@@ -0,0 +1,652 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiLabelFormat;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.swtchart.IAxisTick;
+import org.swtchart.ILineSeries;
+import org.swtchart.ISeries;
+import org.swtchart.ISeries.SeriesType;
+import org.swtchart.LineStyle;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Iterators;
+
+/**
+ * XY Scatter chart viewer for Lami views
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+public class LamiScatterViewer extends LamiXYChartViewer {
+
+    private static final int SELECTION_SNAP_RANGE_MULTIPLIER = 20;
+    private static final int SELECTION_CROSS_SIZE_MULTIPLIER = 3;
+
+    private final Map<ISeries, List<Integer>> fIndexMapping;
+
+    /* The current data point for the hovering cross */
+    private Point fHoveringCrossDataPoint;
+
+    /**
+     * Constructor
+     *
+     * @param parent
+     *            parent
+     * @param resultTable
+     *            Result table populating this chart
+     * @param graphModel
+     *            Model of this chart
+     */
+    public LamiScatterViewer(Composite parent, LamiResultTable resultTable, LamiChartModel graphModel) {
+        super(parent, resultTable, graphModel);
+        if (getChartModel().getChartType() != ChartType.XY_SCATTER) {
+            throw new IllegalStateException("Chart type not a Scatter Chart " + getChartModel().getChartType().toString()); //$NON-NLS-1$
+        }
+
+        /* Inspect X series */
+        fIndexMapping = new HashMap<>();
+
+        fHoveringCrossDataPoint = new Point(-1, -1);
+
+        List<LamiTableEntryAspect> xAxisAspects = getXAxisAspects();
+        if (xAxisAspects.stream().distinct().count() == 1) {
+            LamiTableEntryAspect singleXAspect = xAxisAspects.get(0);
+            xAxisAspects.clear();
+            xAxisAspects.add(singleXAspect);
+        }
+
+        BiMap<@Nullable String, Integer> xMap = checkNotNull(HashBiMap.create());
+        boolean xIsLog = graphModel.xAxisIsLog();
+
+        boolean areXAspectsContinuous = areAspectsContinuous(xAxisAspects);
+        boolean areXAspectsTimeStamp = areAspectsTimeStamp(xAxisAspects);
+
+        /* Check all aspect are the same type */
+        for (LamiTableEntryAspect aspect : xAxisAspects) {
+            if (aspect.isContinuous() != areXAspectsContinuous) {
+                throw new IllegalStateException("Some X aspects are continuous and some are not"); //$NON-NLS-1$
+            }
+            if (aspect.isTimeStamp() != areXAspectsTimeStamp) {
+                throw new IllegalStateException("Some X aspects are time based and some are not"); //$NON-NLS-1$
+            }
+        }
+
+        /*
+         * When xAxisAspects are discrete create a map for all values of all
+         * series
+         */
+        if (!areXAspectsContinuous) {
+            generateLabelMap(xAxisAspects, checkNotNull(xMap));
+        }
+
+        /*
+         * Create Y series
+         */
+        List<LamiTableEntryAspect> yAxisAspects = getYAxisAspects();
+        BiMap<@Nullable String, Integer> yMap = checkNotNull(HashBiMap.create());
+        boolean yIsLog = graphModel.yAxisIsLog();
+
+        boolean areYAspectsContinuous = areAspectsContinuous(yAxisAspects);
+        boolean areYAspectsTimeStamp = areAspectsTimeStamp(yAxisAspects);
+
+        /* Check all aspect are the same type */
+        for (LamiTableEntryAspect aspect : yAxisAspects) {
+            if (aspect.isContinuous() != areYAspectsContinuous) {
+                throw new IllegalStateException("Some Y aspects are continuous and some are not"); //$NON-NLS-1$
+            }
+            if (aspect.isTimeStamp() != areYAspectsTimeStamp) {
+                throw new IllegalStateException("Some Y aspects are time based and some are not"); //$NON-NLS-1$
+            }
+        }
+
+        /*
+         * When yAspects are discrete create a map for all values of all series
+         */
+        if (!areYAspectsContinuous) {
+            generateLabelMap(yAxisAspects, yMap);
+        }
+
+        /* Plot the series */
+        int index = 0;
+        for (LamiTableEntryAspect yAspect : getYAxisAspects()) {
+            String name = ""; //$NON-NLS-1$
+            LamiTableEntryAspect xAspect;
+            if (xAxisAspects.size() == 1) {
+                /* Always map to the same x series */
+                xAspect = xAxisAspects.get(0);
+                name = yAspect.getLabel();
+            } else {
+                xAspect = xAxisAspects.get(index);
+                name = (yAspect.getName() + ' ' + Messages.LamiScatterViewer_by + ' ' + xAspect.getName());
+            }
+
+            List<@Nullable Double> xDoubleSeries = new ArrayList<>();
+            List<@Nullable Double> yDoubleSeries = new ArrayList<>();
+
+            if (xAspect.isContinuous()) {
+                xDoubleSeries = getResultTable().getEntries().stream().map((entry -> xAspect.resolveDouble(entry))).collect(Collectors.toList());
+            } else {
+                xDoubleSeries = getResultTable().getEntries().stream().map(entry -> {
+                    String string = xAspect.resolveString(entry);
+                    Integer value = xMap.get(string);
+                    if (value != null) {
+                        return Double.valueOf(value.doubleValue());
+                    }
+                    return null;
+
+                }).collect(Collectors.toList());
+            }
+
+            if (yAspect.isContinuous()) {
+                yDoubleSeries = getResultTable().getEntries().stream().map((entry -> yAspect.resolveDouble(entry))).collect(Collectors.toList());
+            } else {
+                yDoubleSeries = getResultTable().getEntries().stream().map(entry -> {
+                    String string = yAspect.resolveString(entry);
+                    Integer value = yMap.get(string);
+                    if (value != null) {
+                        return Double.valueOf(value.doubleValue());
+                    }
+                    return null;
+
+                }).collect(Collectors.toList());
+            }
+
+            List<@Nullable Double> validXDoubleSeries = new ArrayList<>();
+            List<@Nullable Double> validYDoubleSeries = new ArrayList<>();
+            List<Integer> indexSeriesCorrespondance = new ArrayList<>();
+
+            if (xDoubleSeries.size() != yDoubleSeries.size()) {
+                throw new IllegalStateException("Series sizes don't match!"); //$NON-NLS-1$
+            }
+
+            /* Check for invalid tuple value. Any null elements are invalid */
+            for (int i = 0; i < xDoubleSeries.size(); i++) {
+                Double xValue = xDoubleSeries.get(i);
+                Double yValue = yDoubleSeries.get(i);
+                if (xValue == null || yValue == null) {
+                    /* Reject this tuple */
+                    continue;
+                }
+                if ((xIsLog && xValue <= ZERO) || (yIsLog && yValue <= ZERO)) {
+                    /*
+                     * Equal or less than 0 values can't be plotted on log scale
+                     */
+                    continue;
+                }
+                validXDoubleSeries.add(xValue);
+                validYDoubleSeries.add(yValue);
+                indexSeriesCorrespondance.add(i);
+            }
+
+            if (validXDoubleSeries.isEmpty() || validXDoubleSeries.isEmpty()) {
+                /* No need to plot an empty series */
+                index++;
+                continue;
+            }
+
+            ILineSeries scatterSeries = (ILineSeries) getChart().getSeriesSet().createSeries(SeriesType.LINE, name);
+            scatterSeries.setLineStyle(LineStyle.NONE);
+
+            double[] xserie = validXDoubleSeries.stream().mapToDouble(elem -> checkNotNull(elem).doubleValue()).toArray();
+            double[] yserie = validYDoubleSeries.stream().mapToDouble(elem -> checkNotNull(elem).doubleValue()).toArray();
+            scatterSeries.setXSeries(xserie);
+            scatterSeries.setYSeries(yserie);
+            fIndexMapping.put(scatterSeries, indexSeriesCorrespondance);
+            index++;
+        }
+
+        /* Modify x axis related chart styling */
+        IAxisTick xTick = getChart().getAxisSet().getXAxis(0).getTick();
+        if (areXAspectsContinuous) {
+            xTick.setFormat(getContinuousAxisFormatter(xAxisAspects, getResultTable().getEntries()));
+        } else {
+            xTick.setFormat(new LamiLabelFormat(checkNotNull(xMap)));
+            updateTickMark(checkNotNull(xMap), xTick, getChart().getPlotArea().getSize().x);
+
+            /* Remove vertical grid line */
+            getChart().getAxisSet().getXAxis(0).getGrid().setStyle(LineStyle.NONE);
+        }
+
+        /* Modify Y axis related chart styling */
+        IAxisTick yTick = getChart().getAxisSet().getYAxis(0).getTick();
+        if (areYAspectsContinuous) {
+            yTick.setFormat(getContinuousAxisFormatter(yAxisAspects, getResultTable().getEntries()));
+        } else {
+            yTick.setFormat(new LamiLabelFormat(checkNotNull(yMap)));
+            updateTickMark(checkNotNull(yMap), yTick, getChart().getPlotArea().getSize().y);
+
+            /*
+             * SWTChart workaround: SWTChart fiddles with tick mark visibility
+             * based on the fact that it can parse the label to double or not.
+             *
+             * If the label happens to be a double, it checks for the presence
+             * of that value in its own tick labels to decide if it should add
+             * it or not. If it happens that the parsed value is already present
+             * in its map, the tick gets a visibility of false.
+             *
+             * The X axis does not have this problem since SWTCHART checks on
+             * label angle, and if it is != 0 simply does no logic regarding
+             * visibility. So simply set a label angle of 1 to the axis.
+             */
+            yTick.setTickLabelAngle(1);
+
+            /* Remove horizontal grid line */
+            getChart().getAxisSet().getYAxis(0).getGrid().setStyle(LineStyle.NONE);
+        }
+
+        setLineSeriesColor();
+
+        /* Put log scale if necessary */
+        if (xIsLog && areXAspectsContinuous && !areXAspectsTimeStamp) {
+            Stream.of(getChart().getAxisSet().getXAxes()).forEach(axis -> axis.enableLogScale(xIsLog));
+        }
+
+        if (yIsLog && areYAspectsContinuous && !areYAspectsTimeStamp) {
+            /* Set the axis as logscale */
+            Stream.of(getChart().getAxisSet().getYAxes()).forEach(axis -> axis.enableLogScale(yIsLog));
+        }
+        getChart().getAxisSet().adjustRange();
+
+        /*
+         * Selection listener
+         */
+        getChart().getPlotArea().addMouseListener(new LamiScatterMouseDownListener());
+
+        /*
+         * Hovering cross listener
+         */
+        getChart().getPlotArea().addMouseMoveListener(new HoveringCrossListener());
+
+        /*
+         * Mouse exit listener: reset state of hovering cross on mouse exit.
+         */
+        getChart().getPlotArea().addListener(SWT.MouseExit, new Listener() {
+
+            @Override
+            public void handleEvent(@Nullable Event event) {
+                if (event != null) {
+                    fHoveringCrossDataPoint.x = -1;
+                    fHoveringCrossDataPoint.y = -1;
+                    redraw();
+                }
+            }
+        });
+
+        /*
+         * Selections and hovering cross painting
+         */
+        getChart().getPlotArea().addPaintListener(new LamiScatterPainterListener());
+
+        /* On resize check for axis tick updating */
+        getChart().addListener(SWT.Resize, new Listener() {
+            @Override
+            public void handleEvent(@Nullable Event event) {
+                if (yTick.getFormat() instanceof LamiLabelFormat) {
+                    updateTickMark(checkNotNull(yMap), yTick, getChart().getPlotArea().getSize().y);
+                }
+                if (xTick.getFormat() instanceof LamiLabelFormat) {
+                    updateTickMark(checkNotNull(xMap), xTick, getChart().getPlotArea().getSize().x);
+                }
+            }
+        });
+    }
+
+    private void generateLabelMap(List<LamiTableEntryAspect> aspects, BiMap<@Nullable String, Integer> map) {
+        TreeSet<@Nullable String> set = new TreeSet<>();
+        for (LamiTableEntryAspect aspect : aspects) {
+            for (LamiTableEntry entry : getResultTable().getEntries()) {
+                String string = aspect.resolveString(entry);
+                if (string != null) {
+                    set.add(string);
+                }
+            }
+        }
+        /* Ordered label mapping to double */
+        for (String string : set) {
+            map.put(string, map.size());
+        }
+    }
+
+    /**
+     * Set the chart series colors.
+     */
+    private void setLineSeriesColor() {
+        Iterator<Color> colorsIt;
+
+        colorsIt = Iterators.cycle(COLORS);
+
+        for (ISeries series : getChart().getSeriesSet().getSeries()) {
+            ((ILineSeries) series).setSymbolColor((colorsIt.next()));
+            /*
+             * Generate initial array of Color to enable per point color change
+             * on selection in the future
+             */
+            ArrayList<Color> colors = new ArrayList<>();
+            for (int i = 0; i < series.getXSeries().length; i++) {
+                Color color = ((ILineSeries) series).getSymbolColor();
+                colors.add(checkNotNull(color));
+            }
+            ((ILineSeries) series).setSymbolColors(colors.toArray(new Color[colors.size()]));
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Listeners
+    // ------------------------------------------------------------------------
+
+    private final class HoveringCrossListener implements MouseMoveListener {
+
+        @Override
+        public void mouseMove(@Nullable MouseEvent e) {
+            if (e == null) {
+                return;
+            }
+            ISeries[] series = getChart().getSeriesSet().getSeries();
+            @Nullable Point closest = null;
+            double closestDistance = -1.0;
+
+            for (ISeries oneSeries : series) {
+                ILineSeries lineSerie = (ILineSeries) oneSeries;
+                for (int i = 0; i < lineSerie.getXSeries().length; i++) {
+                    Point dataPoint = lineSerie.getPixelCoordinates(i);
+
+                    /*
+                     * Find the distance between the data point and the mouse
+                     * location and compare it to the symbol size * the range
+                     * multiplier, so when a user hovers the mouse near the dot
+                     * the cursor cross snaps to it.
+                     */
+                    int snapRangeRadius = lineSerie.getSymbolSize() * SELECTION_SNAP_RANGE_MULTIPLIER;
+
+                    /*
+                     * FIXME if and only if performance of this code is an issue
+                     * for large sets, this can be accelerated by getting the
+                     * distance squared, and if it is smaller than
+                     * snapRangeRadius squared, then check hypot.
+                     */
+                    double distance = Math.hypot(dataPoint.x - e.x, dataPoint.y - e.y);
+                    if (distance < snapRangeRadius) {
+                        if (closestDistance == -1 || distance < closestDistance) {
+                            closest = dataPoint;
+                            closestDistance = distance;
+                        }
+                    }
+                }
+            }
+            if (closest != null) {
+                fHoveringCrossDataPoint.x = closest.x;
+                fHoveringCrossDataPoint.y = closest.y;
+            } else {
+                fHoveringCrossDataPoint.x = -1;
+                fHoveringCrossDataPoint.y = -1;
+            }
+            refresh();
+        }
+    }
+
+    private final class LamiScatterMouseDownListener extends MouseAdapter {
+
+        @Override
+        public void mouseDown(@Nullable MouseEvent event) {
+            if (event == null || event.button != 1) {
+                return;
+            }
+
+            int xMouseLocation = event.x;
+            int yMouseLocation = event.y;
+
+            boolean ctrlMode = false;
+
+            ISeries[] series = getChart().getSeriesSet().getSeries();
+            Set<Integer> selections = getSelection();
+
+            /* Check for ctrl on click */
+            if ((event.stateMask & SWT.CTRL) != 0) {
+                selections = getSelection();
+                ctrlMode = true;
+            } else {
+                /* Reset selection */
+                unsetSelection();
+                selections = new HashSet<>();
+            }
+
+            for (ISeries oneSeries : series) {
+                ILineSeries lineSerie = (ILineSeries) oneSeries;
+
+                int closest = -1;
+                double closestDistance = -1;
+                for (int i = 0; i < lineSerie.getXSeries().length; i++) {
+                    Point dataPoint = lineSerie.getPixelCoordinates(i);
+
+                    /*
+                     * Find the distance between the data point and the mouse
+                     * location, and compare it to the symbol size so when a
+                     * user clicks on a symbol it selects it.
+                     */
+                    double distance = Math.hypot(dataPoint.x - xMouseLocation, dataPoint.y - yMouseLocation);
+                    int snapRangeRadius = lineSerie.getSymbolSize() * SELECTION_SNAP_RANGE_MULTIPLIER;
+                    if (distance < snapRangeRadius) {
+                        if (closestDistance == -1 || distance < closestDistance) {
+                            closest = i;
+                            closestDistance = distance;
+                        }
+                    }
+                }
+                if (closest != -1) {
+                    /* Translate to global index */
+                    int tableEntryIndex = getTableEntryIndexFromGraphIndex(checkNotNull(oneSeries), closest);
+                    if (tableEntryIndex < 0) {
+                        continue;
+                    }
+                    LamiTableEntry entry = getResultTable().getEntries().get(tableEntryIndex);
+                    int index = getResultTable().getEntries().indexOf(entry);
+
+                    if (!ctrlMode || !selections.remove(index)) {
+                        selections.add(index);
+                    }
+                    /* Do no iterate since we already found a match */
+                    break;
+                }
+            }
+            setSelection(selections);
+            /* Signal all Lami viewers & views of the selection */
+            LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(this,
+                    selections, checkNotNull(getResultTable().hashCode()));
+            TmfSignalManager.dispatchSignal(signal);
+            refresh();
+        }
+    }
+
+    private final class LamiScatterPainterListener implements PaintListener {
+
+        @Override
+        public void paintControl(@Nullable PaintEvent e) {
+            if (e == null) {
+                return;
+            }
+            GC gc = e.gc;
+
+            /* Draw the selection */
+            drawSelectedDot(checkNotNull(gc));
+
+            /* Draw the hovering cross */
+            drawHoveringCross(checkNotNull(gc));
+        }
+
+        private void drawSelectedDot(GC gc) {
+            if (isSelected()) {
+                Iterator<Color> colorsIt;
+                colorsIt = Iterators.cycle(COLORS);
+                for (ISeries series : getChart().getSeriesSet().getSeries()) {
+
+                    /* Get series colors */
+                    Color color = colorsIt.next();
+                    int symbolSize = ((ILineSeries) series).getSymbolSize();
+
+                    for (int index : getInternalSelections()) {
+                        int graphIndex = getGraphIndexFromTableEntryIndex(series, index);
+
+                        if (graphIndex < 0) {
+                            continue;
+                        }
+                        Point point = series.getPixelCoordinates(graphIndex);
+
+                        /* Create a colored dot for selection */
+                        gc.setBackground(color);
+                        gc.fillOval(point.x - symbolSize, point.y - symbolSize, symbolSize * 2, symbolSize * 2);
+
+                        /* Draw cross */
+                        gc.setLineWidth(2);
+                        gc.setLineStyle(SWT.LINE_SOLID);
+                        /* Vertical line */
+                        int drawingDelta = SELECTION_CROSS_SIZE_MULTIPLIER * symbolSize;
+                        gc.drawLine(point.x, point.y - drawingDelta, point.x, point.y + drawingDelta);
+                        /* Horizontal line */
+                        gc.drawLine(point.x - drawingDelta, point.y, point.x + drawingDelta, point.y);
+
+                    }
+                }
+            }
+        }
+
+        private void drawHoveringCross(GC gc) {
+            gc.setLineWidth(1);
+            gc.setLineStyle(SWT.LINE_SOLID);
+            gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+            gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+            /* Vertical line */
+            gc.drawLine(fHoveringCrossDataPoint.x, 0, fHoveringCrossDataPoint.x, getChart().getPlotArea().getSize().y);
+            /* Horizontal line */
+            gc.drawLine(0, fHoveringCrossDataPoint.y, getChart().getPlotArea().getSize().x, fHoveringCrossDataPoint.y);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility functions
+    // ------------------------------------------------------------------------
+
+    private int getTableEntryIndexFromGraphIndex(ISeries series, int index) {
+        List<Integer> indexes = fIndexMapping.get(series);
+        if (indexes == null || index > indexes.size() || index < 0) {
+            return -1;
+        }
+        return indexes.get(index);
+    }
+
+    private int getGraphIndexFromTableEntryIndex(ISeries series, int index) {
+        List<Integer> indexes = fIndexMapping.get(series);
+        if (indexes == null || !indexes.contains(index)) {
+            return -1;
+        }
+        return indexes.indexOf(index);
+    }
+
+    @Override
+    protected void refreshDisplayLabels() {
+    }
+
+    /**
+     * Return the current selection in internal mapping
+     *
+     * @return the internal selections
+     */
+    protected Set<Integer> getInternalSelections() {
+        /* Translate to internal table location */
+        Set<Integer> indexes = super.getSelection();
+        Set<Integer> internalIndexes = indexes.stream()
+                .mapToInt(index -> getResultTable().getEntries().indexOf((getResultTable().getEntries().get(index))))
+                .boxed()
+                .collect(Collectors.toSet());
+        return internalIndexes;
+    }
+
+    private static void updateTickMark(BiMap<@Nullable String, Integer> map, IAxisTick tick, int availableLenghtPixel) {
+        int nbLabels = Math.max(1, map.size());
+        int stepSizePixel = availableLenghtPixel / nbLabels;
+        /*
+         * This step is a limitation on swtchart side regarding minimal grid
+         * step hint size. When the step size are smaller it get defined as the
+         * "default" value for the axis instead of the smallest one.
+         */
+        if (IAxisTick.MIN_GRID_STEP_HINT > stepSizePixel) {
+            stepSizePixel = (int) IAxisTick.MIN_GRID_STEP_HINT;
+        }
+        tick.setTickMarkStepHint(stepSizePixel);
+    }
+
+    @Override
+    protected void setSelection(@NonNull Set<@NonNull Integer> selection) {
+        super.setSelection(selection);
+
+        /* Set color of selected symbol */
+        Iterator<Color> colorsIt = Iterators.cycle(COLORS);
+        Iterator<Color> lightColorsIt = Iterators.cycle(LIGHT_COLORS);
+
+        Set<Integer> currentSelections = getInternalSelections();
+
+        for (ISeries series : getChart().getSeriesSet().getSeries()) {
+            /* Series color */
+            Color lightColor = lightColorsIt.next();
+            Color color = colorsIt.next();
+            Color[] colors = ((ILineSeries) series).getSymbolColors();
+
+            if (currentSelections.isEmpty()) {
+                /* Put all symbols to the normal colors */
+                Arrays.fill(colors, color);
+            } else {
+                /*
+                 * Fill with light colors to represent the deselected state. The
+                 * paint listener is then responsible for drawing the cross and
+                 * the dark colors for the selection.
+                 */
+                Arrays.fill(colors, lightColor);
+            }
+            ((ILineSeries) series).setSymbolColors(colors);
+        }
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableContentProvider.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableContentProvider.java
new file mode 100644 (file)
index 0000000..e7fe5a3
--- /dev/null
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Ericsson, EfficiOS Inc. and others
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.table.ISortingLazyContentProvider;
+
+/**
+ * Content provider for the latency table viewers.
+ *
+ * @author France Lapointe Nguyen
+ * @author Alexandre Montplaisir
+ */
+class LamiTableContentProvider implements ISortingLazyContentProvider {
+
+    /**
+     * Table viewer of the latency table viewer
+     */
+    private @Nullable TableViewer fTableViewer = null;
+
+    private List<LamiTableEntry> fCurrentEntries;
+
+    private @Nullable Comparator<LamiTableEntry> fComparator = null;
+
+    /**
+     * Flag to avoid recursive calls to {@link #updateElement} due to table
+     * refreshes.
+     */
+    private volatile boolean fOngoingUpdate = false;
+
+    /**
+     * Constructor.
+     */
+    public LamiTableContentProvider() {
+        fCurrentEntries = checkNotNull(Collections.EMPTY_LIST);
+    }
+
+    @Override
+    public void updateElement(int index) {
+        final TableViewer tableViewer = fTableViewer;
+        final List<LamiTableEntry> entries = fCurrentEntries;
+        if ((tableViewer != null) && (entries.size() > index) && !fOngoingUpdate) {
+            fOngoingUpdate = true;
+            tableViewer.replace(entries.get(index), index);
+            fOngoingUpdate = false;
+        }
+    }
+
+    @Override
+    public void dispose() {
+        fCurrentEntries = checkNotNull(Collections.EMPTY_LIST);
+        fTableViewer = null;
+        fComparator = null;
+    }
+
+    @Override
+    public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) {
+        fTableViewer = (TableViewer) viewer;
+        if (!(newInput instanceof List<?>)) {
+            /*
+             * Should be a List<BabeltraceTableEntry>, but may be null if it is
+             * not yet set.
+             */
+            return;
+        }
+        @SuppressWarnings("unchecked")
+        List<LamiTableEntry> entries = (List<LamiTableEntry>) newInput;
+
+        /* Do a copy here so that we can sort it to our heart's content */
+        fCurrentEntries = new ArrayList<>(entries);
+
+        if (fComparator != null) {
+            Collections.sort(fCurrentEntries, fComparator);
+        }
+    }
+
+    @Override
+    public void setSortOrder(@Nullable Comparator<?> comparator) {
+        if (comparator == null) {
+            return;
+        }
+        final TableViewer tableViewer = fTableViewer;
+        if (tableViewer == null) {
+            return;
+        }
+        @SuppressWarnings("unchecked")
+        Comparator<LamiTableEntry> entryComparator = (Comparator<LamiTableEntry>) comparator;
+        fComparator = entryComparator;
+        Collections.sort(fCurrentEntries, fComparator);
+        tableViewer.refresh();
+    }
+
+    /**
+     * Get the segment count
+     *
+     * @return the segment count
+     */
+    public int getNbEntries() {
+        return fCurrentEntries.size();
+    }
+
+    /**
+     * Get the index of a table entry.
+     *
+     * @param entry
+     *            Entry to look for
+     * @return the index of the table entry
+     */
+    public int getIndexOf(LamiTableEntry entry) {
+        return fCurrentEntries.indexOf(entry);
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiTableViewer.java
new file mode 100644 (file)
index 0000000..6bc712d
--- /dev/null
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Ericsson, EfficiOS Inc. and others
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportView;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.ui.viewers.table.TmfSimpleTableViewer;
+
+/**
+ * Table viewer to use in {@link LamiReportView}s.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class LamiTableViewer extends TmfSimpleTableViewer implements ILamiViewer {
+
+    // ------------------------------------------------------------------------
+    // Attributes
+    // ------------------------------------------------------------------------
+
+    private final LamiResultTable fResultTable;
+    private Set<Integer> fSelections;
+
+    // ------------------------------------------------------------------------
+    // Inner class definitions
+    // ------------------------------------------------------------------------
+
+    /**
+     * Abstract class for the column label provider for the latency analysis
+     * table viewer
+     */
+    private static class LamiTableColumnLabelProvider extends ColumnLabelProvider {
+
+        private final LamiTableEntryAspect fColumnAspect;
+
+        public LamiTableColumnLabelProvider(LamiTableEntryAspect aspect) {
+            fColumnAspect = aspect;
+        }
+
+        @Override
+        public String getText(@Nullable Object input) {
+            if (!(input instanceof LamiTableEntry)) {
+                /* Doubles as a null check */
+                return ""; //$NON-NLS-1$
+            }
+            LamiTableEntry entry = (LamiTableEntry) input;
+            return nullToEmptyString(fColumnAspect.resolveString(entry));
+        }
+    }
+
+    /**
+     * Listener to update in other viewers when a cell of the latency
+     * table view is selected
+     */
+    private class LamiTableSelectionListener extends SelectionAdapter {
+        @Override
+        public void widgetSelected(@Nullable SelectionEvent e) {
+
+            IStructuredSelection selections = getTableViewer().getStructuredSelection();
+
+            Set<Integer> selectionIndexes = new HashSet<>();
+            for (Object selectedEntry : selections.toArray() ) {
+                selectionIndexes.add(fResultTable.getEntries().indexOf(selectedEntry));
+            }
+
+            fSelections = selectionIndexes;
+
+            /* Signal all Lami viewers & views of the selection */
+            LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiTableViewer.this, selectionIndexes, checkNotNull(fResultTable).hashCode());
+            TmfSignalManager.dispatchSignal(signal);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructor
+     *
+     * @param tableViewer
+     *            Table viewer of the view
+     * @param resultTable
+     *            Data table populating this viewer
+     */
+    public LamiTableViewer(TableViewer tableViewer, LamiResultTable resultTable) {
+        super(tableViewer);
+        /*
+         * The table viewer should always be the first element in the control.
+         */
+        tableViewer.getTable().moveAbove(null);
+
+        fResultTable = resultTable;
+        fSelections = new HashSet<>();
+
+        /* Default sort order of the content provider is by its first column */
+        getTableViewer().setContentProvider(new LamiTableContentProvider());
+        getTableViewer().getTable().addSelectionListener(new LamiTableSelectionListener());
+
+        createColumns();
+        fillData();
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    private void createColumns() {
+        final List<LamiTableEntryAspect> aspects = fResultTable.getTableClass().getAspects();
+
+        Display.getDefault().asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                for (LamiTableEntryAspect aspect : aspects) {
+                    createColumn(aspect.getLabel(), new LamiTableColumnLabelProvider(aspect), aspect.getComparator());
+                }
+            }
+        });
+    }
+
+    /**
+     * Update the data in the table viewer
+     *
+     * @param dataInput
+     *            New data input
+     */
+    private void fillData() {
+        final TableViewer tableViewer = getTableViewer();
+        Display.getDefault().asyncExec(() -> {
+            if (tableViewer.getTable().isDisposed()) {
+                return;
+            }
+            // Go to the top of the table
+            tableViewer.getTable().setTopIndex(0);
+            // Reset selected row
+            tableViewer.setSelection(StructuredSelection.EMPTY);
+
+            /* Fill the table data */
+            tableViewer.setInput(fResultTable.getEntries());
+            LamiTableContentProvider latencyContentProvider = (LamiTableContentProvider) getTableViewer().getContentProvider();
+            tableViewer.setItemCount(latencyContentProvider.getNbEntries());
+
+            /* Set the column's alignment and pack them */
+            TableColumn[] cols = tableViewer.getTable().getColumns();
+            for (int i = 0; i < cols.length; i++) {
+                LamiTableEntryAspect colAspect = fResultTable.getTableClass().getAspects().get(i);
+                int alignment = (colAspect.isContinuous() ? SWT.RIGHT : SWT.LEFT);
+                cols[i].setAlignment(alignment);
+
+            }
+
+            /*
+             * On creation check if there is selections if so update the table
+             * selections here. Selections needs the ContentProvider for valid
+             * index lookup and since the content provider is set in an
+             * asynchronous task we cannot use the normal signal handler since
+             * we have no guarantee of time of execution of the fill data.
+             */
+            if (!fSelections.isEmpty()) {
+                int[] selectionsIndexes = fSelections.stream().map(index -> fResultTable.getEntries().get(index)).mapToInt(entry -> ((LamiTableContentProvider) getTableViewer().getContentProvider()).getIndexOf(entry)).toArray();
+                Display.getDefault().asyncExec(() -> {
+                    getTableViewer().getTable().setSelection(selectionsIndexes);
+                    getTableViewer().getTable().redraw();
+                });
+            }
+        });
+        Display.getDefault().asyncExec(() -> {
+            TableColumn[] cols = tableViewer.getTable().getColumns();
+            for (int i = 0; i < cols.length; i++) {
+                cols[i].pack();
+            }
+        });
+    }
+
+    /**
+     * The signal handler for selection update.
+     *
+     * @param signal
+     *          The selection update signal
+     */
+    @TmfSignalHandler
+    public void updateSelection(LamiSelectionUpdateSignal signal) {
+
+        if (fResultTable.hashCode() != signal.getSignalHash() || equals(signal.getSource())) {
+            /* The signal is not for us */
+            return;
+         }
+        /* Fetch the position of the selected entry in the actual table since it could be sorted by another column */
+        LamiTableContentProvider latencyContentProvider = (LamiTableContentProvider) getTableViewer().getContentProvider();
+
+        Set<Integer> selections = signal.getEntryIndex();
+
+        int[] selectionsIndexes = selections.stream()
+                .map(index -> fResultTable.getEntries().get(index))
+                .mapToInt(entry -> latencyContentProvider.getIndexOf(entry))
+                .toArray();
+
+        fSelections = new HashSet<>(selections);
+
+        Display.getDefault().asyncExec(() -> {
+            getTableViewer().getTable().setSelection(selectionsIndexes);
+            getTableViewer().getTable().redraw();
+        });
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/LamiXYChartViewer.java
new file mode 100644 (file)
index 0000000..4a0a776
--- /dev/null
@@ -0,0 +1,595 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Michael Jeanson
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.text.Format;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.ToDoubleFunction;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTimeStampFormat;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.swtchart.Chart;
+import org.swtchart.ITitle;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Abstract XYChart Viewer for LAMI views.
+ *
+ * @author Michael Jeanson
+ *
+ */
+public abstract class LamiXYChartViewer extends TmfViewer implements ILamiViewer {
+
+    /** Ellipsis character */
+    protected static final String ELLIPSIS = "…"; //$NON-NLS-1$
+
+    /**
+     * String representing unknown values. Can be present even in numerical
+     * aspects!
+     */
+    protected static final String UNKNOWN = "?"; //$NON-NLS-1$
+
+    /** Zero value */
+    protected static final double ZERO = 0.0;
+
+    /** Symbol for seconds (used in the custom ns -> s conversion) */
+    private static final String SECONDS_SYMBOL = "s"; //$NON-NLS-1$
+
+    /** Symbol for nanoseconds (used in the custom ns -> s conversion) */
+    private static final String NANOSECONDS_SYMBOL = "ns"; //$NON-NLS-1$
+
+    /**
+     * Function to use to map Strings read from the data table to doubles for
+     * use in SWTChart series.
+     */
+    protected static final ToDoubleFunction<@Nullable String> DOUBLE_MAPPER = str -> {
+        if (str == null || str.equals(UNKNOWN)) {
+            return ZERO;
+        }
+        return Double.parseDouble(str);
+    };
+
+    /**
+     * List of standard colors
+     */
+    protected static final List<@NonNull Color> COLORS = ImmutableList.of(
+                new Color(Display.getDefault(),  72, 120, 207),
+                new Color(Display.getDefault(), 106, 204, 101),
+                new Color(Display.getDefault(), 214,  95,  95),
+                new Color(Display.getDefault(), 180, 124, 199),
+                new Color(Display.getDefault(), 196, 173, 102),
+                new Color(Display.getDefault(), 119, 190, 219)
+                );
+
+    /**
+     * List of "light" colors (when unselected)
+     */
+    protected static final List<@NonNull Color> LIGHT_COLORS = ImmutableList.of(
+                new Color(Display.getDefault(), 173, 195, 233),
+                new Color(Display.getDefault(), 199, 236, 197),
+                new Color(Display.getDefault(), 240, 196, 196),
+                new Color(Display.getDefault(), 231, 213, 237),
+                new Color(Display.getDefault(), 231, 222, 194),
+                new Color(Display.getDefault(), 220, 238, 246)
+                );
+
+    /**
+     * Time stamp formatter for intervals in the days range.
+     */
+    protected static final LamiTimeStampFormat DAYS_FORMATTER = new LamiTimeStampFormat("dd HH:mm"); //$NON-NLS-1$
+
+    /**
+     * Time stamp formatter for intervals in the hours range.
+     */
+    protected static final LamiTimeStampFormat HOURS_FORMATTER = new LamiTimeStampFormat("HH:mm"); //$NON-NLS-1$
+
+    /**
+     * Time stamp formatter for intervals in the minutes range.
+     */
+    protected static final LamiTimeStampFormat MINUTES_FORMATTER = new LamiTimeStampFormat("mm:ss"); //$NON-NLS-1$
+
+    /**
+     * Time stamp formatter for intervals in the seconds range.
+     */
+    protected static final LamiTimeStampFormat SECONDS_FORMATTER = new LamiTimeStampFormat("ss"); //$NON-NLS-1$
+
+    /**
+     * Time stamp formatter for intervals in the milliseconds range.
+     */
+    protected static final LamiTimeStampFormat MILLISECONDS_FORMATTER = new LamiTimeStampFormat("ss.SSS"); //$NON-NLS-1$
+
+    /**
+     * Decimal formatter to display nanoseconds as seconds.
+     */
+    protected static final DecimalUnitFormat NANO_TO_SECS_FORMATTER = new DecimalUnitFormat(0.000000001);
+
+    /**
+     * Default decimal formatter.
+     */
+    protected static final DecimalUnitFormat DECIMAL_FORMATTER = new DecimalUnitFormat();
+
+    private final Listener fResizeListener = event -> {
+        /* Refresh the titles to fit the current chart size */
+        refreshDisplayTitles();
+
+        /* Refresh the Axis labels to fit the current chart size */
+        refreshDisplayLabels();
+    };
+
+    private final LamiResultTable fResultTable;
+    private final LamiChartModel fChartModel;
+
+    private final Chart fChart;
+
+    private final String fChartTitle;
+    private final String fXTitle;
+    private final String fYTitle;
+
+    private boolean fSelected;
+    private Set<Integer> fSelection;
+
+    /**
+     * Creates a Viewer instance based on SWTChart.
+     *
+     * @param parent
+     *            The parent composite to draw in.
+     * @param resultTable
+     *            The result table containing the data from which to build the
+     *            chart
+     * @param chartModel
+     *            The information about the chart to build
+     */
+    public LamiXYChartViewer(Composite parent, LamiResultTable resultTable, LamiChartModel chartModel) {
+        super(parent);
+
+        fParent = parent;
+        fResultTable = resultTable;
+        fChartModel = chartModel;
+        fSelection = new HashSet<>();
+
+        fChart = new Chart(parent, SWT.NONE);
+        fChart.addListener(SWT.Resize, fResizeListener);
+
+        /* Set Chart title */
+        fChartTitle = fResultTable.getTableClass().getTableTitle();
+
+        /* Set X axis title */
+        if (fChartModel.getXSeriesColumns().size() == 1) {
+            /*
+             * There is only 1 series in the chart, we will use its name as the
+             * Y axis (and hide the legend).
+             */
+            String seriesName = getChartModel().getXSeriesColumns().get(0);
+            // The time duration formatter converts ns to s on the axis
+            if (NANOSECONDS_SYMBOL.equals(getXAxisAspects().get(0).getUnits())) {
+                seriesName = getXAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$
+            }
+            fXTitle = seriesName;
+        } else {
+            /*
+             * There are multiple series in the chart, if they all share the same
+             * units, display that.
+             */
+            long nbDiffAspects = getXAxisAspects().stream()
+                .map(aspect -> aspect.getUnits())
+                .distinct()
+                .count();
+
+            String units = getXAxisAspects().get(0).getUnits();
+            if (nbDiffAspects == 1 && units != null) {
+                /* All aspects use the same unit type */
+
+                // The time duration formatter converts ns to s on the axis
+                if (NANOSECONDS_SYMBOL.equals(units)) {
+                    units = SECONDS_SYMBOL;
+                }
+                fXTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$
+            } else {
+                /* Various unit types, just say "Value" */
+                fXTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName);
+            }
+        }
+
+        /* Set Y axis title */
+        if (fChartModel.getYSeriesColumns().size() == 1) {
+            /*
+             * There is only 1 series in the chart, we will use its name as the
+             * Y axis (and hide the legend).
+             */
+            String seriesName = getChartModel().getYSeriesColumns().get(0);
+            // The time duration formatter converts ns to s on the axis
+            if (NANOSECONDS_SYMBOL.equals(getYAxisAspects().get(0).getUnits())) {
+                seriesName = getYAxisAspects().get(0).getName() + " (" + SECONDS_SYMBOL + ')'; //$NON-NLS-1$
+            }
+            fYTitle = seriesName;
+            fChart.getLegend().setVisible(false);
+        } else {
+            /*
+             * There are multiple series in the chart, if they all share the same
+             * units, display that.
+             */
+            long nbDiffAspects = getYAxisAspects().stream()
+                .map(aspect -> aspect.getUnits())
+                .distinct()
+                .count();
+
+            String units = getYAxisAspects().get(0).getUnits();
+            if (nbDiffAspects == 1 && units != null) {
+                /* All aspects use the same unit type */
+
+                // The time duration formatter converts ns to s on the axis
+                if (NANOSECONDS_SYMBOL.equals(units)) {
+                    units = SECONDS_SYMBOL;
+                }
+                fYTitle = Messages.LamiViewer_DefaultValueName + " (" + units + ')'; //$NON-NLS-1$
+            } else {
+                /* Various unit types, just say "Value" */
+                fYTitle = nullToEmptyString(Messages.LamiViewer_DefaultValueName);
+            }
+
+            /* Put legend at the bottom */
+            fChart.getLegend().setPosition(SWT.BOTTOM);
+        }
+
+        /* Set all titles and labels font color to black */
+        fChart.getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+        fChart.getAxisSet().getXAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+        fChart.getAxisSet().getYAxis(0).getTitle().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+        fChart.getAxisSet().getXAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+        fChart.getAxisSet().getYAxis(0).getTick().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+
+        /* Set X label 90 degrees */
+        fChart.getAxisSet().getXAxis(0).getTick().setTickLabelAngle(90);
+
+        /* Refresh the titles to fit the current chart size */
+        refreshDisplayTitles();
+
+        fChart.addDisposeListener(e -> {
+                /* Dispose resources of this class */
+                LamiXYChartViewer.super.dispose();
+        });
+    }
+
+    /**
+     * Util method to check if a list of aspects are all continuous.
+     *
+     * @param axisAspects
+     *            The list of aspects to check.
+     * @return true is all aspects are continuous, otherwise false.
+     */
+    protected static boolean areAspectsContinuous(List<LamiTableEntryAspect> axisAspects) {
+        return axisAspects.stream().allMatch(aspect -> aspect.isContinuous());
+    }
+
+    /**
+     * Util method to check if a list of aspects are all time stamps.
+     *
+     * @param axisAspects
+     *            The list of aspects to check.
+     * @return true is all aspects are time stamps, otherwise false.
+     */
+    protected static boolean areAspectsTimeStamp(List<LamiTableEntryAspect> axisAspects) {
+        return axisAspects.stream().allMatch(aspect -> aspect.isTimeStamp());
+    }
+
+    /**
+     * Util method to check if a list of aspects are all time durations.
+     *
+     * @param axisAspects
+     *            The list of aspects to check.
+     * @return true is all aspects are time durations, otherwise false.
+     */
+    protected static boolean areAspectsTimeDuration(List<LamiTableEntryAspect> axisAspects) {
+        return axisAspects.stream().allMatch(aspect -> aspect.isTimeDuration());
+    }
+
+    /**
+     * Util method that will return a formatter based on the aspects linked to an axis
+     *
+     * If all aspects are time stamps, return a timestamp formatter tuned to the interval.
+     * If all aspects are time durations, return the nanoseconds to seconds formatter.
+     * Otherwise, return the generic decimal formatter.
+     *
+     * @param axisAspects
+     *            The list of aspects of the axis.
+     * @param entries
+     *            The list of entries of the chart.
+     * @return a formatter for the axis.
+     */
+    protected static Format getContinuousAxisFormatter(List<LamiTableEntryAspect> axisAspects, List<LamiTableEntry> entries) {
+
+        if (areAspectsTimeStamp(axisAspects)) {
+            /* Set a TimeStamp formatter depending on the duration between the first and last value */
+            double max = Double.MIN_VALUE;
+            double min = Double.MAX_VALUE;
+
+            for (LamiTableEntry entry : entries) {
+                for (LamiTableEntryAspect aspect : axisAspects) {
+                    Double current = aspect.resolveDouble(entry);
+                    if (current != null) {
+                        max = Math.max(max, current);
+                        min = Math.min(min, current);
+                    }
+                }
+            }
+            long duration = (long) max - (long) min;
+
+            if (duration > TimeUnit.DAYS.toNanos(1)) {
+                return DAYS_FORMATTER;
+            } else if (duration > TimeUnit.HOURS.toNanos(1)) {
+                return HOURS_FORMATTER;
+            } else if (duration > TimeUnit.MINUTES.toNanos(1)) {
+                return MINUTES_FORMATTER;
+            } else if (duration > TimeUnit.SECONDS.toNanos(15)) {
+                return SECONDS_FORMATTER;
+            } else {
+                return MILLISECONDS_FORMATTER;
+            }
+        } else if (areAspectsTimeDuration(axisAspects)) {
+            /* Set the time duration formatter */
+            return NANO_TO_SECS_FORMATTER;
+
+        } else {
+            /* For other numeric aspects, use the default decimal unit formatter */
+            return DECIMAL_FORMATTER;
+        }
+    }
+
+    /**
+     * Get the chart result table.
+     *
+     * @return The chart result table.
+     */
+    protected LamiResultTable getResultTable() {
+        return fResultTable;
+    }
+
+    /**
+     * Get the chart model.
+     *
+     * @return The chart model.
+     */
+    protected LamiChartModel getChartModel() {
+        return fChartModel;
+    }
+
+    /**
+     * Get the chart object.
+     * @return The chart object.
+     */
+    protected Chart getChart() {
+        return fChart;
+    }
+
+    /**
+     * Is a selection made in the chart.
+     *
+     * @return true if there is a selection.
+     */
+    protected boolean isSelected() {
+        return fSelected;
+    }
+
+    /**
+     * Set the selection index.
+     *
+     * @param selection the index to select.
+     */
+    protected void setSelection(Set<Integer> selection) {
+        fSelection = selection;
+        fSelected = !selection.isEmpty();
+    }
+
+    /**
+     * Unset the chart selection.
+     */
+    protected void unsetSelection() {
+        fSelection.clear();
+        fSelected = false;
+    }
+
+    /**
+     * Get the current selection index.
+     *
+     * @return the current selection index.
+     */
+    protected Set<Integer> getSelection() {
+        return fSelection;
+    }
+
+    @Override
+    public @Nullable Control getControl() {
+        return fChart.getParent();
+    }
+
+    @Override
+    public void refresh() {
+        Display.getDefault().asyncExec(() -> {
+            if (!fChart.isDisposed()) {
+                fChart.redraw();
+            }
+        });
+    }
+
+    @Override
+    public void dispose() {
+        fChart.dispose();
+        /* The control's DisposeListener will call super.dispose() */
+    }
+
+    /**
+     * Get a list of all the aspect of the Y axis.
+     *
+     * @return The aspects for the Y axis
+     */
+    protected List<LamiTableEntryAspect> getYAxisAspects() {
+
+        List<LamiTableEntryAspect> yAxisAspects = new ArrayList<>();
+
+        for (String colName : getChartModel().getYSeriesColumns()) {
+            yAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName)));
+        }
+
+        return yAxisAspects;
+    }
+
+    /**
+     * Get a list of all the aspect of the X axis.
+     *
+     * @return The aspects for the X axis
+     */
+    protected List<LamiTableEntryAspect> getXAxisAspects() {
+
+        List<LamiTableEntryAspect> xAxisAspects = new ArrayList<>();
+
+        for (String colName : getChartModel().getXSeriesColumns()) {
+            xAxisAspects.add(checkNotNull(getAspectFromName(getResultTable().getTableClass().getAspects(), colName)));
+        }
+
+        return xAxisAspects;
+    }
+
+    /**
+     * Set the ITitle object text to a substring of canonicalTitle that when
+     * rendered in the chart will fit maxPixelLength.
+     */
+    private void refreshDisplayTitle(ITitle title, String canonicalTitle, int maxPixelLength) {
+        if (title.isVisible()) {
+
+            String newTitle = canonicalTitle;
+
+            /* Get the title font */
+            Font font = title.getFont();
+
+            GC gc = new GC(fParent);
+            gc.setFont(font);
+
+            /* Get the length and height of the canonical title in pixels */
+            Point pixels = gc.stringExtent(canonicalTitle);
+
+            /*
+             * If the title is too long, generate a shortened version based on the
+             * average character width of the current font.
+             */
+            if (pixels.x > maxPixelLength) {
+                int charwidth = gc.getFontMetrics().getAverageCharWidth();
+
+                int minimum = 3;
+
+                int strLen = ((maxPixelLength / charwidth) - minimum);
+
+                if (strLen > minimum) {
+                    newTitle = canonicalTitle.substring(0, strLen) + ELLIPSIS;
+                } else {
+                    newTitle = ELLIPSIS;
+                }
+            }
+
+            title.setText(newTitle);
+
+            // Cleanup
+            gc.dispose();
+        }
+    }
+
+    /**
+     * Refresh the Chart, XAxis and YAxis titles to fit the current
+     * chart size.
+     */
+    private void refreshDisplayTitles() {
+        Rectangle chartRect = fChart.getClientArea();
+        Rectangle plotRect = fChart.getPlotArea().getClientArea();
+
+        ITitle chartTitle = checkNotNull(fChart.getTitle());
+        refreshDisplayTitle(chartTitle, fChartTitle, chartRect.width);
+
+        ITitle xTitle = checkNotNull(fChart.getAxisSet().getXAxis(0).getTitle());
+        refreshDisplayTitle(xTitle, fXTitle, plotRect.width);
+
+        ITitle yTitle = checkNotNull(fChart.getAxisSet().getYAxis(0).getTitle());
+        refreshDisplayTitle(yTitle, fYTitle, plotRect.height);
+    }
+
+    /**
+     * Get the aspect with the given name
+     *
+     * @param aspects
+     *            The list of aspects to search into
+     * @param aspectName
+     *            The name of the aspect we are looking for
+     * @return The corresponding aspect
+     */
+    protected static @Nullable LamiTableEntryAspect getAspectFromName(List<LamiTableEntryAspect> aspects, String aspectName) {
+        for (LamiTableEntryAspect lamiTableEntryAspect : aspects) {
+
+            if (lamiTableEntryAspect.getLabel().equals(aspectName)) {
+                return lamiTableEntryAspect;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Refresh the axis labels to fit the current chart size.
+     */
+    protected abstract void refreshDisplayLabels();
+
+    /**
+     * Redraw the chart.
+     */
+    protected void redraw() {
+        refresh();
+    }
+
+    /**
+     * Signal handler for selection update.
+     *
+     * @param signal
+     *          The selection update signal
+     */
+    @TmfSignalHandler
+    public void updateSelection(LamiSelectionUpdateSignal signal) {
+        if (getResultTable().hashCode() != signal.getSignalHash() || equals(signal.getSource())) {
+            /* The signal is not for us */
+            return;
+        }
+        setSelection(signal.getEntryIndex());
+
+        redraw();
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/Messages.java
new file mode 100644 (file)
index 0000000..3a0e790
--- /dev/null
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiViewer_DefaultValueName;
+
+    public static String LamiScatterViewer_by;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/messages.properties
new file mode 100644 (file)
index 0000000..deaeae3
--- /dev/null
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiViewer_DefaultValueName = Value
+
+LamiScatterViewer_by = by
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/viewers/package-info.java
new file mode 100644 (file)
index 0000000..d349d0a
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiAxisCheckBoxOption.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiAxisCheckBoxOption.java
new file mode 100644 (file)
index 0000000..9beb35d
--- /dev/null
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+
+/**
+ * Basic representation of a check box option for dialog.
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+class LamiAxisCheckBoxOption {
+
+    private final String fName;
+    private final boolean fDefaultValue;
+    private @Nullable Button fButton;
+    private boolean fValue;
+    private final Predicate<LamiTableEntryAspect> fAppliesToAspect;
+
+    /**
+     * Constructor
+     *
+     * @param name
+     *          The name of the check box. The actual string shown to user.
+     * @param defaultValue
+     *          The default value of the check box.
+     * @param validationPredicate
+     *          The predicate to check if an option can be applied to an aspect
+     */
+    public LamiAxisCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> validationPredicate) {
+        fName = name;
+        this.fDefaultValue = defaultValue;
+        this.fValue = defaultValue;
+        fButton = null;
+        fAppliesToAspect = validationPredicate;
+    }
+
+    public String getName() {
+        return fName;
+    }
+
+    public boolean getDefaultValue() {
+        return fDefaultValue;
+    }
+
+    public void setButton(Button button) {
+        fButton = button;
+    }
+
+    public boolean getValue() {
+        return fValue;
+    }
+
+    public void updateValue() {
+        if (fButton != null) {
+            fValue = fButton.getSelection();
+        }
+    }
+
+    public void setButtonEnabled(boolean enabled) {
+        @Nullable Button button = fButton;
+        if (button != null) {
+            /* Only change state when necessary */
+            if (button.getEnabled() != enabled) {
+                button.setEnabled(enabled);
+                button.setSelection(fDefaultValue);
+            }
+        }
+    }
+
+    public boolean getButtonEnabled() {
+        if (fButton != null) {
+            return fButton.getEnabled();
+        }
+        return false;
+    }
+
+    public Predicate<LamiTableEntryAspect> getPredicate() {
+        return fAppliesToAspect;
+    }
+}
\ No newline at end of file
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportView.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportView.java
new file mode 100644 (file)
index 0000000..ad7e68d
--- /dev/null
@@ -0,0 +1,456 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiEmptyAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+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.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ui.views.TmfView;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Base view showing output of Babeltrace scripts.
+ *
+ * Implementations can specify which analysis modules to use, which will define
+ * the scripts and parameters to use accordingly.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class LamiReportView extends TmfView {
+
+    // ------------------------------------------------------------------------
+    // Attributes
+    // ------------------------------------------------------------------------
+
+    /** View ID */
+    public static final String VIEW_ID = "org.eclipse.tracecompass.analysis.lami.views.reportview"; //$NON-NLS-1$
+
+    private final @Nullable LamiResultTable fResultTable;
+
+    private @Nullable LamiViewerControl fTableViewerControl;
+    private final Set<LamiViewerControl> fPredefGraphViewerControls = new LinkedHashSet<>();
+    private final Set<LamiViewerControl> fCustomGraphViewerControls = new LinkedHashSet<>();
+    private @Nullable SashForm fSashForm;
+    private Set<Integer> fSelectionIndexes;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructor
+     */
+    public LamiReportView() {
+        super(VIEW_ID);
+        fResultTable = LamiReportViewFactory.getCurrentResultTable();
+        fSelectionIndexes = new HashSet<>();
+        if (fResultTable != null) {
+            fSelectionIndexes = getIndexOfEntriesIntersectingTimerange(checkNotNull(fResultTable), TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange());
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // ViewPart
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void createPartControl(@Nullable Composite parent) {
+        LamiResultTable resultTable = fResultTable;
+        if (resultTable == null || parent == null) {
+            return;
+        }
+
+        SashForm sf = new SashForm(parent, SWT.NONE);
+        fSashForm = sf;
+        setPartName(resultTable.getTableClass().getTableTitle());
+
+        /* Prepare the table viewer, which is always present */
+        LamiViewerControl tableViewerControl = new LamiViewerControl(sf, resultTable);
+        fTableViewerControl = tableViewerControl;
+
+        /* Prepare the predefined graph viewers, if any */
+        resultTable.getTableClass().getPredefinedViews()
+            .forEach(graphModel -> fPredefGraphViewerControls.add(new LamiViewerControl(sf, resultTable, graphModel)));
+
+        /* Automatically open the table viewer initially */
+        tableViewerControl.getToggleAction().run();
+
+        /* Add toolbar buttons */
+        IToolBarManager toolbarMgr = getViewSite().getActionBars().getToolBarManager();
+        toolbarMgr.add(tableViewerControl.getToggleAction());
+        fPredefGraphViewerControls.stream()
+            .map(LamiViewerControl::getToggleAction)
+            .forEach(toolbarMgr::add);
+
+        IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager();
+        IAction newBarChartAction = new NewChartAction(checkNotNull(parent.getShell()), sf, resultTable, ChartType.BAR_CHART);
+        IAction newXYScatterAction = new NewChartAction(checkNotNull(parent.getShell()), sf, resultTable, ChartType.XY_SCATTER);
+
+        newBarChartAction.setText(Messages.LamiReportView_NewCustomBarChart);
+        newXYScatterAction.setText(Messages.LamiReportView_NewCustomScatterChart);
+
+
+        IAction clearCustomViewsAction = new Action() {
+            @Override
+            public void run() {
+                fCustomGraphViewerControls.forEach(LamiViewerControl::dispose);
+                fCustomGraphViewerControls.clear();
+                sf.layout();
+
+            }
+        };
+        clearCustomViewsAction.setText(Messages.LamiReportView_ClearAllCustomViews);
+
+        menuMgr.add(newBarChartAction);
+        menuMgr.add(newXYScatterAction);
+        menuMgr.add(new Separator());
+        menuMgr.add(clearCustomViewsAction);
+
+        /* Simulate a new external signal to the default viewer */
+        LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportView.this, fSelectionIndexes, checkNotNull(fResultTable).hashCode());
+        TmfSignalManager.dispatchSignal(signal);
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void setFocus() {
+    }
+
+    @Override
+    public void dispose() {
+        super.dispose();
+        if (fSashForm != null) {
+            fSashForm.dispose();
+        }
+        if (fTableViewerControl != null) {
+            fTableViewerControl.dispose();
+        }
+        fPredefGraphViewerControls.forEach(LamiViewerControl::dispose);
+        fCustomGraphViewerControls.forEach(LamiViewerControl::dispose);
+    }
+
+    private class NewChartAction extends Action {
+
+        private final Shell icfDialogParentShell;
+        private final Composite icfChartViewerParent;
+        private final LamiResultTable icfResultTable;
+        private boolean icfXLogScale;
+        private boolean icfYLogScale;
+        private final ChartType icfChartType;
+
+        public NewChartAction(Shell parentShell, Composite chartViewerParent,
+                LamiResultTable resultTable, ChartType chartType) {
+            icfDialogParentShell = parentShell;
+            icfChartViewerParent = chartViewerParent;
+            icfResultTable = resultTable;
+            icfXLogScale = false;
+            icfYLogScale = false;
+            icfChartType = chartType;
+        }
+
+        @Override
+        public void run() {
+            int xLogScaleOptionIndex = -1;
+            int yLogScaleOptionIndex = -1;
+
+            List<LamiTableEntryAspect> xStringColumn = icfResultTable.getTableClass().getAspects().stream()
+                    .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
+                    .collect(Collectors.toList());
+
+            /* Get the flattened aspects for Y since mapping an aggregate aspect to y series make no sense so far */
+            List<LamiTableEntryAspect> yStringColumn = icfResultTable.getTableClass().getAspects().stream()
+                    .filter(aspect -> !(aspect instanceof LamiEmptyAspect))
+                    .collect(Collectors.toList());
+
+            switch (icfChartType) {
+            case BAR_CHART:
+                /* Y value must strictly continous and non timestamp */
+                yStringColumn = yStringColumn.stream()
+                    .filter(aspect -> !aspect.isTimeStamp() && aspect.isContinuous())
+                    .collect(Collectors.toList());
+                break;
+            case PIE_CHART:
+                break;
+            case XY_SCATTER:
+                break;
+            default:
+                break;
+            }
+
+            IStructuredContentProvider contentProvider = checkNotNull(ArrayContentProvider.getInstance());
+
+            LamiSeriesDialog dialog = new LamiSeriesDialog(icfDialogParentShell,
+                    icfChartType,
+                    xStringColumn,
+                    yStringColumn,
+                    contentProvider,
+                    new LabelProvider() {
+                        @Override
+                        public String getText(@Nullable Object element) {
+                            return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
+                        }
+                    },
+                    contentProvider,
+                    new LabelProvider() {
+                        @Override
+                        public String getText(@Nullable Object element) {
+                            return ((LamiTableEntryAspect) checkNotNull(element)).getLabel();
+                        }
+                    });
+            dialog.setTitle(icfChartType.toString() + ' ' + Messages.LamiSeriesDialog_creation);
+
+            /* X options per chart type */
+            switch (icfChartType) {
+            case XY_SCATTER:
+                xLogScaleOptionIndex = dialog.addXCheckBoxOption(
+                        Messages.LamiSeriesDialog_x_axis + ' ' + Messages.LamiReportView_LogScale,
+                        false, new Predicate<LamiTableEntryAspect>() {
+                    @Override
+                    public boolean test(@NonNull LamiTableEntryAspect t) {
+                        return t.isContinuous() && !t.isTimeStamp();
+                    }
+                });
+                break;
+            case BAR_CHART:
+            case PIE_CHART:
+            default:
+                break;
+            }
+
+            /* Y options per chart type */
+            switch (icfChartType) {
+            case BAR_CHART:
+            case XY_SCATTER:
+                yLogScaleOptionIndex = dialog.addYCheckBoxOption(
+                        Messages.LamiSeriesDialog_y_axis + ' ' + Messages.LamiReportView_LogScale,
+                        false, new Predicate<LamiTableEntryAspect>() {
+                    @Override
+                    public boolean test(@NonNull LamiTableEntryAspect t) {
+                        return t.isContinuous() && !t.isTimeStamp();
+                    }
+                });
+                break;
+
+            case PIE_CHART:
+            default:
+                break;
+            }
+
+            if (dialog.open() != Window.OK) {
+                return;
+            }
+
+            List<LamiXYSeriesDescription> results = Arrays.stream(dialog.getResult())
+                    .map(serie -> (LamiXYSeriesDescription) serie)
+                    .collect(Collectors.toList());
+
+            boolean[] xCheckBoxOptionsResults = dialog.getXCheckBoxOptionValues();
+            boolean[] yCheckBoxOptionsResults = dialog.getYCheckBoxOptionValues();
+
+            /* Get X log scale option */
+            if (xLogScaleOptionIndex > -1 && xLogScaleOptionIndex < xCheckBoxOptionsResults.length) {
+                icfXLogScale = xCheckBoxOptionsResults[xLogScaleOptionIndex];
+            }
+            /* Get Y log scale option */
+            if (yLogScaleOptionIndex > -1 && yLogScaleOptionIndex < yCheckBoxOptionsResults.length) {
+                icfYLogScale = yCheckBoxOptionsResults[yLogScaleOptionIndex];
+            }
+
+            List<String> xAxisColString = new ArrayList<>();
+            List<String> yAxisColString = new ArrayList<>();
+
+            /* Specific chart type result fetching */
+            switch (icfChartType) {
+            case PIE_CHART:
+            case BAR_CHART:
+                /* Validate that we only have 1 X aspect */
+                if (results.stream()
+                        .map(element -> element.getXAspect().getLabel())
+                        .distinct()
+                        .count() != 1) {
+                    throw new IllegalStateException("No unique X axis label for results"); //$NON-NLS-1$
+                }
+                xAxisColString = results.stream()
+                        .map(element -> element.getXAspect().getLabel())
+                        .distinct()
+                        .collect(Collectors.toList());
+                break;
+            case XY_SCATTER:
+                xAxisColString = results.stream()
+                        .map(element -> element.getXAspect().getLabel())
+                        .collect(Collectors.toList());
+                break;
+            default:
+                break;
+            }
+
+            yAxisColString = results.stream()
+                    .map(element -> element.getYAspect().getLabel())
+                    .collect(Collectors.toList());
+
+            LamiChartModel model = new LamiChartModel(icfChartType,
+                    nullToEmptyString(Messages.LamiReportView_Custom),
+                    xAxisColString,
+                    yAxisColString,
+                    icfXLogScale,
+                    icfYLogScale);
+
+            LamiViewerControl viewerControl = new LamiViewerControl(icfChartViewerParent, icfResultTable, model);
+            fCustomGraphViewerControls.add(viewerControl);
+            viewerControl.getToggleAction().run();
+
+            /* Signal the current selection to the newly created graph */
+            LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiReportView.this, fSelectionIndexes, checkNotNull(fResultTable).hashCode());
+            TmfSignalManager.dispatchSignal(signal);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Signals
+    // ------------------------------------------------------------------------
+
+    /**
+     * Signal handler for selection update.
+     * Propagate a TmfSelectionRangeUpdatedSignal if possible.
+     *
+     * @param signal
+     *          The selection update signal
+     */
+    @TmfSignalHandler
+    public void updateSelection(LamiSelectionUpdateSignal signal) {
+        LamiResultTable table = fResultTable;
+        if (table == null) {
+            return;
+        }
+
+        if (table.hashCode() != signal.getSignalHash() || equals(signal.getSource())) {
+            /* The signal is not for us */
+            return;
+        }
+
+        Set<Integer> entryIndex = signal.getEntryIndex();
+
+        /*
+         * Since most of the external viewers deal only with continuous time
+         * ranges and do not allow multi-time range selection, simply signal
+         * only when one selection is present.
+         */
+
+        if (entryIndex.isEmpty()) {
+            /*
+             * In an ideal world we would send a null signal to reset all view
+             * and simply show no selection. But since this is Tracecompass
+             * there is no notion of "unselected state" in most of the viewers so
+             * we do not update/clear the last timerange and show false information to the user.
+             */
+            return;
+        }
+
+        if (entryIndex.size() == 1) {
+            int index = Iterables.getOnlyElement(entryIndex).intValue();
+            LamiTimeRange timeRange = table.getEntries().get(index).getCorrespondingTimeRange();
+            if (timeRange != null) {
+                /* Send Range update to other views */
+                ITmfTimestamp start = TmfTimestamp.fromNanos(timeRange.getStart());
+                ITmfTimestamp end = TmfTimestamp.fromNanos(timeRange.getEnd());
+                TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(LamiReportView.this, start, end));
+            }
+        }
+
+        fSelectionIndexes = entryIndex;
+    }
+
+    /**
+     * Signal handler for time range selections
+     *
+     * @param signal
+     *            The received signal
+     */
+    @TmfSignalHandler
+    public void externalUpdateSelection(TmfSelectionRangeUpdatedSignal signal) {
+        LamiResultTable table = fResultTable;
+        if (table == null) {
+            return;
+        }
+
+        if (signal.getSource() == this) {
+            /* We are the source */
+            return;
+        }
+        TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime());
+
+        Set<Integer> selections = getIndexOfEntriesIntersectingTimerange(table, range);
+
+        /* Update all LamiViewer */
+        LamiSelectionUpdateSignal signal1 = new LamiSelectionUpdateSignal(LamiReportView.this, selections, table.hashCode());
+        TmfSignalManager.dispatchSignal(signal1);
+    }
+
+    private static Set<Integer> getIndexOfEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) {
+        Set<Integer> selections = new HashSet<>();
+        for (LamiTableEntry entry : table.getEntries()) {
+            LamiTimeRange timerange = entry.getCorrespondingTimeRange();
+            if (timerange == null) {
+                /* Return since the table have no timerange */
+                return selections;
+            }
+
+            TmfTimeRange tempTimeRange = new TmfTimeRange(TmfTimestamp.fromNanos(timerange.getStart()), TmfTimestamp.fromNanos(timerange.getEnd()));
+            if (tempTimeRange.getIntersection(range) != null) {
+                selections.add(table.getEntries().indexOf(entry));
+            }
+        }
+        return selections;
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewFactory.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiReportViewFactory.java
new file mode 100644 (file)
index 0000000..d768dc8
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysisReport;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Factory to instantiate and display new Lami report views.
+ *
+ * It works by setting a static field, then having the view access it.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class LamiReportViewFactory {
+
+    private LamiReportViewFactory() {
+    }
+
+    private static @Nullable LamiResultTable currentTable;
+    private static int secondaryViewId = 1;
+
+    /**
+     * Return the current result table
+     *
+     * @return The current result table
+     */
+    public static @Nullable LamiResultTable getCurrentResultTable() {
+        return currentTable;
+    }
+
+    /**
+     * Create all the views from a given report
+     *
+     * @param report
+     *            The report to open
+     * @throws PartInitException
+     *             If there was a problem initializing a view
+     */
+    public static synchronized void createNewViews(LamiAnalysisReport report) throws PartInitException {
+        boolean firstView = true;
+
+        for (LamiResultTable table : report.getTables()) {
+            currentTable = table;
+
+            int mode = (firstView ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_VISIBLE);
+
+            final IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+            page.showView(LamiReportView.VIEW_ID, String.valueOf(secondaryViewId), mode);
+            secondaryViewId++;
+
+            currentTable = null;
+            firstView = false;
+        }
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiSeriesDialog.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiSeriesDialog.java
new file mode 100644 (file)
index 0000000..8aebff8
--- /dev/null
@@ -0,0 +1,511 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.IntStream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.ChartType;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
+import org.eclipse.ui.dialogs.SelectionDialog;
+
+/**
+ * Series creation dialog
+ *
+ * @author Jonathan Rajotte-Julien
+ */
+public class LamiSeriesDialog extends SelectionDialog {
+
+    private static final int MINIMUM_COLUMN_WIDTH = 30;
+    private static final int MININAL_SERIES_TABLE_HEIGHT = 150;
+
+    /* The root element to populate the viewer with */
+    private final Object fXInputElement;
+    private final Object fYInputElement;
+    private final List<LamiXYSeriesDescription> series;
+
+    /* Providers for populating dialog */
+    private final ILabelProvider fXLabelProvider;
+    private final IStructuredContentProvider fXContentProvider;
+    private final ILabelProvider fYLabelProvider;
+    private final IStructuredContentProvider fYContentProvider;
+    private final IStructuredContentProvider fSeriesContentProvider;
+
+    private final boolean fRestrictXSeriesNumbers;
+
+    private final List<LamiAxisCheckBoxOption> fXCheckBoxOptions;
+    private final List<LamiAxisCheckBoxOption> fYCheckBoxOptions;
+
+    // the visual selection widget group
+    private TableViewer fXTableViewer;
+    private CheckboxTableViewer fYCheckBoxViewer;
+    private TableViewer fSeriesListViewer;
+
+    private Label fWarning;
+
+    /**
+     * @param parentShell
+     *            The parent shell of the dialog
+     * @param chartType
+     *            The chart type for which the dialog construct series
+     * @param xInput
+     *            The possible X axis set of values
+     * @param yInput
+     *            The possible Y axis set of values
+     * @param xContentProvider
+     *            A content provider for the X axis set
+     * @param xLabelProvider
+     *            The label provider for the X axis set
+     * @param yContentProvider
+     *            The content provider for the Y axis set
+     * @param yLabelProvider
+     *            The label provider for the Y axis set
+     */
+    public LamiSeriesDialog(Shell parentShell, ChartType chartType, Object xInput,
+            Object yInput,
+            IStructuredContentProvider xContentProvider,
+            ILabelProvider xLabelProvider,
+            IStructuredContentProvider yContentProvider,
+            ILabelProvider yLabelProvider) {
+        super(parentShell);
+        fXInputElement = xInput;
+        fYInputElement = yInput;
+        fXContentProvider = xContentProvider;
+        fXLabelProvider = xLabelProvider;
+        fYContentProvider = yContentProvider;
+        fYLabelProvider = yLabelProvider;
+        series = new ArrayList<>();
+        fSeriesContentProvider = checkNotNull(ArrayContentProvider.getInstance());
+
+        fXCheckBoxOptions = new ArrayList<>();
+        fYCheckBoxOptions = new ArrayList<>();
+        fSeriesListViewer = new TableViewer(parentShell);
+        fXTableViewer = new TableViewer(parentShell);
+        fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(parentShell, SWT.NONE));
+
+        /* Dynamic restriction per chart type */
+        switch (chartType) {
+        case XY_SCATTER:
+            fRestrictXSeriesNumbers = false;
+            break;
+        case BAR_CHART:
+        case PIE_CHART:
+        default:
+            fRestrictXSeriesNumbers = true;
+            break;
+        }
+
+        this.fWarning = new Label(parentShell, SWT.NONE);
+    }
+
+    @Override
+    protected Control createDialogArea(@Nullable Composite parent) {
+
+        Composite composite = (Composite) super.createDialogArea(parent);
+        initializeDialogUnits(composite);
+
+        /* Base 3 column grid layout */
+        GridLayout gridLayout = new GridLayout(3, false);
+        composite.setLayout(gridLayout);
+
+        GridData gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 3;
+        Group seriesGroup = new Group(composite, SWT.NONE);
+        seriesGroup.setLayoutData(gridData);
+        seriesGroup.setLayout(new GridLayout(3, false));
+        seriesGroup.setText(Messages.LamiSeriesDialog_series);
+
+        /*
+         * New sub group for the series table.
+         */
+        gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 2;
+        gridData.heightHint = MININAL_SERIES_TABLE_HEIGHT;
+        Group seriesTableGroup = new Group(seriesGroup, SWT.NONE);
+        seriesTableGroup.setLayoutData(gridData);
+        TableColumnLayout layout = new TableColumnLayout();
+        seriesTableGroup.setLayout(layout);
+
+        /* Current series */
+        fSeriesListViewer = new TableViewer(seriesTableGroup, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+        fSeriesListViewer.setContentProvider(fSeriesContentProvider);
+        fSeriesListViewer.setInput(series);
+        fSeriesListViewer.getTable().setHeaderVisible(true);
+        fSeriesListViewer.getTable().setLinesVisible(true);
+        TableViewerColumn column1 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_x_values, element -> element.getXAspect().getLabel());
+        TableViewerColumn column2 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_y_values, element -> element.getYAspect().getLabel());
+        layout.setColumnData(column1.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
+        layout.setColumnData(column2.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
+
+        /* Delete series button */
+        gridData = new GridData(GridData.CENTER);
+        gridData.horizontalSpan = 1;
+        Button deleteSeries = new Button(seriesGroup, SWT.PUSH);
+        deleteSeries.setText(Messages.LamiSeriesDialog_delete);
+        deleteSeries.setLayoutData(gridData);
+        deleteSeries.addSelectionListener(new SelectionListener() {
+            @Override
+            public void widgetSelected(@Nullable SelectionEvent e) {
+                /* Remove the selectecd series */
+                IStructuredSelection selections = (IStructuredSelection) fSeriesListViewer.getSelection();
+                for (Object selection : selections.toList()) {
+                    series.remove(selection);
+                }
+                /* When table is empty reset to initial state */
+                if (series.isEmpty()) {
+                    /* Make sure the OK button is disabled */
+                    getButton(IDialogConstants.OK_ID).setEnabled(false);
+                    /* Hide the selection warning */
+                    fWarning.setVisible(false);
+
+                    /*
+                     * Reset the initial selection of the X axis selection table
+                     */
+                    fXTableViewer.refresh();
+                    /* Reset check boxes options */
+                    fXCheckBoxOptions.forEach(checkBox -> {
+                        checkBox.setButtonEnabled(true);
+                    });
+                    fYCheckBoxOptions.forEach(checkBox -> {
+                        checkBox.setButtonEnabled(true);
+                    });
+                }
+                /* Refresh the series table to show the added series */
+                fSeriesListViewer.refresh();
+            }
+
+            @Override
+            public void widgetDefaultSelected(@Nullable SelectionEvent e) {
+            }
+        });
+
+        /*
+         * Series creator subgroup
+         */
+        gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 3;
+        Group seriesCreatorGroup = new Group(composite, getShellStyle());
+        seriesCreatorGroup.setLayoutData(gridData);
+        seriesCreatorGroup.setLayout(new GridLayout(3, false));
+        seriesCreatorGroup.setText(Messages.LamiSeriesDialog_serie_creator);
+
+        /* X axis sash label */
+        gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
+        gridData.horizontalSpan = 1;
+        Label xSeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
+        xSeriesCreatorLabel.setLayoutData(gridData);
+        xSeriesCreatorLabel.setText(Messages.LamiSeriesDialog_x_axis);
+
+        gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
+        gridData.horizontalSpan = 1;
+        Label ySeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
+        ySeriesCreatorLabel.setLayoutData(gridData);
+        ySeriesCreatorLabel.setText(Messages.LamiSeriesDialog_y_axis);
+
+        /* Empty label for grid layout */
+        gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 1;
+        Label emptyLabel = new Label(seriesCreatorGroup, SWT.CENTER);
+        emptyLabel.setLayoutData(gridData);
+
+        SashForm sash1 = new SashForm(seriesCreatorGroup, SWT.BORDER | SWT.HORIZONTAL);
+        gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 2;
+        sash1.setLayoutData(gridData);
+        sash1.setVisible(true);
+
+        fXTableViewer = new TableViewer(sash1, getTableStyle());
+        fXTableViewer.setContentProvider(fXContentProvider);
+        fXTableViewer.setLabelProvider(fXLabelProvider);
+        fXTableViewer.setInput(fXInputElement);
+
+        fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(sash1, SWT.BORDER));
+        fYCheckBoxViewer.setLabelProvider(fYLabelProvider);
+        fYCheckBoxViewer.setContentProvider(fYContentProvider);
+        fYCheckBoxViewer.setInput(fYInputElement);
+
+        gridData = new GridData(SWT.FILL, SWT.NONE, true, true);
+        gridData.horizontalSpan = 1;
+        Button button1 = new Button(seriesCreatorGroup, SWT.PUSH);
+        button1.setText(Messages.LamiSeriesDialog_add);
+        button1.setLayoutData(gridData);
+        button1.addSelectionListener(new SelectionListener() {
+
+            @Override
+            public void widgetSelected(@Nullable SelectionEvent e) {
+                Object[] ySelections = fYCheckBoxViewer.getCheckedElements();
+                IStructuredSelection xSelections = (IStructuredSelection) fXTableViewer.getSelection();
+                @Nullable Object x = xSelections.getFirstElement();
+                if (!(x instanceof LamiTableEntryAspect) || ySelections.length == 0) {
+                    return;
+                }
+
+                /* Add selection to series if it doesn not already exist in the list */
+                for (Object y : ySelections) {
+                    if(!(y instanceof LamiTableEntryAspect)) {
+                       continue;
+                    }
+                    LamiXYSeriesDescription serie = new LamiXYSeriesDescription((LamiTableEntryAspect) x, ((LamiTableEntryAspect) y));
+                    if (!series.contains(serie)) {
+                        series.add(serie);
+                        fSeriesListViewer.refresh();
+                    }
+                }
+
+                /* Set label warning visible and enable OK button */
+                fWarning.setVisible(true);
+                getButton(IDialogConstants.OK_ID).setEnabled(true);
+
+                /* Update possible X selection based on current series */
+                TableItem[] items = fXTableViewer.getTable().getItems();
+                Arrays.stream(items).forEach(item -> {
+                    LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
+                    if (!aspect.arePropertiesEqual(series.get(0).getXAspect())) {
+                        fXTableViewer.remove(aspect);
+                    }
+                    if (fRestrictXSeriesNumbers && aspect != (series.get(0).getXAspect())) {
+                        fXTableViewer.remove(aspect);
+                    }
+                });
+
+                /*
+                 * Disable all checkBox that do not apply to aspects series.
+                 * Simply take the first one since all series should comply to
+                 * the same restriction
+                 */
+                fXCheckBoxOptions.forEach(checkBox -> {
+                    checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getXAspect()));
+                });
+                fYCheckBoxOptions.forEach(checkBox -> {
+                    checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getYAspect()));
+                });
+            }
+
+            @Override
+            public void widgetDefaultSelected(@Nullable SelectionEvent e) {
+            }
+        });
+
+
+        gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
+        gridData.horizontalSpan = 3;
+        fWarning = new Label(seriesCreatorGroup, SWT.LEFT);
+        fWarning.setLayoutData(gridData);
+        fWarning.setText(Messages.LamiSeriesDialog_selectionRestrictionWarning);
+        fWarning.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
+        fWarning.setVisible(false);
+
+        gridData = new GridData(GridData.FILL_BOTH);
+        gridData.horizontalSpan = 3;
+        Group optionGroups = new Group(composite, getShellStyle());
+        optionGroups.setLayoutData(gridData);
+        optionGroups.setLayout(new GridLayout(3, false));
+        optionGroups.setText(Messages.LamiSeriesDialog_chart_options);
+
+        for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
+            Button button = new Button(optionGroups, SWT.CHECK);
+            button.setSelection(checkBox.getDefaultValue());
+            button.setText(checkBox.getName());
+            checkBox.setButton(button);
+        }
+
+        for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
+            Button button = new Button(optionGroups, SWT.CHECK);
+            button.setSelection(checkBox.getDefaultValue());
+            button.setText(checkBox.getName());
+            checkBox.setButton(button);
+        }
+
+        fYCheckBoxViewer.getTable().addSelectionListener(new SelectionListener() {
+
+            @Override
+            public void widgetSelected(@Nullable SelectionEvent e) {
+                /* On check */
+                if (e != null && e.detail == SWT.CHECK) {
+                    /* Change possible selection */
+                    IStructuredSelection selections = (IStructuredSelection) fYCheckBoxViewer.getSelection();
+                    if (selections.getFirstElement() != null) {
+
+                        boolean checked = fYCheckBoxViewer.getChecked(selections.getFirstElement());
+                        /*
+                         * If just selected look for stuff to disable. If not no
+                         * need to look for stuff to disable since it was
+                         * already done before.
+                         */
+                        if (checked) {
+                            TableItem[] items = fYCheckBoxViewer.getTable().getItems();
+                            Arrays.stream(items).forEach(item -> {
+                                LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
+                                if (!aspect.arePropertiesEqual((LamiTableEntryAspect) checkNotNull(selections.getFirstElement()))) {
+                                    fYCheckBoxViewer.remove(aspect);
+                                }
+                            });
+                        } else if (!checked && fYCheckBoxViewer.getCheckedElements().length == 0 && fSeriesListViewer.getTable().getItemCount() == 0) {
+                            fYCheckBoxViewer.refresh();
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public void widgetDefaultSelected(@Nullable SelectionEvent e) {
+            }
+        });
+
+        Dialog.applyDialogFont(composite);
+        return composite;
+    }
+
+    /*
+     * Disable OK button on dialog creation.
+     */
+    @Override
+    protected void createButtonsForButtonBar(@Nullable Composite parent) {
+        super.createButtonsForButtonBar(parent);
+        getButton(IDialogConstants.OK_ID).setEnabled(false);
+    }
+
+    /**
+     * Return the style flags for the table viewer.
+     *
+     * @return int
+     */
+    protected int getTableStyle() {
+        return SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
+    }
+
+    /**
+     * Add check box option for X series.
+     *
+     * @param name
+     *            The name of the option. The actual text shown to the user.
+     * @param defaultValue
+     *            The default state of the check box option.
+     * @param predicate
+     *            The predicate to check if the option applies to the given
+     *            aspect
+     * @return The index of the option value in the result table.
+     */
+    public int addXCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
+        LamiAxisCheckBoxOption checkBox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
+        fXCheckBoxOptions.add(checkBox);
+        return fXCheckBoxOptions.size() - 1;
+    }
+
+    /**
+     * Add check box option for Y series.
+     *
+     * @param name
+     *            The name of the option. The actual text shown to the user.
+     * @param defaultValue
+     *            The default state of the check box option.
+     * @param predicate
+     *            The predicate to check if the option applies to the given
+     *            aspect
+     * @return The index of the option value in the result table.
+     */
+    public int addYCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
+        LamiAxisCheckBoxOption checkbox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
+        fYCheckBoxOptions.add(checkbox);
+        return fYCheckBoxOptions.size() - 1;
+    }
+
+    /**
+     * @return The final values of X series check boxes.
+     */
+    public boolean[] getXCheckBoxOptionValues() {
+        boolean[] selections = new boolean[fXCheckBoxOptions.size()];
+        if (selections.length != 0) {
+            IntStream.range(0, selections.length).forEach(i -> selections[i] = fXCheckBoxOptions.get(i).getValue());
+        }
+        return selections;
+    }
+
+    /**
+     * @return The final values of Y series check boxes.
+     */
+    public boolean[] getYCheckBoxOptionValues() {
+        boolean[] selections = new boolean[fYCheckBoxOptions.size()];
+        if (selections.length != 0) {
+            IntStream.range(0, selections.length).forEach(i -> selections[i] = fYCheckBoxOptions.get(i).getValue());
+        }
+        return selections;
+    }
+
+    @Override
+    protected void okPressed() {
+        for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
+            checkBox.updateValue();
+        }
+        for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
+            checkBox.updateValue();
+        }
+        super.okPressed();
+    }
+
+    @Override
+    public Object[] getResult() {
+        return series.toArray();
+    }
+
+    private static <T extends Comparable<T>> TableViewerColumn createTableViewerColumn(TableViewer viewer, String name,
+            Function<LamiXYSeriesDescription, T> propertyFunction) {
+        TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.CENTER);
+        viewerColumn.setLabelProvider(new ColumnLabelProvider() {
+            @Override
+            public @Nullable String getText(@Nullable Object element) {
+                if (element != null) {
+                    return propertyFunction.apply((LamiXYSeriesDescription) element).toString();
+                }
+                return null;
+            }
+        });
+
+        TableColumn column = viewerColumn.getColumn();
+        column.setText(name);
+        return viewerColumn;
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiViewerControl.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/LamiViewerControl.java
new file mode 100644 (file)
index 0000000..2ff51df
--- /dev/null
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.internal.analysis.lami.ui.Activator;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers.ILamiViewer;
+
+/**
+ * Control for Lami viewers.
+ *
+ * Since viewers can be disposed, the "viewer control" will remain and be ready
+ * to re-instantiate the viewer if required to.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class LamiViewerControl {
+
+    private final Action fToggleAction;
+
+    private @Nullable ILamiViewer fViewer;
+
+    /**
+     * Build a new control for a Lami table viewer.
+     *
+     * @param parent
+     *            The parent composite
+     * @param table
+     *            The results table populating the table viewer
+     */
+    public LamiViewerControl(Composite parent, LamiResultTable table) {
+        fToggleAction = new Action() {
+            @Override
+            public void run() {
+                ILamiViewer viewer = fViewer;
+                if (viewer == null) {
+                    fViewer = ILamiViewer.createLamiTable(parent, table);
+                } else {
+                    viewer.dispose();
+                    fViewer = null;
+                }
+                parent.layout();
+            }
+        };
+        fToggleAction.setText(Messages.LamiReportView_ActivateTableAction_ButtonName);
+        fToggleAction.setToolTipText(Messages.LamiReportView_ActivateTableAction_ButtonTooltip);
+        fToggleAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath("icons/table.gif")); //$NON-NLS-1$
+    }
+
+    /**
+     * Build a new control for a graph viewer.
+     *
+     * @param parent
+     *            The parent composite
+     * @param table
+     *            The table containing the source data
+     * @param graphModel
+     *            The graph model
+     */
+    public LamiViewerControl(Composite parent, LamiResultTable table, LamiChartModel graphModel) {
+        fToggleAction = new Action() {
+            @Override
+            public void run() {
+                ILamiViewer viewer = fViewer;
+                if (viewer == null) {
+                    fViewer = ILamiViewer.createLamiChart(parent, table, graphModel);
+                } else {
+                    viewer.dispose();
+                    fViewer = null;
+                }
+                parent.layout();
+            }
+        };
+        fToggleAction.setText(Messages.LamiReportView_ToggleAction_ButtonNamePrefix + ' ' + graphModel.getName());
+        fToggleAction.setToolTipText(Messages.LamiReportView_ToggleAction_ButtonTooltip);
+        fToggleAction.setImageDescriptor(getIconForGraphType(graphModel.getChartType()));
+    }
+
+    /**
+     * Get the viewer of this control. Returns null if the viewer is current
+     * disposed.
+     *
+     * @return The viewer
+     */
+    public @Nullable ILamiViewer getViewer() {
+        return fViewer;
+    }
+
+    /**
+     * Get the toggle action that shows/hide this control's viewer.
+     *
+     * @return The toggle action
+     */
+    public Action getToggleAction() {
+        return fToggleAction;
+    }
+
+    /**
+     * Explicitly dispose this control's viewer.
+     */
+    public void dispose() {
+        if (fViewer != null) {
+            fViewer.dispose();
+        }
+    }
+
+    private static @Nullable ImageDescriptor getIconForGraphType(LamiChartModel.ChartType graphType) {
+        switch (graphType) {
+        case BAR_CHART:
+            return Activator.getDefault().getImageDescripterFromPath("icons/histogram.gif"); //$NON-NLS-1$
+        case PIE_CHART:
+        case XY_SCATTER:
+        default:
+            // FIXME Use other icons
+            return Activator.getDefault().getImageDescripterFromPath("icons/histogram.gif"); //$NON-NLS-1$
+        }
+    }
+
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/Messages.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/Messages.java
new file mode 100644 (file)
index 0000000..f6cb479
--- /dev/null
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String LamiReportView_ActivateTableAction_ButtonName;
+    public static String LamiReportView_ActivateTableAction_ButtonTooltip;
+
+    public static String LamiReportView_ToggleAction_ButtonNamePrefix;
+    public static String LamiReportView_ToggleAction_ButtonTooltip;
+
+    public static String LamiReportView_NewCustomBarChart;
+    public static String LamiReportView_NewCustomScatterChart;
+    public static String LamiReportView_ClearAllCustomViews;
+    public static String LamiReportView_LogScale;
+    public static String LamiReportView_SelectColumnForX;
+    public static String LamiReportView_SelectColumnsForCategories;
+    public static String LamiReportView_SelectColumnsForSeries;
+    public static String LamiReportView_Custom;
+
+    public static String LamiSeriesDialog_creation;
+    public static String LamiSeriesDialog_add;
+    public static String LamiSeriesDialog_chart_options;
+    public static String LamiSeriesDialog_delete;
+    public static String LamiSeriesDialog_selectionRestrictionWarning;
+    public static String LamiSeriesDialog_serie_creator;
+    public static String LamiSeriesDialog_series;
+    public static String LamiSeriesDialog_x_axis;
+    public static String LamiSeriesDialog_x_values;
+    public static String LamiSeriesDialog_y_axis;
+    public static String LamiSeriesDialog_y_values;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/messages.properties b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/messages.properties
new file mode 100644 (file)
index 0000000..bd16a40
--- /dev/null
@@ -0,0 +1,35 @@
+###############################################################################
+# Copyright (c) 2015, 2016 EfficiOS Inc. and others
+#
+# 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
+###############################################################################
+
+LamiReportView_ActivateTableAction_ButtonName = Toggle Table
+LamiReportView_ActivateTableAction_ButtonTooltip = Toggle the Table view of the results
+
+LamiReportView_ToggleAction_ButtonNamePrefix = Toggle
+LamiReportView_ToggleAction_ButtonTooltip = Toggle showing this graph in the view
+
+LamiReportView_NewCustomBarChart = New custom bar chart
+LamiReportView_NewCustomScatterChart = New custom scatter chart
+LamiReportView_ClearAllCustomViews = Clear all custom views
+LamiReportView_LogScale = Log scale
+LamiReportView_SelectColumnForX = Select the column used for the X axis
+LamiReportView_SelectColumnsForCategories = Select the columns used for the categories
+LamiReportView_SelectColumnsForSeries = Select the columns used for the series
+LamiReportView_Custom = Custom
+
+LamiSeriesDialog_creation = chart series creation
+LamiSeriesDialog_add = Add
+LamiSeriesDialog_chart_options = Chart options
+LamiSeriesDialog_delete = Delete
+LamiSeriesDialog_selectionRestrictionWarning = Note: Selection might be restricted based on type checking of previously selected series
+LamiSeriesDialog_serie_creator = Series creator
+LamiSeriesDialog_series = Series
+LamiSeriesDialog_x_axis = X axis
+LamiSeriesDialog_x_values = X-Values
+LamiSeriesDialog_y_axis = Y axis
+LamiSeriesDialog_y_values = Y-Values
diff --git a/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/package-info.java b/analysis/org.eclipse.tracecompass.analysis.lami.ui/src/org/eclipse/tracecompass/internal/provisional/analysis/lami/ui/views/package-info.java
new file mode 100644 (file)
index 0000000..0dc90ae
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 EfficiOS Inc. and others
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
This page took 0.122485 seconds and 5 git commands to generate.