tmf: Update XML trace stub to use ns directly
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core.tests / stubs / org / eclipse / tracecompass / tmf / tests / stubs / trace / xml / TmfXmlTraceStub.java
CommitLineData
1d8ab692 1/*******************************************************************************
ed902a2b 2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal
1d8ab692
GB
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial implementation
a62b0a0d 11 * Patrick Tasse - Dispose wrapped trace
1d8ab692
GB
12 *******************************************************************************/
13
2bdf0193 14package org.eclipse.tracecompass.tmf.tests.stubs.trace.xml;
1d8ab692 15
fafdd006 16import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
ec52ed4a 17import static org.junit.Assert.fail;
fafdd006 18
1d8ab692
GB
19import java.io.File;
20import java.io.IOException;
21import java.io.InputStream;
22import java.net.URL;
69ff4f26 23import java.util.Collection;
c0d85d59 24import java.util.HashSet;
b405ad64
JCK
25import java.util.Optional;
26import java.util.stream.StreamSupport;
1d8ab692
GB
27
28import javax.xml.XMLConstants;
29import javax.xml.transform.Source;
30import javax.xml.transform.stream.StreamSource;
31import javax.xml.validation.Schema;
32import javax.xml.validation.SchemaFactory;
33import javax.xml.validation.Validator;
34
35import org.eclipse.core.resources.IProject;
36import org.eclipse.core.resources.IResource;
ec52ed4a 37import org.eclipse.core.runtime.IPath;
1d8ab692
GB
38import org.eclipse.core.runtime.IStatus;
39import org.eclipse.core.runtime.Status;
d12b792b 40import org.eclipse.jdt.annotation.DefaultLocation;
b405ad64 41import org.eclipse.jdt.annotation.NonNull;
d12b792b 42import org.eclipse.jdt.annotation.NonNullByDefault;
69ff4f26 43import org.eclipse.jdt.annotation.Nullable;
1d8ab692 44import org.eclipse.osgi.util.NLS;
2bdf0193 45import org.eclipse.tracecompass.internal.tmf.core.Activator;
b405ad64 46import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
2bdf0193
AM
47import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
48import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
49import org.eclipse.tracecompass.tmf.core.event.ITmfEventType;
50import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
51import org.eclipse.tracecompass.tmf.core.event.TmfEventField;
52import org.eclipse.tracecompass.tmf.core.event.TmfEventType;
69ff4f26 53import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
b405ad64 54import org.eclipse.tracecompass.tmf.core.event.aspect.TmfBaseAspects;
40dfafb3 55import org.eclipse.tracecompass.tmf.core.event.aspect.TmfContentFieldAspect;
69ff4f26 56import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect;
2bdf0193
AM
57import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
58import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomEventContent;
59import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlEvent;
60import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTrace;
61import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTraceDefinition;
62import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
63import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
64import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
65import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
66import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
0e564353
PT
67import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
68import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpointIndexer;
2bdf0193 69import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
1d8ab692
GB
70import org.xml.sax.SAXException;
71
69ff4f26 72import com.google.common.collect.ImmutableList;
b405ad64 73import com.google.common.collect.Iterables;
69ff4f26 74
1d8ab692
GB
75/**
76 * An XML development trace using a custom XML trace definition and schema.
77 *
78 * This class will typically be used to build custom traces to unit test more
79 * complex functionalities like analyzes or to develop and test data-driven
80 * analyzes.
81 *
82 * This class wraps a custom XML trace and rewrites the returned events in the
83 * getNext() method so that event's fields are the ones defined in <field ... />
84 * elements instead of those defined in the custom XML parser. This way, each
85 * event can have a different set of fields. This class can, for example, mimic
86 * a CTF trace.
87 *
88 * @author Geneviève Bastien
89 */
90public class TmfXmlTraceStub extends TmfTrace {
91
92 private static final String DEVELOPMENT_TRACE_PARSER_PATH = "TmfXmlDevelopmentTrace.xml"; //$NON-NLS-1$
93 private static final String DEVELOPMENT_TRACE_XSD = "TmfXmlDevelopmentTrace.xsd"; //$NON-NLS-1$
94 private static final String EMPTY = ""; //$NON-NLS-1$
95
96 /* XML elements and attributes names */
97 private static final String EVENT_NAME_FIELD = "Message"; //$NON-NLS-1$
98 private static final String FIELD_NAMES_FIELD = "fields"; //$NON-NLS-1$
1d8ab692
GB
99 private static final String VALUES_FIELD = "values"; //$NON-NLS-1$
100 private static final String TYPES_FIELD = "type"; //$NON-NLS-1$
101 private static final String VALUES_SEPARATOR = " \\| "; //$NON-NLS-1$
102 private static final String TYPE_INTEGER = "int"; //$NON-NLS-1$
103 private static final String TYPE_LONG = "long"; //$NON-NLS-1$
69ff4f26 104 private static final String ASPECT_CPU = "cpu";
1d8ab692 105
0e564353
PT
106 private final CustomXmlTraceDefinition fDefinition;
107 private CustomXmlTrace fTrace;
1d8ab692 108
ec48d248
AM
109 private Collection<ITmfEventAspect<?>> fAspects = TmfTrace.BASE_ASPECTS;
110 private final Collection<ITmfEventAspect<?>> fAdditionalAspects = new HashSet<>();
b405ad64 111 private final Collection<IAnalysisModule> fAdditionalModules = new HashSet<>();
69ff4f26 112
ec52ed4a
GB
113 /**
114 * Validate and initialize a {@link TmfXmlTraceStub} object
115 *
116 * @param absolutePath
117 * The absolute file path of the trace file
118 * @return The trace
119 */
120 public static TmfXmlTraceStub setupTrace(IPath absolutePath) {
121 TmfXmlTraceStub trace = new TmfXmlTraceStub();
122 IStatus status = trace.validate(null, absolutePath.toOSString());
123 if (!status.isOK()) {
124 fail(status.getException().getMessage());
125 }
126 try {
127 trace.initTrace(null, absolutePath.toOSString(), TmfEvent.class);
128 } catch (TmfTraceException e) {
ded2b27f 129 trace.dispose();
ec52ed4a
GB
130 fail(e.getMessage());
131 }
132 return trace;
133 }
134
1d8ab692
GB
135 /**
136 * Constructor. Constructs the custom XML trace with the appropriate
137 * definition.
138 */
139 public TmfXmlTraceStub() {
ded2b27f 140 boolean nonNullTraceIsNonNull = false;
1d8ab692
GB
141 /* Load custom XML definition */
142 try (InputStream in = TmfXmlTraceStub.class.getResourceAsStream(DEVELOPMENT_TRACE_PARSER_PATH);) {
143 CustomXmlTraceDefinition[] definitions = CustomXmlTraceDefinition.loadAll(in);
0e564353 144 if (definitions.length < 2) {
1d8ab692
GB
145 throw new IllegalStateException("The custom trace definition does not exist"); //$NON-NLS-1$
146 }
0e564353
PT
147 /* The first definition parses the 'set_aspects' event */
148 fTrace = new CustomXmlTrace(definitions[0]) {
149 @Override
150 protected ITmfTraceIndexer createIndexer(int interval) {
151 /* Use the in-memory checkpoint indexer */
152 return new TmfCheckpointIndexer(this, interval);
153 }
154 };
ded2b27f
PT
155 TmfSignalManager.deregister(fTrace);
156 nonNullTraceIsNonNull = true;
0e564353
PT
157 /* The second definition parses 'event' trace events */
158 fDefinition = checkNotNull(definitions[1]);
1d8ab692 159 } catch (IOException e) {
ded2b27f
PT
160 if (nonNullTraceIsNonNull) {
161 fTrace.dispose();
162 }
1d8ab692
GB
163 throw new IllegalStateException("Cannot open the trace parser for development traces"); //$NON-NLS-1$
164 }
165
166 }
167
168 @Override
d12b792b 169 @NonNullByDefault({DefaultLocation.TYPE_ARGUMENT})
69ff4f26 170 public void initTrace(@Nullable IResource resource, @Nullable String path, @Nullable Class<? extends ITmfEvent> type) throws TmfTraceException {
1d8ab692 171 super.initTrace(resource, path, type);
1d8ab692 172 ITmfContext ctx;
0e564353
PT
173
174 /* Initialize and read the trace with the 'set_aspects' definition */
175 TmfSignalManager.deregister(fTrace);
176 fTrace.initTrace(resource, path, type);
177 ctx = seekEvent(0L);
178 /* If a set_aspects event exists, getNext() will process it */
179 getNext(ctx);
180 ctx.dispose();
181 fTrace.dispose();
182
183 /* Initialize a new trace with the trace events definition */
184 fTrace = new CustomXmlTrace(fDefinition);
185 TmfSignalManager.deregister(fTrace);
186 fTrace.initTrace(resource, path, type);
1d8ab692
GB
187 /* Set the start and (current) end times for this trace */
188 ctx = seekEvent(0L);
69ff4f26
GB
189 if (ctx == null) {
190 return;
191 }
1d8ab692
GB
192 ITmfEvent event = getNext(ctx);
193 if (event != null) {
194 final ITmfTimestamp curTime = event.getTimestamp();
195 this.setStartTime(curTime);
196 this.setEndTime(curTime);
197 }
0e564353 198 ctx.dispose();
1d8ab692
GB
199 }
200
a62b0a0d
PT
201 @Override
202 public synchronized void dispose() {
203 super.dispose();
204 fTrace.dispose();
205 }
206
5733be39
AM
207 @Override
208 public @Nullable ITmfEvent parseEvent(@Nullable ITmfContext context) {
209 return fTrace.parseEvent(context);
210 }
211
1d8ab692 212 @Override
69ff4f26 213 public @Nullable ITmfLocation getCurrentLocation() {
1d8ab692
GB
214 return fTrace.getCurrentLocation();
215 }
216
217 @Override
69ff4f26 218 public double getLocationRatio(@Nullable ITmfLocation location) {
1d8ab692
GB
219 return fTrace.getLocationRatio(location);
220 }
221
222 @Override
69ff4f26 223 public @Nullable ITmfContext seekEvent(@Nullable ITmfLocation location) {
1d8ab692
GB
224 return fTrace.seekEvent(location);
225 }
226
227 @Override
69ff4f26 228 public @Nullable ITmfContext seekEvent(double ratio) {
1d8ab692
GB
229 return fTrace.seekEvent(ratio);
230 }
231
232 @Override
69ff4f26 233 public IStatus validate(@Nullable IProject project, @Nullable String path) {
1d8ab692
GB
234 File xmlFile = new File(path);
235 if (!xmlFile.exists() || !xmlFile.isFile() || !xmlFile.canRead()) {
2bdf0193 236 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_FileNotFound, path));
1d8ab692
GB
237 }
238 /* Does the XML file validate with the XSD */
239 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
240 Source xmlSource = new StreamSource(xmlFile);
241
242 try {
243 URL url = TmfXmlTraceStub.class.getResource(DEVELOPMENT_TRACE_XSD);
244 Schema schema = schemaFactory.newSchema(url);
245
246 Validator validator = schema.newValidator();
247 validator.validate(xmlSource);
248 } catch (SAXException e) {
2bdf0193 249 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_ValidationError, path), e);
1d8ab692 250 } catch (IOException e) {
2bdf0193 251 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_IoError, path), e);
1d8ab692 252 }
8cf4e2d7 253 return Status.OK_STATUS;
1d8ab692
GB
254 }
255
69ff4f26 256 private static String getStringValue(ITmfEventField content, String fieldName) {
56e9f830
GB
257 ITmfEventField field = content.getField(fieldName);
258 if (field == null) {
259 return EMPTY;
260 }
261 Object val = field.getValue();
262 if (!(val instanceof String)) {
263 return EMPTY;
264 }
265 return (String) val;
266 }
267
1d8ab692 268 @Override
69ff4f26
GB
269 public synchronized @Nullable ITmfEvent getNext(@Nullable ITmfContext context) {
270 if (context == null) {
271 return null;
272 }
1d8ab692
GB
273 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
274 CustomXmlEvent event = fTrace.getNext(context);
275 if (event == null) {
276 return null;
277 }
278
279 /* Translate the content of the event */
280 /* The "fields" field contains a | separated list of field names */
281 /* The "values" field contains a | separated list of field values */
282 /* the "type" field contains a | separated list of field types */
283 ITmfEventField content = event.getContent();
56e9f830
GB
284 if (content == null) {
285 return null;
286 }
69ff4f26 287
56e9f830
GB
288 String fieldString = getStringValue(content, FIELD_NAMES_FIELD);
289 String valueString = getStringValue(content, VALUES_FIELD);
290 String typeString = getStringValue(content, TYPES_FIELD);
1d8ab692
GB
291
292 String[] fields = fieldString.split(VALUES_SEPARATOR);
293 String[] values = valueString.split(VALUES_SEPARATOR);
294 String[] types = typeString.split(VALUES_SEPARATOR);
295 ITmfEventField[] fieldsArray = new TmfEventField[fields.length];
296
297 for (int i = 0; i < fields.length; i++) {
298 String value = EMPTY;
299 if (values.length > i) {
300 value = values[i];
301 }
302 String type = null;
303 if (types.length > i) {
304 type = types[i];
305 }
306 Object val = value;
307 if (type != null) {
308 switch (type) {
309 case TYPE_INTEGER: {
310 try {
311 val = Integer.valueOf(value);
312 } catch (NumberFormatException e) {
313 Activator.logError(String.format("Get next XML event: cannot cast value %s to integer", value), e); //$NON-NLS-1$
314 val = 0;
315 }
316 break;
317 }
318 case TYPE_LONG: {
319 try {
320 val = Long.valueOf(value);
321 } catch (NumberFormatException e) {
322 Activator.logError(String.format("Get next XML event: cannot cast value %s to long", value), e); //$NON-NLS-1$
323 val = 0L;
324 }
325 break;
326 }
327 default:
328 break;
329 }
330 }
fafdd006 331 fieldsArray[i] = new TmfEventField(checkNotNull(fields[i]), val, null);
1d8ab692
GB
332 }
333
c0d85d59
GB
334 /*
335 * Generate the aspects for this trace if it is the 'set_aspects'
336 * definition
337 */
0e564353 338 if (fTrace.getDefinition() != fDefinition) {
69ff4f26 339 generateAspects(fieldsArray);
0e564353 340 return null;
69ff4f26
GB
341 }
342
1d8ab692
GB
343 /* Create a new event with new fields and name */
344 ITmfEventType customEventType = event.getType();
0e564353 345 String eventName = getStringValue(content, EVENT_NAME_FIELD);
69ff4f26 346 TmfEventType eventType = new TmfEventType(eventName, customEventType.getRootField());
1d8ab692 347 ITmfEventField eventFields = new CustomEventContent(content.getName(), content.getValue(), fieldsArray);
12ca2c10
GB
348 /*
349 * TODO: Timestamps for these traces are in nanos, but since the
350 * CustomXmlTrace does not support this format, the timestamp of the
351 * original is in second and we need to convert it. We should do that at
352 * the source when it is supported
353 */
730fd8d2 354 TmfEvent newEvent = new TmfEvent(this, ITmfContext.UNKNOWN_RANK, event.getTimestamp(), eventType, eventFields);
5904c11e 355 updateAttributes(savedContext, event);
1d8ab692
GB
356 return newEvent;
357 }
358
69ff4f26 359 private void generateAspects(ITmfEventField[] fieldsArray) {
ec48d248 360 ImmutableList.Builder<ITmfEventAspect<?>> builder = new ImmutableList.Builder<>();
69ff4f26
GB
361
362 /* Initialize the first default trace aspects */
d187a29b
GB
363 builder.add(TmfBaseAspects.getTimestampAspect());
364 builder.add(TmfBaseAspects.getEventTypeAspect());
69ff4f26
GB
365
366 /* Add custom aspects in between */
367 for (ITmfEventField field : fieldsArray) {
368 String name = field.getName();
ec48d248 369 final ITmfEventAspect<?> aspect = new TmfContentFieldAspect(name, name);
69ff4f26 370 if (name.equals(ASPECT_CPU)) {
40dfafb3
PT
371 builder.add(new TmfCpuAspect() {
372 @Override
373 public @Nullable Integer resolve(ITmfEvent event) {
374 Object result = aspect.resolve(event);
375 if (result instanceof Number) {
376 return ((Number) result).intValue();
377 }
378 return null;
379 }
380 });
381 } else {
382 builder.add(aspect);
69ff4f26 383 }
69ff4f26
GB
384 }
385
386 /* Add the big content aspect */
d187a29b 387 builder.add(TmfBaseAspects.getContentsAspect());
c0d85d59
GB
388 /* Add the additional aspects */
389 builder.addAll(fAdditionalAspects);
872ec368 390 fAspects = builder.build();
69ff4f26
GB
391 }
392
393 @Override
ec48d248 394 public Iterable<ITmfEventAspect<?>> getEventAspects() {
69ff4f26
GB
395 return fAspects;
396 }
397
c0d85d59
GB
398 /**
399 * Adds a new event aspect to this type of trace. Since this trace type is
400 * used to build custom traces that mimic the behavior of real traces, the
401 * required aspects may be missing and this method allows to add them. This
402 * method should be called before calling
403 * {@link #initTrace(IResource, String, Class)} otherwise the additional
404 * aspects will not be picked up when generating the aspects.
405 *
406 * @param aspect
407 * The aspect to have
408 */
ec48d248 409 public void addEventAspect(ITmfEventAspect<?> aspect) {
c0d85d59
GB
410 fAdditionalAspects.add(aspect);
411 }
412
b405ad64
JCK
413 /**
414 * Add an additional new module
415 *
416 * @param module
417 * The new module
418 */
419 public void addAnalysisModule(IAnalysisModule module) {
420 fAdditionalModules.add(module);
421 }
422
423 @Override
424 public Iterable<@NonNull IAnalysisModule> getAnalysisModules() {
425 @NonNull Iterable<IAnalysisModule> modules = super.getAnalysisModules();
426 return checkNotNull(Iterables.concat(modules, fAdditionalModules));
427 }
428
429 @Override
430 public @Nullable IAnalysisModule getAnalysisModule(@Nullable String analysisId) {
431 Iterable<@NonNull IAnalysisModule> modules = getAnalysisModules();
432 Optional<IAnalysisModule> opt = StreamSupport.stream(modules.spliterator(), false).filter(analysis -> analysis.getId().equals(analysisId)).findFirst();
433 return opt.isPresent() ? opt.get() : null;
434 }
1d8ab692 435}
This page took 0.101347 seconds and 5 git commands to generate.