|
| 1 | +/********************************************************************** |
| 2 | + * Copyright (c) 2018, 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 | + |
| 12 | +package org.eclipse.tracecompass.internal.provisional.tmf.core.model.events; |
| 13 | + |
| 14 | +import java.util.ArrayList; |
| 15 | +import java.util.LinkedHashMap; |
| 16 | +import java.util.List; |
| 17 | +import java.util.Map; |
| 18 | +import java.util.regex.Pattern; |
| 19 | + |
| 20 | +import org.eclipse.core.runtime.IProgressMonitor; |
| 21 | +import org.eclipse.jdt.annotation.Nullable; |
| 22 | +import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| 23 | +import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| 24 | +import org.eclipse.tracecompass.tmf.core.event.aspect.TmfBaseAspects; |
| 25 | +import org.eclipse.tracecompass.tmf.core.model.AbstractTmfTraceDataProvider; |
| 26 | +import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage; |
| 27 | +import org.eclipse.tracecompass.tmf.core.model.object.IObjectDataProvider; |
| 28 | +import org.eclipse.tracecompass.tmf.core.model.object.ObjectModel; |
| 29 | +import org.eclipse.tracecompass.tmf.core.response.ITmfResponse; |
| 30 | +import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse; |
| 31 | +import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| 32 | +import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; |
| 33 | +import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| 34 | + |
| 35 | +import com.google.common.collect.ImmutableMap; |
| 36 | +import com.google.common.collect.ImmutableMap.Builder; |
| 37 | + |
| 38 | +/** |
| 39 | + * This data provider will return an object representing an array of raw event |
| 40 | + * data. |
| 41 | + * |
| 42 | + * @author Patrick Tasse |
| 43 | + */ |
| 44 | +public class TmfRawEventsDataProvider extends AbstractTmfTraceDataProvider implements IObjectDataProvider { |
| 45 | + |
| 46 | + /** |
| 47 | + * Extension point ID. |
| 48 | + */ |
| 49 | + public static final String ID = "org.eclipse.tracecompass.tmf.core.model.events.data"; //$NON-NLS-1$ |
| 50 | + |
| 51 | + private static final String SELECTION_RANGE = "selection_range"; //$NON-NLS-1$ |
| 52 | + private static final String START = "start"; //$NON-NLS-1$ |
| 53 | + private static final String NEXT = "next"; //$NON-NLS-1$ |
| 54 | + private static final String PREVIOUS = "previous"; //$NON-NLS-1$ |
| 55 | + private static final String TYPE = "type"; //$NON-NLS-1$ |
| 56 | + private static final String FIELD = "field"; //$NON-NLS-1$ |
| 57 | + private static final String COUNT = "count"; //$NON-NLS-1$ |
| 58 | + |
| 59 | + /** |
| 60 | + * Constructor |
| 61 | + * |
| 62 | + * @param trace |
| 63 | + * A trace on which we are interested to fetch a raw event model |
| 64 | + */ |
| 65 | + public TmfRawEventsDataProvider(ITmfTrace trace) { |
| 66 | + super(trace); |
| 67 | + } |
| 68 | + |
| 69 | + @Override |
| 70 | + public String getId() { |
| 71 | + return ID; |
| 72 | + } |
| 73 | + |
| 74 | + @Override |
| 75 | + public TmfModelResponse<ObjectModel> fetchData(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) { |
| 76 | + ITmfTrace trace = getTrace(); |
| 77 | + long rank = 0L; |
| 78 | + Long prev = null; |
| 79 | + Long next = null; |
| 80 | + Object selectionRangeObj = fetchParameters.get(SELECTION_RANGE); |
| 81 | + Object nextObj = fetchParameters.get(NEXT); |
| 82 | + Object previousObj = fetchParameters.get(PREVIOUS); |
| 83 | + Object typeObj = fetchParameters.get(TYPE); |
| 84 | + Object fieldObj = fetchParameters.get(FIELD); |
| 85 | + Object countObj = fetchParameters.get(COUNT); |
| 86 | + if (nextObj instanceof Number nextNum) { |
| 87 | + rank = nextNum.longValue(); |
| 88 | + prev = rank; |
| 89 | + previousObj = null; |
| 90 | + } else if (previousObj instanceof Number previousNum) { |
| 91 | + rank = previousNum.longValue(); |
| 92 | + next = rank; |
| 93 | + nextObj = null; |
| 94 | + } else if (selectionRangeObj instanceof Map<?,?> selectionRange) { |
| 95 | + if (selectionRange.get(START) instanceof Number start) { |
| 96 | + rank = trace.seekEvent(TmfTimestamp.fromNanos(start.longValue())).getRank(); |
| 97 | + } |
| 98 | + nextObj = null; |
| 99 | + previousObj = null; |
| 100 | + } |
| 101 | + Pattern eventTypePattern = (typeObj instanceof String type) ? Pattern.compile(type) : null; |
| 102 | + Pattern fieldPattern = (fieldObj instanceof String field) ? Pattern.compile(field) : null; |
| 103 | + Integer max = null; |
| 104 | + if (countObj instanceof Number count) { |
| 105 | + max = count.intValue(); |
| 106 | + } |
| 107 | + if (eventTypePattern == null && fieldPattern == null && previousObj instanceof Number) { |
| 108 | + // optimize backward request when no filters specified |
| 109 | + if (max == null) { |
| 110 | + max = (int) rank; |
| 111 | + rank = 0L; |
| 112 | + } else if (rank >= max) { |
| 113 | + rank = rank - max; |
| 114 | + } else { |
| 115 | + max = (int) rank; |
| 116 | + rank = 0L; |
| 117 | + } |
| 118 | + // clear previousObj to read forwards |
| 119 | + previousObj = null; |
| 120 | + } |
| 121 | + List<Object> object = new ArrayList<>(); |
| 122 | + ObjectModel model; |
| 123 | + if (previousObj instanceof Number) { |
| 124 | + // read backward |
| 125 | + while (rank > 0 && (max == null || object.size() < max)) { |
| 126 | + rank--; |
| 127 | + ITmfContext context = trace.seekEvent(rank); |
| 128 | + long eventRank = context.getRank(); |
| 129 | + ITmfEvent event = trace.getNext(context); |
| 130 | + if (event == null) { |
| 131 | + break; |
| 132 | + } |
| 133 | + if (eventTypePattern == null || eventTypePattern.matcher(event.getName()).find()) { |
| 134 | + Object eventObj = convertEvent(event, eventRank, fieldPattern); |
| 135 | + if (eventObj != null) { |
| 136 | + object.add(0, eventObj); |
| 137 | + } |
| 138 | + } |
| 139 | + } |
| 140 | + model = new ObjectModel(object); |
| 141 | + if (rank > 0L) { |
| 142 | + model.setPrevious(rank); |
| 143 | + } |
| 144 | + if (next != null) { |
| 145 | + model.setNext(next); |
| 146 | + } |
| 147 | + } else { |
| 148 | + // read forward |
| 149 | + ITmfContext context = trace.seekEvent(rank); |
| 150 | + while (max == null || object.size() < max) { |
| 151 | + long eventRank = context.getRank(); |
| 152 | + ITmfEvent event = trace.getNext(context); |
| 153 | + if (event == null) { |
| 154 | + break; |
| 155 | + } |
| 156 | + if (eventTypePattern == null || eventTypePattern.matcher(event.getName()).find()) { |
| 157 | + Object eventObj = convertEvent(event, eventRank, fieldPattern); |
| 158 | + if (eventObj != null) { |
| 159 | + object.add(eventObj); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + model = new ObjectModel(object); |
| 164 | + next = context.getRank(); |
| 165 | + if (trace.getNext(context) != null) { |
| 166 | + model.setNext(next); |
| 167 | + } |
| 168 | + if (prev != null && prev > 0L) { |
| 169 | + model.setPrevious(prev); |
| 170 | + } |
| 171 | + } |
| 172 | + return new TmfModelResponse<>(model, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); |
| 173 | + } |
| 174 | + |
| 175 | + private static @Nullable Object convertEvent(ITmfEvent event, long rank, @Nullable Pattern fieldPattern) { |
| 176 | + Map<String, @Nullable Object> data = new LinkedHashMap<>(); |
| 177 | + |
| 178 | + // Add basic event information |
| 179 | + data.put("rank", rank); //$NON-NLS-1$ |
| 180 | + data.put(TmfBaseAspects.getTimestampAspect().getName(), event.getTimestamp().toNanos()); |
| 181 | + data.put(TmfBaseAspects.getEventTypeAspect().getName(), event.getType().getName()); |
| 182 | + Object contents = convertEventField(event.getContent(), fieldPattern); |
| 183 | + if (fieldPattern != null && contents instanceof Map map) { |
| 184 | + if (map.isEmpty()) { |
| 185 | + return null; |
| 186 | + } |
| 187 | + } |
| 188 | + data.put(TmfBaseAspects.getContentsAspect().getName(), contents); |
| 189 | + return data; |
| 190 | + } |
| 191 | + |
| 192 | + private static @Nullable Object convertEventField(@Nullable ITmfEventField field, @Nullable Pattern fieldPattern) { |
| 193 | + if (field == null) { |
| 194 | + return null; |
| 195 | + } |
| 196 | + Builder<String, @Nullable Object> builder = ImmutableMap.builder(); |
| 197 | + for (ITmfEventField subField : field.getFields()) { |
| 198 | + if (subField == null) { |
| 199 | + continue; |
| 200 | + } |
| 201 | + String name = subField.getName(); |
| 202 | + if (fieldPattern == null || fieldPattern.matcher(name).find()) { |
| 203 | + Object value = subField.getValue(); |
| 204 | + if (value instanceof ITmfEventField) { |
| 205 | + builder.put(name, convertEventField((ITmfEventField) value, null)); |
| 206 | + } else { |
| 207 | + builder.put(name, value); |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + Object value = field.getValue(); |
| 212 | + if (value instanceof ITmfEventField) { |
| 213 | + value = convertEventField((ITmfEventField) value, null); |
| 214 | + } |
| 215 | + ImmutableMap<String, @Nullable Object> fields = builder.build(); |
| 216 | + if (value == null) { |
| 217 | + return fields; |
| 218 | + } else if (fields.isEmpty()) { |
| 219 | + return value; |
| 220 | + } else { |
| 221 | + return Map.of("value", value, "fields", fields); //$NON-NLS-1$ //$NON-NLS-2$ |
| 222 | + } |
| 223 | + } |
| 224 | +} |
0 commit comments