tmf: Make TmfEventFieldAspect independent of event content
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core.tests / stubs / org / eclipse / tracecompass / tmf / tests / stubs / trace / xml / TmfXmlTraceStub.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal
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
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.tests.stubs.trace.xml;
14
15 import java.io.File;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.URL;
19 import java.util.Collection;
20
21 import javax.xml.XMLConstants;
22 import javax.xml.transform.Source;
23 import javax.xml.transform.stream.StreamSource;
24 import javax.xml.validation.Schema;
25 import javax.xml.validation.SchemaFactory;
26 import javax.xml.validation.Validator;
27
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.Status;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.eclipse.osgi.util.NLS;
35 import org.eclipse.tracecompass.internal.tmf.core.Activator;
36 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
37 import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
38 import org.eclipse.tracecompass.tmf.core.event.ITmfEventType;
39 import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
40 import org.eclipse.tracecompass.tmf.core.event.TmfEventField;
41 import org.eclipse.tracecompass.tmf.core.event.TmfEventType;
42 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
43 import org.eclipse.tracecompass.tmf.core.event.aspect.TmfContentFieldAspect;
44 import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect;
45 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
46 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomEventContent;
47 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlEvent;
48 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTrace;
49 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTraceDefinition;
50 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
51 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
52 import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
53 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
54 import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
55 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
56 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
57 import org.xml.sax.SAXException;
58
59 import com.google.common.collect.ImmutableList;
60
61 /**
62 * An XML development trace using a custom XML trace definition and schema.
63 *
64 * This class will typically be used to build custom traces to unit test more
65 * complex functionalities like analyzes or to develop and test data-driven
66 * analyzes.
67 *
68 * This class wraps a custom XML trace and rewrites the returned events in the
69 * getNext() method so that event's fields are the ones defined in <field ... />
70 * elements instead of those defined in the custom XML parser. This way, each
71 * event can have a different set of fields. This class can, for example, mimic
72 * a CTF trace.
73 *
74 * @author Geneviève Bastien
75 */
76 public class TmfXmlTraceStub extends TmfTrace {
77
78 private static final String DEVELOPMENT_TRACE_PARSER_PATH = "TmfXmlDevelopmentTrace.xml"; //$NON-NLS-1$
79 private static final String DEVELOPMENT_TRACE_XSD = "TmfXmlDevelopmentTrace.xsd"; //$NON-NLS-1$
80 private static final String EMPTY = ""; //$NON-NLS-1$
81
82 /* XML elements and attributes names */
83 private static final String EVENT_NAME_FIELD = "Message"; //$NON-NLS-1$
84 private static final String FIELD_NAMES_FIELD = "fields"; //$NON-NLS-1$
85 private static final String VALUES_FIELD = "values"; //$NON-NLS-1$
86 private static final String TYPES_FIELD = "type"; //$NON-NLS-1$
87 private static final String VALUES_SEPARATOR = " \\| "; //$NON-NLS-1$
88 private static final String TYPE_INTEGER = "int"; //$NON-NLS-1$
89 private static final String TYPE_LONG = "long"; //$NON-NLS-1$
90 private static final String ASPECT_SPECIAL_EVENT = "set_aspects";
91 private static final String ASPECT_CPU = "cpu";
92
93 private static final Long SECONDS_TO_NS = 1000000000L;
94
95 private final CustomXmlTrace fTrace;
96
97 private Collection<ITmfEventAspect> fAspects;
98
99 /**
100 * Constructor. Constructs the custom XML trace with the appropriate
101 * definition.
102 */
103 public TmfXmlTraceStub() {
104
105 /* Load custom XML definition */
106 try (InputStream in = TmfXmlTraceStub.class.getResourceAsStream(DEVELOPMENT_TRACE_PARSER_PATH);) {
107 CustomXmlTraceDefinition[] definitions = CustomXmlTraceDefinition.loadAll(in);
108 if (definitions.length == 0) {
109 throw new IllegalStateException("The custom trace definition does not exist"); //$NON-NLS-1$
110 }
111 fTrace = new CustomXmlTrace(definitions[0]);
112 /* Deregister the custom XML trace */
113 TmfSignalManager.deregister(fTrace);
114
115 Collection<ITmfEventAspect> aspects = TmfTrace.BASE_ASPECTS;
116 fAspects = aspects;
117 } catch (IOException e) {
118 throw new IllegalStateException("Cannot open the trace parser for development traces"); //$NON-NLS-1$
119 }
120
121 }
122
123 @Override
124 public void initTrace(@Nullable IResource resource, @Nullable String path, @Nullable Class<? extends ITmfEvent> type) throws TmfTraceException {
125 super.initTrace(resource, path, type);
126 fTrace.initTrace(resource, path, type);
127 ITmfContext ctx;
128 /* Set the start and (current) end times for this trace */
129 ctx = seekEvent(0L);
130 if (ctx == null) {
131 return;
132 }
133 ITmfEvent event = getNext(ctx);
134 if (event != null) {
135 final ITmfTimestamp curTime = event.getTimestamp();
136 this.setStartTime(curTime);
137 this.setEndTime(curTime);
138 }
139 }
140
141 @Override
142 public @Nullable ITmfEvent parseEvent(@Nullable ITmfContext context) {
143 return fTrace.parseEvent(context);
144 }
145
146 @Override
147 public @Nullable ITmfLocation getCurrentLocation() {
148 return fTrace.getCurrentLocation();
149 }
150
151 @Override
152 public double getLocationRatio(@Nullable ITmfLocation location) {
153 return fTrace.getLocationRatio(location);
154 }
155
156 @Override
157 public @Nullable ITmfContext seekEvent(@Nullable ITmfLocation location) {
158 return fTrace.seekEvent(location);
159 }
160
161 @Override
162 public @Nullable ITmfContext seekEvent(double ratio) {
163 return fTrace.seekEvent(ratio);
164 }
165
166 @Override
167 public IStatus validate(@Nullable IProject project, @Nullable String path) {
168 File xmlFile = new File(path);
169 if (!xmlFile.exists() || !xmlFile.isFile() || !xmlFile.canRead()) {
170 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_FileNotFound, path));
171 }
172 /* Does the XML file validate with the XSD */
173 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
174 Source xmlSource = new StreamSource(xmlFile);
175
176 try {
177 URL url = TmfXmlTraceStub.class.getResource(DEVELOPMENT_TRACE_XSD);
178 Schema schema = schemaFactory.newSchema(url);
179
180 Validator validator = schema.newValidator();
181 validator.validate(xmlSource);
182 } catch (SAXException e) {
183 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_ValidationError, path), e);
184 } catch (IOException e) {
185 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.Messages.TmfDevelopmentTrace_IoError, path), e);
186 }
187 @SuppressWarnings("null")
188 @NonNull
189 IStatus status = Status.OK_STATUS;
190 return status;
191 }
192
193 private static String getStringValue(ITmfEventField content, String fieldName) {
194 ITmfEventField field = content.getField(fieldName);
195 if (field == null) {
196 return EMPTY;
197 }
198 Object val = field.getValue();
199 if (!(val instanceof String)) {
200 return EMPTY;
201 }
202 return (String) val;
203 }
204
205 @Override
206 public synchronized @Nullable ITmfEvent getNext(@Nullable ITmfContext context) {
207 if (context == null) {
208 return null;
209 }
210 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
211 CustomXmlEvent event = fTrace.getNext(context);
212 if (event == null) {
213 return null;
214 }
215
216 /* Translate the content of the event */
217 /* The "fields" field contains a | separated list of field names */
218 /* The "values" field contains a | separated list of field values */
219 /* the "type" field contains a | separated list of field types */
220 ITmfEventField content = event.getContent();
221 if (content == null) {
222 return null;
223 }
224
225 String fieldString = getStringValue(content, FIELD_NAMES_FIELD);
226 String valueString = getStringValue(content, VALUES_FIELD);
227 String typeString = getStringValue(content, TYPES_FIELD);
228
229 String[] fields = fieldString.split(VALUES_SEPARATOR);
230 String[] values = valueString.split(VALUES_SEPARATOR);
231 String[] types = typeString.split(VALUES_SEPARATOR);
232 ITmfEventField[] fieldsArray = new TmfEventField[fields.length];
233
234 for (int i = 0; i < fields.length; i++) {
235 String value = EMPTY;
236 if (values.length > i) {
237 value = values[i];
238 }
239 String type = null;
240 if (types.length > i) {
241 type = types[i];
242 }
243 Object val = value;
244 if (type != null) {
245 switch (type) {
246 case TYPE_INTEGER: {
247 try {
248 val = Integer.valueOf(value);
249 } catch (NumberFormatException e) {
250 Activator.logError(String.format("Get next XML event: cannot cast value %s to integer", value), e); //$NON-NLS-1$
251 val = 0;
252 }
253 break;
254 }
255 case TYPE_LONG: {
256 try {
257 val = Long.valueOf(value);
258 } catch (NumberFormatException e) {
259 Activator.logError(String.format("Get next XML event: cannot cast value %s to long", value), e); //$NON-NLS-1$
260 val = 0L;
261 }
262 break;
263 }
264 default:
265 break;
266 }
267 }
268 fieldsArray[i] = new TmfEventField(fields[i], val, null);
269 }
270
271 /* Generate the aspects for this trace if it is the aspects special event */
272 String eventName = getStringValue(content, EVENT_NAME_FIELD);
273 if (eventName.equals(ASPECT_SPECIAL_EVENT)) {
274 generateAspects(fieldsArray);
275 return getNext(context);
276 }
277
278 /* Create a new event with new fields and name */
279 ITmfEventType customEventType = event.getType();
280 TmfEventType eventType = new TmfEventType(eventName, customEventType.getRootField());
281 ITmfEventField eventFields = new CustomEventContent(content.getName(), content.getValue(), fieldsArray);
282 /*
283 * TODO: Timestamps for these traces are in nanos, but since the
284 * CustomXmlTrace does not support this format, the timestamp of the
285 * original is in second and we need to convert it. We should do that at
286 * the source when it is supported
287 */
288 ITmfTimestamp timestamp = new TmfNanoTimestamp(event.getTimestamp().getValue() / SECONDS_TO_NS);
289 TmfEvent newEvent = new TmfEvent(this, ITmfContext.UNKNOWN_RANK, timestamp, eventType, eventFields);
290 updateAttributes(savedContext, event.getTimestamp());
291 context.increaseRank();
292
293 return newEvent;
294 }
295
296 private void generateAspects(ITmfEventField[] fieldsArray) {
297 ImmutableList.Builder<ITmfEventAspect> builder = new ImmutableList.Builder<>();
298
299 /* Initialize the first default trace aspects */
300 builder.add(ITmfEventAspect.BaseAspects.TIMESTAMP);
301 builder.add(ITmfEventAspect.BaseAspects.EVENT_TYPE);
302
303 /* Add custom aspects in between */
304 for (ITmfEventField field : fieldsArray) {
305 String name = field.getName();
306 if (name == null) {
307 break;
308 }
309 final ITmfEventAspect aspect = new TmfContentFieldAspect(name, name);
310 if (name.equals(ASPECT_CPU)) {
311 builder.add(new TmfCpuAspect() {
312 @Override
313 public @Nullable Integer resolve(ITmfEvent event) {
314 Object result = aspect.resolve(event);
315 if (result instanceof Number) {
316 return ((Number) result).intValue();
317 }
318 return null;
319 }
320 });
321 } else {
322 builder.add(aspect);
323 }
324 }
325
326 /* Add the big content aspect */
327 builder.add(ITmfEventAspect.BaseAspects.CONTENTS);
328
329 @SuppressWarnings("null")
330 @NonNull Collection<ITmfEventAspect> aspectList = builder.build();
331 fAspects = aspectList;
332 }
333
334 @Override
335 public Iterable<ITmfEventAspect> getEventAspects() {
336 return fAspects;
337 }
338
339 }
This page took 0.037919 seconds and 5 git commands to generate.