tmf: Add TmfEventTableColumn class
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / viewers / events / TmfEventsTable.java
index 1cd957c54fee85877fee026a151b44403141b74d..d54dc4a45a97a02ec905feb30ecea59dfc7222f4 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2013 Ericsson
+ * Copyright (c) 2010, 2014 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -7,22 +7,22 @@
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *   Francois Chouinard - Initial API and implementation
- *   Patrick Tasse - Factored out from events view
- *   Francois Chouinard - Replaced Table by TmfVirtualTable
- *   Patrick Tasse - Filter implementation (inspired by www.eclipse.org/mat)
+ *   Francois Chouinard - Initial API and implementation, replaced Table by TmfVirtualTable
+ *   Patrick Tasse - Factored out from events view,
+ *                   Filter implementation (inspired by www.eclipse.org/mat)
  *   Ansgar Radermacher - Support navigation to model URIs (Bug 396956)
  *   Bernd Hufmann - Updated call site and model URI implementation
+ *   Alexandre Montplaisir - Update to new column API
  *******************************************************************************/
 
 package org.eclipse.linuxtools.tmf.ui.viewers.events;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -49,6 +49,7 @@ import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuListener;
@@ -78,8 +79,6 @@ import org.eclipse.linuxtools.internal.tmf.ui.dialogs.MultiLineInputDialog;
 import org.eclipse.linuxtools.tmf.core.component.ITmfEventProvider;
 import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
-import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
-import org.eclipse.linuxtools.tmf.core.event.TmfEventField;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfCallsite;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfModelLookup;
 import org.eclipse.linuxtools.tmf.core.event.lookup.ITmfSourceLookup;
@@ -102,12 +101,13 @@ import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
 import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsCache.CachedEvent;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
+import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableFieldColumn;
 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSetting;
 import org.eclipse.linuxtools.tmf.ui.views.colors.ColorSettingsManager;
 import org.eclipse.linuxtools.tmf.ui.views.colors.IColorSettingsListener;
 import org.eclipse.linuxtools.tmf.ui.views.filter.FilterManager;
 import org.eclipse.linuxtools.tmf.ui.widgets.rawviewer.TmfRawEventViewer;
-import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData;
 import org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.SashForm;
@@ -148,18 +148,34 @@ import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.ide.IGotoMarker;
 import org.eclipse.ui.themes.ColorUtil;
 
+import com.google.common.base.Joiner;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+
 /**
  * The generic TMF Events table
  *
  * This is a view that will list events that are read from a trace.
  *
- * @version 1.0
  * @author Francois Chouinard
  * @author Patrick Tasse
  * @since 2.0
  */
 public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorSettingsListener, ISelectionProvider {
 
+    /**
+     * Empty string array, used by {@link #getItemStrings}.
+     * @since 3.0
+     */
+    protected static final @NonNull String[] EMPTY_STRING_ARRAY = new String[0];
+
+    /**
+     * Empty string
+     * @since 3.1
+     */
+    protected static final @NonNull String EMPTY_STRING = ""; //$NON-NLS-1$
+
     private static final Image BOOKMARK_IMAGE = Activator.getDefault().getImageFromPath(
             "icons/elcl16/bookmark_obj.gif"); //$NON-NLS-1$
     private static final Image SEARCH_IMAGE = Activator.getDefault().getImageFromPath("icons/elcl16/search.gif"); //$NON-NLS-1$
@@ -173,11 +189,18 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private static final String SEARCH_HINT = Messages.TmfEventsTable_SearchHint;
     private static final String FILTER_HINT = Messages.TmfEventsTable_FilterHint;
     private static final int MAX_CACHE_SIZE = 1000;
+
     /**
-     * Empty ITmfEventField array, used by {@link #extractItemFields(ITmfEvent)}
-     * @since 2.2
+     * Default set of columns to use for trace types that do not specify
+     * anything
      */
-    public static final ITmfEventField[] EMPTY_FIELD_ARRAY = new TmfEventField[0];
+    private static final Collection<TmfEventTableColumn> DEFAULT_COLUMNS = ImmutableList.of(
+            TmfEventTableColumn.BaseColumns.TIMESTAMP,
+            TmfEventTableColumn.BaseColumns.SOURCE,
+            TmfEventTableColumn.BaseColumns.EVENT_TYPE,
+            TmfEventTableColumn.BaseColumns.REFERENCE,
+            TmfEventTableColumn.BaseColumns.CONTENTS
+            );
 
     /**
      * The events table search/filter keys
@@ -264,7 +287,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private ListenerList selectionChangedListeners = new ListenerList();
 
     // Bookmark map <Rank, MarkerId>
-    private Map<Long, Long> fBookmarksMap = new HashMap<Long, Long>();
+    private Multimap<Long, Long> fBookmarksMap = HashMultimap.create();
     private IFile fBookmarksFile;
     private long fPendingGotoRank = -1;
 
@@ -274,14 +297,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     private Color fGreenColor;
     private Font fBoldFont;
 
-    // Table column names
-    private static final String[] COLUMN_NAMES = new String[] { Messages.TmfEventsTable_TimestampColumnHeader,
-        Messages.TmfEventsTable_SourceColumnHeader, Messages.TmfEventsTable_TypeColumnHeader,
-        Messages.TmfEventsTable_ReferenceColumnHeader, Messages.TmfEventsTable_ContentColumnHeader };
-
-    private static final ColumnData[] COLUMN_DATA = new ColumnData[] { new ColumnData(COLUMN_NAMES[0], 100, SWT.LEFT),
-        new ColumnData(COLUMN_NAMES[1], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[2], 100, SWT.LEFT),
-        new ColumnData(COLUMN_NAMES[3], 100, SWT.LEFT), new ColumnData(COLUMN_NAMES[4], 100, SWT.LEFT) };
+    private final List<TmfEventTableColumn> fColumns = new LinkedList<>();
 
     // Event cache
     private final TmfEventsCache fCache;
@@ -297,7 +313,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     // ------------------------------------------------------------------------
 
     /**
-     * Basic constructor, will use default column data.
+     * Basic constructor, using the default set of columns
      *
      * @param parent
      *            The parent composite UI object
@@ -305,20 +321,63 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            The size of the event table cache
      */
     public TmfEventsTable(final Composite parent, final int cacheSize) {
-        this(parent, cacheSize, COLUMN_DATA);
+        this(parent, cacheSize, DEFAULT_COLUMNS);
     }
 
     /**
-     * Advanced constructor, where we also define which column data to use.
+     * Legacy constructor, using ColumnData to define columns
      *
      * @param parent
      *            The parent composite UI object
      * @param cacheSize
      *            The size of the event table cache
      * @param columnData
-     *            The column data to use for this table
+     *            Unused
+     * @deprecated Deprecated constructor, use
+     *             {@link #TmfEventsTable(Composite, int, Collection)}
      */
-    public TmfEventsTable(final Composite parent, int cacheSize, final ColumnData[] columnData) {
+    @Deprecated
+    public TmfEventsTable(final Composite parent, int cacheSize,
+            final org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
+        /*
+         * We'll do a "best-effort" to keep trace types still using this API to
+         * keep working, by defining a TmfEventTableFieldColumn for each
+         * ColumnData they passed.
+         */
+        this(parent, cacheSize, convertFromColumnData(columnData));
+    }
+
+    @Deprecated
+    private static Collection<TmfEventTableColumn> convertFromColumnData(
+            org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData[] columnData) {
+
+        ImmutableList.Builder<TmfEventTableColumn> builder = new ImmutableList.Builder<>();
+        for (org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData col : columnData) {
+            String header = col.header;
+            if (header != null) {
+                builder.add(new TmfEventTableFieldColumn(header));
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Standard constructor, where we define which columns to use.
+     *
+     * @param parent
+     *            The parent composite UI object
+     * @param cacheSize
+     *            The size of the event table cache
+     * @param columns
+     *            The columns to use in this table.
+     *            <p>
+     *            The iteration order of this collection will correspond to the
+     *            initial ordering of this series of columns in the table.
+     *            </p>
+     * @since 3.1
+     */
+    public TmfEventsTable(final Composite parent, int cacheSize,
+            Collection<? extends TmfEventTableColumn> columns) {
         super("TmfEventsTable"); //$NON-NLS-1$
 
         fComposite = new Composite(parent, SWT.NONE);
@@ -343,18 +402,14 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         fTable.setHeaderVisible(true);
         fTable.setLinesVisible(true);
 
-        // Set the columns
-        setColumnHeaders(columnData);
-
-        // Set the default column field ids if this is not a subclass
-        if (Arrays.equals(columnData, COLUMN_DATA)) {
-            fTable.getColumns()[0].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TIMESTAMP);
-            fTable.getColumns()[1].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_SOURCE);
-            fTable.getColumns()[2].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_TYPE);
-            fTable.getColumns()[3].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_REFERENCE);
-            fTable.getColumns()[4].setData(Key.FIELD_ID, ITmfEvent.EVENT_FIELD_CONTENT);
+        // Setup the columns
+        if (columns != null) {
+            fColumns.addAll(columns);
         }
 
+        // Create the UI columns in the table
+        fTable.createColumns(fColumns);
+
         // Set the frozen row for header row
         fTable.setFrozenRowCount(1);
 
@@ -585,6 +640,10 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         createPopupMenu();
     }
 
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
     /**
      * Create a pop-up menu.
      */
@@ -645,7 +704,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                     try {
                         String fileName = cs.getFileName();
                         final String trimmedPath = fileName.replaceAll("\\.\\./", ""); //$NON-NLS-1$ //$NON-NLS-2$
-                        final ArrayList<IFile> files = new ArrayList<IFile>();
+                        final ArrayList<IFile> files = new ArrayList<>();
                         ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
                             @Override
                             public boolean visit(IResource resource) throws CoreException {
@@ -751,7 +810,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                 IHandlerService handlerService = (IHandlerService) activePage.getActiveEditor().getSite().getService(IHandlerService.class);
                 ICommandService cmdService = (ICommandService) activePage.getActiveEditor().getSite().getService(ICommandService.class);
                 try {
-                    HashMap<String, Object> parameters = new HashMap<String, Object>();
+                    HashMap<String, Object> parameters = new HashMap<>();
                     StringBuilder header = new StringBuilder();
                     boolean needTab = false;
                     for (TableColumn tc: fTable.getColumns()) {
@@ -803,9 +862,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         };
 
         class ToggleBookmarkAction extends Action {
-            long fRank;
+            Long fRank;
 
-            public ToggleBookmarkAction(final String text, final long rank) {
+            public ToggleBookmarkAction(final String text, final Long rank) {
                 super(text);
                 fRank = rank;
             }
@@ -965,6 +1024,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
             fTrace.dispose();
         }
         fResourceManager.dispose();
+        fRawViewer.dispose();
         super.dispose();
     }
 
@@ -989,11 +1049,13 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
 
     /**
      * @param columnData
-     *
-     * FIXME: Add support for column selection
+     *            columnData
+     * @deprecated The column headers are now set at the constructor, this
+     *             shouldn't be called anymore.
      */
-    protected void setColumnHeaders(final ColumnData[] columnData) {
-        fTable.setColumnHeaders(columnData);
+    @Deprecated
+    protected void setColumnHeaders(final org.eclipse.linuxtools.tmf.ui.widgets.virtualtable.ColumnData [] columnData) {
+        /* No-op */
     }
 
     /**
@@ -1007,26 +1069,27 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            Which rank this event has in the trace/experiment
      */
     protected void setItemData(final TableItem item, final ITmfEvent event, final long rank) {
-        final ITmfEventField[] fields = extractItemFields(event);
-        final String[] content = new String[fields.length];
-        for (int i = 0; i < fields.length; i++) {
-            content[i] = fields[i].getValue() != null ? fields[i].getValue().toString() : ""; //$NON-NLS-1$
-        }
-        item.setText(content);
+        item.setText(getItemStrings(fColumns, event));
         item.setData(event);
         item.setData(Key.TIMESTAMP, new TmfTimestamp(event.getTimestamp()));
         item.setData(Key.RANK, rank);
 
-        boolean bookmark = false;
-        final Long markerId = fBookmarksMap.get(rank);
-        if (markerId != null) {
-            bookmark = true;
+        final Collection<Long> markerIds = fBookmarksMap.get(rank);
+        if (!markerIds.isEmpty()) {
+            Joiner joiner = Joiner.on("\n -").skipNulls(); //$NON-NLS-1$
+            List<Object> parts = new ArrayList<>();
+            if (markerIds.size() > 1) {
+                parts.add(Messages.TmfEventsTable_MultipleBookmarksToolTip);
+            }
             try {
-                final IMarker marker = fBookmarksFile.findMarker(markerId);
-                item.setData(Key.BOOKMARK, marker.getAttribute(IMarker.MESSAGE));
-            } catch (final CoreException e) {
+                for (long markerId : markerIds) {
+                    final IMarker marker = fBookmarksFile.findMarker(markerId);
+                    parts.add(marker.getAttribute(IMarker.MESSAGE));
+                }
+            } catch (CoreException e) {
                 displayException(e);
             }
+            item.setData(Key.BOOKMARK, joiner.join(parts));
         } else {
             item.setData(Key.BOOKMARK, null);
         }
@@ -1052,12 +1115,12 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         }
 
         if (searchMatch) {
-            if (bookmark) {
+            if (!markerIds.isEmpty()) {
                 item.setImage(SEARCH_MATCH_BOOKMARK_IMAGE);
             } else {
                 item.setImage(SEARCH_MATCH_IMAGE);
             }
-        } else if (bookmark) {
+        } else if (!markerIds.isEmpty()) {
             item.setImage(BOOKMARK_IMAGE);
         } else {
             item.setImage((Image) null);
@@ -1209,6 +1272,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                             if (e.character == SWT.CR) {
                                 updateHeader(newEditor.getText());
                                 applyHeader();
+
+                                // Set focus on the table so that the next carriage return goes to the next result
+                                TmfEventsTable.this.getTable().setFocus();
                             } else if (e.character == SWT.ESC) {
                                 tableEditor.getEditor().dispose();
                             }
@@ -1850,43 +1916,42 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
     }
 
     /**
-     * Extract the fields of an event (item in the table).
+     * Get the array of item strings (e.g., what to display in each cell of the
+     * table row) corresponding to the columns and trace event passed in
+     * parameter. The order of the Strings in the returned array will correspond
+     * to the iteration order of 'columns'.
      *
-     * @param event
-     *            The event to extract from
-     * @return The array of fields
-     * @since 2.2
+     * <p>
+     * To ensure consistent results, make sure only call this within a scope
+     * synchronized on 'columns'! If the order of 'columns' changes right after
+     * this method is called, the returned value won't be ordered correctly
+     * anymore.
      */
-    public final ITmfEventField[] getItemFields(final ITmfEvent event) {
-        return extractItemFields(event);
+    private static String[] getItemStrings(List<TmfEventTableColumn> columns, ITmfEvent event) {
+        if (event == null) {
+            return EMPTY_STRING_ARRAY;
+        }
+        synchronized (columns) {
+            List<String> itemStrings = new ArrayList<>(columns.size());
+            for (TmfEventTableColumn column : columns) {
+                itemStrings.add(column.getItemString(event));
+            }
+            return itemStrings.toArray(new String[0]);
+        }
     }
 
     /**
-     * Extract the fields of an event (item in the table).
+     * Get the contents of the row in the events table corresponding to an
+     * event. The order of the elements corresponds to the current order of the
+     * columns.
      *
      * @param event
-     *            The event to extract from
-     * @return The array of fields
-     *
-     *         FIXME: Add support for column selection
+     *            The event printed in this row
+     * @return The event row entries
+     * @since 3.0
      */
-    protected ITmfEventField[] extractItemFields(final ITmfEvent event) {
-        ITmfEventField[] fields = EMPTY_FIELD_ARRAY;
-        if (event != null) {
-            final String timestamp = event.getTimestamp().toString();
-            final String source = event.getSource();
-            final String type = event.getType().getName();
-            final String reference = event.getReference();
-            final String content = event.getContent().toString();
-            fields = new TmfEventField[] {
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TIMESTAMP, timestamp, null),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_SOURCE, source, null),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_TYPE, type, null),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_REFERENCE, reference, null),
-                    new TmfEventField(ITmfEvent.EVENT_FIELD_CONTENT, content, null)
-            };
-        }
-        return fields;
+    public String[] getItemStrings(ITmfEvent event) {
+        return getItemStrings(fColumns, event);
     }
 
     /**
@@ -2033,7 +2098,7 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
         if (fTable == null || fTable.isDisposed()) {
             return StructuredSelection.EMPTY;
         }
-        List<Object> list = new ArrayList<Object>(fTable.getSelection().length);
+        List<Object> list = new ArrayList<>(fTable.getSelection().length);
         for (TableItem item : fTable.getSelection()) {
             if (item.getData() != null) {
                 list.add(item.getData());
@@ -2114,9 +2179,9 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
                         final IMarker bookmark = bookmarksFile.createMarker(IMarker.BOOKMARK);
                         if (bookmark.exists()) {
                             bookmark.setAttribute(IMarker.MESSAGE, message.toString());
-                            final long rank = (Long) tableItem.getData(Key.RANK);
-                            final int location = (int) rank;
-                            bookmark.setAttribute(IMarker.LOCATION, (Integer) location);
+                            final Long rank = (Long) tableItem.getData(Key.RANK);
+                            final int location = rank.intValue();
+                            bookmark.setAttribute(IMarker.LOCATION, Integer.valueOf(location));
                             fBookmarksMap.put(rank, bookmark.getId());
                             fTable.refresh();
                         }
@@ -2136,26 +2201,28 @@ public class TmfEventsTable extends TmfComponent implements IGotoMarker, IColorS
      *            The bookmark to remove
      */
     public void removeBookmark(final IMarker bookmark) {
-        for (final Entry<Long, Long> entry : fBookmarksMap.entrySet()) {
+        for (final Entry<Long, Long> entry : fBookmarksMap.entries()) {
             if (entry.getValue().equals(bookmark.getId())) {
-                fBookmarksMap.remove(entry.getKey());
+                fBookmarksMap.remove(entry.getKey(), entry.getValue());
                 fTable.refresh();
                 return;
             }
         }
     }
 
-    private void toggleBookmark(final long rank) {
+    private void toggleBookmark(final Long rank) {
         if (fBookmarksFile == null) {
             return;
         }
         if (fBookmarksMap.containsKey(rank)) {
-            final Long markerId = fBookmarksMap.remove(rank);
+            final Collection<Long> markerIds = fBookmarksMap.removeAll(rank);
             fTable.refresh();
             try {
-                final IMarker bookmark = fBookmarksFile.findMarker(markerId);
-                if (bookmark != null) {
-                    bookmark.delete();
+                for (long markerId : markerIds) {
+                    final IMarker bookmark = fBookmarksFile.findMarker(markerId);
+                    if (bookmark != null) {
+                        bookmark.delete();
+                    }
                 }
             } catch (final CoreException e) {
                 displayException(e);
This page took 0.038045 seconds and 5 git commands to generate.