tmf.core: Allow merging annotations with same category names

Allow experiements to work with annotatinos.

[Fixed] Annotations in experiements

Change-Id: Ic87004592bb7c968747c408d0dfe01c284368c45
Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/178026
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/model/tree/TmfTreeDataModelTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/model/tree/TmfTreeDataModelTest.java
index e50979d..1d31e36 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/model/tree/TmfTreeDataModelTest.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/model/tree/TmfTreeDataModelTest.java
@@ -12,14 +12,35 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.Annotation;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.AnnotationCategoriesModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.AnnotationModel;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.IAnnotation.AnnotationType;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.IOutputAnnotationProvider;
+import org.eclipse.tracecompass.internal.tmf.core.model.tree.TmfTreeCompositeDataProvider;
 import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -185,6 +206,37 @@
         assertEquals(TO_STRING, "<name=[Name] id=0 parentId=-1 style=null hasRowModel=true>", model2.toString());
     }
 
+    /**
+     * Test {@link TmfTreeCompositeDataProvider}
+     */
+    @Test
+    public void testCompositeTree() {
+        List<DummyDataProvider> ddps = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            ddps.add(new DummyDataProvider(i));
+        }
+        TmfTreeCompositeDataProvider<@NonNull TmfTreeDataModel, @NonNull DummyDataProvider> composite = new TmfTreeCompositeDataProvider<>(ddps, "composite-dummy");
+        assertNotNull(composite);
+        NullProgressMonitor monitor = new NullProgressMonitor();
+        TmfModelResponse<@NonNull TmfTreeModel<@NonNull TmfTreeDataModel>> tree = composite.fetchTree(Collections.emptyMap(), monitor);
+        TmfTreeModel<@NonNull TmfTreeDataModel> model = tree.getModel();
+        assertNotNull(model);
+        assertEquals(Arrays.asList("header"), model.getHeaders());
+        assertEquals(2, model.getEntries().size());
+        // AnnotationCategories
+        TmfModelResponse<@NonNull AnnotationCategoriesModel> returnVal = composite.fetchAnnotationCategories(Collections.emptyMap(), monitor);
+        AnnotationCategoriesModel categoryModel = returnVal.getModel();
+        assertNotNull(categoryModel);
+        assertEquals(Arrays.asList("0","1","common"), categoryModel.getAnnotationCategories());
+        // Annotations
+        TmfModelResponse<@NonNull AnnotationModel> annotations = composite.fetchAnnotations(Collections.emptyMap(), monitor);
+        AnnotationModel annotationsModel = annotations.getModel();
+        assertNotNull(annotationsModel);
+        Collection<@NonNull Annotation> collection = annotationsModel.getAnnotations().get("test");
+        assertNotNull(collection);
+        assertEquals(4, collection.size());
+    }
+
     // ------------------------------------------------------------------------
     // Helpers
     // ------------------------------------------------------------------------
@@ -201,4 +253,40 @@
             return new TmfTreeDataModel(ID0, PARENT_ID0, LABELS0);
         }
     }
+
+    @NonNullByDefault
+    private static class DummyDataProvider implements ITmfTreeDataProvider<@NonNull TmfTreeDataModel>, IOutputAnnotationProvider {
+        private final int fModelNo;
+
+        public DummyDataProvider(int modelNo) {
+            fModelNo = modelNo;
+        }
+
+        @Override
+        public TmfModelResponse<TmfTreeModel<TmfTreeDataModel>> fetchTree(@NonNull Map<@NonNull String, @NonNull Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+            TmfTreeDataModel createModel = createModel(fModelNo);
+            TmfModelResponse<TmfTreeModel<TmfTreeDataModel>> response = new TmfModelResponse<>(new TmfTreeModel<>(Arrays.asList("header"), Arrays.asList(createModel)), ITmfResponse.Status.COMPLETED, "");
+            return response;
+        }
+
+        @Override
+        public @NonNull String getId() {
+            return "dummy";
+        }
+
+        @Override
+        public TmfModelResponse<AnnotationCategoriesModel> fetchAnnotationCategories(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+            AnnotationCategoriesModel model = new AnnotationCategoriesModel(Arrays.asList("common", Integer.toString(fModelNo)));
+            return new TmfModelResponse<AnnotationCategoriesModel>(model, Status.COMPLETED, "");
+        }
+
+        @Override
+        public TmfModelResponse<AnnotationModel> fetchAnnotations(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+            Map<String, Collection<Annotation>> annotations = new HashMap<>();
+            annotations.put("test", Arrays.asList(new Annotation(0, 1, 2, AnnotationType.CHART, "Hi" + fModelNo, new OutputElementStyle("black")), new Annotation(0, 1, 2, AnnotationType.CHART, "Hello", new OutputElementStyle("pink"))));
+            annotations.put("test" + fModelNo, Arrays.asList(new Annotation(0, 1, 2, AnnotationType.CHART, "bye", new OutputElementStyle("white"))));
+            AnnotationModel model = new AnnotationModel(annotations);
+            return new TmfModelResponse<AnnotationModel>(model, Status.COMPLETED, "");
+        }
+    }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/TmfTreeCompositeDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/TmfTreeCompositeDataProvider.java
index d31490f..7ee35ee 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/TmfTreeCompositeDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/TmfTreeCompositeDataProvider.java
@@ -15,8 +15,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -38,7 +40,6 @@
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 /**
  * Represents a base implementation of {@link ITmfTreeDataProvider} that
@@ -200,7 +201,7 @@
     @Override
     public TmfModelResponse<AnnotationModel> fetchAnnotations(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
         boolean isComplete = true;
-        ImmutableMap.Builder<String, Collection<Annotation>> annotationsBuilder = ImmutableMap.builder();
+        Map<String, Collection<Annotation>> annotations = new LinkedHashMap<>();
 
         for (P dataProvider : getProviders()) {
             if (dataProvider instanceof IOutputAnnotationProvider) {
@@ -208,7 +209,10 @@
                 isComplete &= response.getStatus() == ITmfResponse.Status.COMPLETED;
                 AnnotationModel model = response.getModel();
                 if (model != null) {
-                    annotationsBuilder.putAll(model.getAnnotations());
+                    for (Entry<String, Collection<Annotation>> annotation:  model.getAnnotations().entrySet()) {
+                        Collection<Annotation> elements = annotations.computeIfAbsent(annotation.getKey(), unused -> new ArrayList<>());
+                        elements.addAll(annotation.getValue());
+                    }
                 }
 
                 if (monitor != null && monitor.isCanceled()) {
@@ -217,9 +221,9 @@
             }
         }
         if (isComplete) {
-            return new TmfModelResponse<>(new AnnotationModel(annotationsBuilder.build()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+            return new TmfModelResponse<>(new AnnotationModel(annotations), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
         }
-        return new TmfModelResponse<>(new AnnotationModel(annotationsBuilder.build()), ITmfResponse.Status.RUNNING, CommonStatusMessage.RUNNING);
+        return new TmfModelResponse<>(new AnnotationModel(annotations), ITmfResponse.Status.RUNNING, CommonStatusMessage.RUNNING);
     }
 }