Skip to content

Commit 2062a0b

Browse files
author
Siwei Zhang
committed
tmf: Create interface for TmfTreeModel
This commit adds a new ITmfTreeModel interface and updates the existing TmfTreeModel class to implement it. The interface is introduced to support additional implementations, such as the one required by the bar chart data provider. Signed-off-by: Siwei Zhang <siwei.zhang@ericsson.com>
1 parent 980bc83 commit 2062a0b

5 files changed

Lines changed: 192 additions & 87 deletions

File tree

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/tree/AbstractTreeDataProvider.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public abstract class AbstractTreeDataProvider<A extends TmfStateSystemAnalysisM
7979
private final A fAnalysisModule;
8080
private final ReentrantReadWriteLock fLock = new ReentrantReadWriteLock(false);
8181
private final BiMap<Long, Integer> fIdToQuark = HashBiMap.create();
82-
private @Nullable TmfModelResponse<TmfTreeModel<M>> fCached;
82+
private @Nullable TmfModelResponse<ITmfTreeModel<M>> fCached;
8383
private final Map<Long, Multimap<String, Object>> fEntryMetadata = new HashMap<>();
8484
private @Nullable String fScope;
8585
private Map<Object, Long> fIdTable = new HashMap<>();
@@ -262,6 +262,13 @@ protected static Collection<Long> getTimes(TimeQueryFilter filter, long start, l
262262

263263
@Override
264264
public final TmfModelResponse<TmfTreeModel<M>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
265+
@SuppressWarnings("unchecked")
266+
TmfModelResponse<TmfTreeModel<M>> castedResponse = (TmfModelResponse<TmfTreeModel<M>>) (TmfModelResponse<?>) fetchTreeAsInterface(fetchParameters, monitor);
267+
return castedResponse;
268+
}
269+
270+
@Override
271+
public final TmfModelResponse<ITmfTreeModel<M>> fetchTreeAsInterface(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
265272
fLock.readLock().lock();
266273
try {
267274
if (fCached != null) {
@@ -286,7 +293,7 @@ public final TmfModelResponse<TmfTreeModel<M>> fetchTree(Map<String, Object> fet
286293
boolean complete = ss.waitUntilBuilt(0);
287294
try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "AbstractTreeDataProvider#fetchTree") //$NON-NLS-1$
288295
.setCategory(getClass().getSimpleName()).build()) {
289-
TmfTreeModel<M> tree = null;
296+
ITmfTreeModel<M> tree = null;
290297
/* Don't query empty state system */
291298
if (ss.getNbAttributes() > 0 && ss.getStartTime() != Long.MIN_VALUE) {
292299
tree = getTree(ss, fetchParameters, monitor);
@@ -297,7 +304,7 @@ public final TmfModelResponse<TmfTreeModel<M>> fetchTree(Map<String, Object> fet
297304
}
298305
}
299306
if (complete) {
300-
TmfModelResponse<TmfTreeModel<M>> response = new TmfModelResponse<>(tree,
307+
TmfModelResponse<ITmfTreeModel<M>> response = new TmfModelResponse<>(tree,
301308
ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
302309
if (isCacheable()) {
303310
fCached = response;
@@ -349,8 +356,9 @@ protected Multimap<String, Object> getEntryMetadata(Long entryId) {
349356
* @throws StateSystemDisposedException
350357
* if the state system was closed during the query or could not be
351358
* queried.
359+
* @since 10.2
352360
*/
353-
protected abstract TmfTreeModel<M> getTree(ITmfStateSystem ss, Map<String, Object> fetchParameters,
361+
protected abstract ITmfTreeModel<M> getTree(ITmfStateSystem ss, Map<String, Object> fetchParameters,
354362
@Nullable IProgressMonitor monitor) throws StateSystemDisposedException;
355363

356364
private void checkScopeInitialized() {

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/tree/ITmfTreeDataProvider.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,26 @@ public interface ITmfTreeDataProvider<T extends ITmfTreeDataModel> extends ITmfD
4242
* @return A {@link TmfModelResponse} instance
4343
* @since 5.0
4444
*/
45+
@Deprecated(since = "10.2", forRemoval = true)
4546
TmfModelResponse<TmfTreeModel<T>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor);
47+
48+
/**
49+
* Computes a tree model and returns a {@link TmfModelResponse} containing a
50+
* generic {@link ITmfTreeModel} interface.
51+
*
52+
* @param fetchParameters
53+
* A query filter that contains an array of time. Times are used
54+
* for requesting data.
55+
* @param monitor
56+
* A ProgressMonitor to cancel the task.
57+
* @return A {@link TmfModelResponse} containing the tree model interface.
58+
* @since 10.2
59+
*/
60+
default TmfModelResponse<ITmfTreeModel<T>> fetchTreeAsInterface(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
61+
@SuppressWarnings("unchecked")
62+
TmfModelResponse<ITmfTreeModel<T>> response = (TmfModelResponse<ITmfTreeModel<T>>) (TmfModelResponse<?>) fetchTree(fetchParameters, monitor);
63+
return response;
64+
}
4665
}
4766

4867

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**********************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License 2.0 which
6+
* accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
**********************************************************************/
11+
package org.eclipse.tracecompass.tmf.core.model.tree;
12+
13+
import java.util.List;
14+
import org.eclipse.jdt.annotation.Nullable;
15+
16+
/**
17+
* Interface representing a tree model with entries and optional scope.
18+
*
19+
* @param <T>
20+
* The tree data model extending {@link ITmfTreeDataModel}
21+
*
22+
* @author Siwei Zhang
23+
* @since 10.2
24+
*/
25+
public interface ITmfTreeModel<T extends ITmfTreeDataModel> {
26+
/**
27+
* Entries for the model
28+
*
29+
* @return List of entries
30+
*/
31+
List<T> getEntries();
32+
33+
/**
34+
* Scope of all entry ids in the model
35+
*
36+
* @return Scope
37+
* @since 5.2
38+
*/
39+
@Nullable String getScope();
40+
41+
/**
42+
* Returns the auto-expand level.
43+
*
44+
* @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
45+
* the tree are expanded automatically
46+
* @see #setAutoExpandLevel
47+
* @since 10.1
48+
*/
49+
int getAutoExpandLevel();
50+
51+
/**
52+
* Sets the auto-expand level to be used for the input of the tree. The
53+
* value 0 means that there is no auto-expand; 1 means that top-level
54+
* elements are expanded, but not their children; 2 means that top-level
55+
* elements are expanded, and their children, but not grand-children; and so
56+
* on.
57+
* <p>
58+
* The value {@link #ALL_LEVELS} means that all subtrees should be expanded.
59+
* </p>
60+
*
61+
* @param level
62+
* The auto expand level to set
63+
* @since 10.1
64+
*/
65+
void setAutoExpandLevel(int level);
66+
}

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/tree/TmfTreeCompositeDataProvider.java

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
import java.util.ArrayList;
1616
import java.util.Collection;
1717
import java.util.Collections;
18-
import java.util.HashMap;
1918
import java.util.List;
2019
import java.util.Map;
2120
import java.util.Map.Entry;
2221
import java.util.concurrent.CopyOnWriteArrayList;
2322
import java.util.concurrent.atomic.AtomicInteger;
23+
import java.util.stream.Collectors;
2424

2525
import org.eclipse.core.runtime.IProgressMonitor;
2626
import org.eclipse.jdt.annotation.NonNull;
@@ -36,15 +36,14 @@
3636
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
3737

3838
import com.google.common.collect.HashBasedTable;
39-
import com.google.common.collect.Lists;
4039
import com.google.common.collect.Table;
4140

4241
/**
4342
* Represents a base implementation of {@link ITmfTreeDataProvider} that
4443
* supports experiments. Clients of this data provider must provide a list of
4544
* {@link ITmfTreeDataProvider} for each trace in the experiment which supports
4645
* the provider. From the list of sub data provider, this data provider will
47-
* merge all responses into one.
46+
* merge all responses into one and default merges {@link TmfTreeModel}.
4847
*
4948
* @param <M>
5049
* The type of {@link ITmfTreeDataModel} that this composite's tree
@@ -101,63 +100,105 @@ public TmfTreeCompositeDataProvider(List<P> providers, String id) {
101100
fId = id;
102101
}
103102

103+
/**
104+
* Helper class to merge entries from multiple ITmfTreeModel instances into
105+
* a unified ordered list, preserving parent-child relationships and
106+
* avoiding duplicate nodes across scopes.
107+
*
108+
* @param <TEntry>
109+
* The type of tree entry model
110+
* @since 10.2
111+
*/
112+
public class TreeMergeHelper<TEntry extends ITmfTreeDataModel> {
113+
114+
private final List<Entry<TEntry, Object>> entries = new ArrayList<>();
115+
private final Table<Object, Long, @NonNull TEntry> scopedEntries = HashBasedTable.create();
116+
117+
/**
118+
* Merges the entries from a given tree model into the current list of
119+
* merged entries.
120+
*
121+
* @param model
122+
* The tree model containing entries to merge.
123+
* @param scope
124+
* The logical scope or owner of the model (e.g., a data
125+
* provider).
126+
*/
127+
public void mergeTree(ITmfTreeModel<TEntry> model, Object scope) {
128+
Map<Long, AtomicInteger> indexMap = new java.util.HashMap<>();
129+
Map<Long, @NonNull TEntry> row = scopedEntries.row(scope);
130+
131+
for (TEntry entry : model.getEntries()) {
132+
TEntry previous = row.putIfAbsent(entry.getId(), entry);
133+
// Ignore duplicate entries from different data providers
134+
if (previous == null) {
135+
if (entry.getParentId() == -1) {
136+
entries.add(new SimpleEntry<>(entry, scope));
137+
} else {
138+
/*
139+
* Insert new entries from subsequent data providers
140+
* at the correct position in the entries list. New
141+
* entries are inserted before sibling entries from
142+
* previous data providers.
143+
*/
144+
int index = indexMap.computeIfAbsent(entry.getParentId(), l -> new AtomicInteger()).getAndIncrement();
145+
int pos = 0;
146+
while (pos < entries.size()) {
147+
Entry<TEntry, Object> added = entries.get(pos);
148+
if (added.getValue().equals(scope) && added.getKey().getParentId() == entry.getParentId()) {
149+
if (index == 0) {
150+
break;
151+
}
152+
index--;
153+
}
154+
pos++;
155+
}
156+
if (pos < entries.size()) {
157+
entries.add(pos, new SimpleEntry<>(entry, scope));
158+
} else {
159+
entries.add(new SimpleEntry<>(entry, scope));
160+
}
161+
}
162+
} else {
163+
indexMap.computeIfAbsent(entry.getParentId(), l -> new AtomicInteger()).getAndIncrement();
164+
}
165+
}
166+
}
167+
168+
/**
169+
* Retrieves the final list of merged entries.
170+
*
171+
* @return The list of merged model entries, preserving insertion order.
172+
*/
173+
public List<TEntry> getMergedEntries() {
174+
return entries.stream().map(Entry::getKey).collect(Collectors.toList());
175+
}
176+
}
177+
104178
@Override
105179
public TmfModelResponse<TmfTreeModel<M>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
106180
boolean isComplete = true;
107-
List<Entry<M, Object>> entries = new ArrayList<>();
108181
List<ITableColumnDescriptor> columnDescriptor = null;
109182
int autoExpandLevel = TmfTreeModel.ALL_LEVELS;
110183

111-
Table<Object, Long, @NonNull M> scopedEntries = HashBasedTable.create();
184+
TreeMergeHelper<M> mergeHelper = new TreeMergeHelper<>();
185+
112186
for (P dataProvider : fProviders) {
113-
Map<Long, AtomicInteger> indexMap = new HashMap<>();
114-
TmfModelResponse<TmfTreeModel<M>> response = dataProvider.fetchTree(fetchParameters, monitor);
187+
TmfModelResponse<? extends ITmfTreeModel<M>> response = dataProvider.fetchTree(fetchParameters, monitor);
115188
isComplete &= response.getStatus() == ITmfResponse.Status.COMPLETED;
116-
TmfTreeModel<M> model = response.getModel();
189+
ITmfTreeModel<M> model = response.getModel();
190+
117191
if (model != null) {
118192
Object scope = (model.getScope() == null) ? dataProvider : model.getScope();
119-
Map<Long, @NonNull M> row = scopedEntries.row(scope);
120-
for (M entry : model.getEntries()) {
121-
M previous = row.putIfAbsent(entry.getId(), entry);
122-
// Ignore duplicate entries from different data providers
123-
if (previous == null) {
124-
if (entry.getParentId() == -1) {
125-
entries.add(new SimpleEntry(entry, scope));
126-
} else {
127-
/*
128-
* Insert new entries from subsequent data providers
129-
* at the correct position in the entries list. New
130-
* entries are inserted before sibling entries from
131-
* previous data providers.
132-
*/
133-
int index = indexMap.computeIfAbsent(entry.getParentId(), l -> new AtomicInteger()).getAndIncrement();
134-
int pos = 0;
135-
while (pos < entries.size()) {
136-
Entry<M, Object> added = entries.get(pos);
137-
if (added.getValue().equals(scope) && added.getKey().getParentId() == entry.getParentId()) {
138-
if (index == 0) {
139-
break;
140-
}
141-
index--;
142-
}
143-
pos++;
144-
}
145-
if (pos < entries.size()) {
146-
entries.add(pos, new SimpleEntry(entry, scope));
147-
} else {
148-
entries.add(new SimpleEntry(entry, scope));
149-
}
150-
}
151-
} else {
152-
indexMap.computeIfAbsent(entry.getParentId(), l -> new AtomicInteger()).getAndIncrement();
193+
mergeHelper.mergeTree(model, scope);
194+
195+
if (model instanceof TmfTreeModel) {
196+
autoExpandLevel = ((TmfTreeModel<M>) model).getAutoExpandLevel();
197+
// Use the column descriptor of the first model. All descriptors are supposed to be the same
198+
if(columnDescriptor == null) {
199+
columnDescriptor = ((TmfTreeModel<M>) model).getColumnDescriptors();
153200
}
154201
}
155-
// Use the column descriptor of the first model. All descriptors are supposed to be the same
156-
if (columnDescriptor == null) {
157-
columnDescriptor = model.getColumnDescriptors();
158-
}
159-
// All autoExpandLevel are supposed to be the same
160-
autoExpandLevel = model.getAutoExpandLevel();
161202
}
162203
if (monitor != null && monitor.isCanceled()) {
163204
return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
@@ -169,7 +210,7 @@ public TmfModelResponse<TmfTreeModel<M>> fetchTree(Map<String, Object> fetchPara
169210
columnDescriptor = Collections.emptyList();
170211
}
171212
treeModelBuilder.setColumnDescriptors(columnDescriptor)
172-
.setEntries(Lists.transform(entries, e -> e.getKey()))
213+
.setEntries(mergeHelper.getMergedEntries())
173214
.setAutoExpandLevel(autoExpandLevel);
174215

175216
if (isComplete) {

0 commit comments

Comments
 (0)