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