Commit | Line | Data |
---|---|---|
be222f56 | 1 | /******************************************************************************* |
70b7bc9c | 2 | * Copyright (c) 2010, 2014 Ericsson |
be222f56 PT |
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 | * Patrick Tasse - Initial API and implementation | |
70b7bc9c | 11 | * Matthew Khouzam - Add support for default xml parsers |
be222f56 PT |
12 | *******************************************************************************/ |
13 | ||
47aafe74 | 14 | package org.eclipse.linuxtools.tmf.core.parsers.custom; |
be222f56 PT |
15 | |
16 | import java.io.ByteArrayInputStream; | |
17 | import java.io.File; | |
18 | import java.io.FileWriter; | |
19 | import java.io.IOException; | |
20 | import java.io.StringWriter; | |
21 | import java.util.ArrayList; | |
70b7bc9c MK |
22 | import java.util.Arrays; |
23 | import java.util.Comparator; | |
be222f56 | 24 | import java.util.List; |
70b7bc9c MK |
25 | import java.util.Set; |
26 | import java.util.TreeSet; | |
be222f56 PT |
27 | |
28 | import javax.xml.parsers.DocumentBuilder; | |
29 | import javax.xml.parsers.DocumentBuilderFactory; | |
30 | import javax.xml.parsers.ParserConfigurationException; | |
31 | import javax.xml.transform.OutputKeys; | |
32 | import javax.xml.transform.Transformer; | |
be222f56 PT |
33 | import javax.xml.transform.TransformerException; |
34 | import javax.xml.transform.TransformerFactory; | |
35 | import javax.xml.transform.TransformerFactoryConfigurationError; | |
36 | import javax.xml.transform.dom.DOMSource; | |
37 | import javax.xml.transform.stream.StreamResult; | |
38 | ||
70b7bc9c | 39 | import org.eclipse.core.runtime.Platform; |
47aafe74 AM |
40 | import org.eclipse.linuxtools.internal.tmf.core.Activator; |
41 | import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceType; | |
be222f56 PT |
42 | import org.w3c.dom.Document; |
43 | import org.w3c.dom.Element; | |
44 | import org.w3c.dom.Node; | |
45 | import org.w3c.dom.NodeList; | |
46 | import org.xml.sax.EntityResolver; | |
47 | import org.xml.sax.ErrorHandler; | |
48 | import org.xml.sax.InputSource; | |
49 | import org.xml.sax.SAXException; | |
50 | import org.xml.sax.SAXParseException; | |
51 | ||
a0a88f65 AM |
52 | /** |
53 | * Trace definition for custom XML traces. | |
54 | * | |
55 | * @author Patrick Tassé | |
47aafe74 | 56 | * @since 3.0 |
a0a88f65 | 57 | */ |
be222f56 PT |
58 | public class CustomXmlTraceDefinition extends CustomTraceDefinition { |
59 | ||
a0a88f65 AM |
60 | /** "ignore" tag */ |
61 | public static final String TAG_IGNORE = Messages.CustomXmlTraceDefinition_ignoreTag; | |
62 | ||
70b7bc9c MK |
63 | /** Name of the default XML definitions file */ |
64 | protected static final String CUSTOM_XML_TRACE_DEFINITIONS_DEFAULT_FILE_NAME = "custom_xml_default_parsers.xml"; //$NON-NLS-1$ | |
65 | ||
a0a88f65 | 66 | /** Name of the XML definitions file */ |
be222f56 | 67 | protected static final String CUSTOM_XML_TRACE_DEFINITIONS_FILE_NAME = "custom_xml_parsers.xml"; //$NON-NLS-1$ |
a0a88f65 | 68 | |
70b7bc9c MK |
69 | /** Path to the XML definitions file */ |
70 | protected static final String CUSTOM_XML_TRACE_DEFINITIONS_DEFAULT_PATH_NAME = | |
cb1cf0e8 MK |
71 | Platform.getInstallLocation().getURL().getPath() + "templates/org.eclipse.linuxtools.tmf.core/" + //$NON-NLS-1$ |
72 | CUSTOM_XML_TRACE_DEFINITIONS_DEFAULT_FILE_NAME; | |
70b7bc9c | 73 | |
a0a88f65 | 74 | /** Path to the XML definitions file */ |
be222f56 | 75 | protected static final String CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME = |
70b7bc9c | 76 | Activator.getDefault().getStateLocation().addTrailingSeparator().append(CUSTOM_XML_TRACE_DEFINITIONS_FILE_NAME).toString(); |
be222f56 | 77 | |
cb1cf0e8 MK |
78 | /** |
79 | * Legacy path to the XML definitions file (in the UI plug-in) TODO Remove | |
80 | * once we feel the transition phase is over. | |
81 | */ | |
03573754 AM |
82 | private static final String CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME_LEGACY = |
83 | Activator.getDefault().getStateLocation().removeLastSegments(1).addTrailingSeparator() | |
84 | .append("org.eclipse.linuxtools.tmf.ui") //$NON-NLS-1$ | |
85 | .append(CUSTOM_XML_TRACE_DEFINITIONS_FILE_NAME).toString(); | |
86 | ||
332527a4 | 87 | // TODO: These strings should not be externalized |
be222f56 PT |
88 | private static final String CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT = Messages.CustomXmlTraceDefinition_definitionRootElement; |
89 | private static final String DEFINITION_ELEMENT = Messages.CustomXmlTraceDefinition_definition; | |
332527a4 | 90 | private static final String CATEGORY_ATTRIBUTE = Messages.CustomXmlTraceDefinition_category; |
be222f56 PT |
91 | private static final String NAME_ATTRIBUTE = Messages.CustomXmlTraceDefinition_name; |
92 | private static final String LOG_ENTRY_ATTRIBUTE = Messages.CustomXmlTraceDefinition_logEntry; | |
93 | private static final String TIME_STAMP_OUTPUT_FORMAT_ELEMENT = Messages.CustomXmlTraceDefinition_timestampOutputFormat; | |
94 | private static final String INPUT_ELEMENT_ELEMENT = Messages.CustomXmlTraceDefinition_inputElement; | |
95 | private static final String ATTRIBUTE_ELEMENT = Messages.CustomXmlTraceDefinition_attribute; | |
96 | private static final String INPUT_DATA_ELEMENT = Messages.CustomXmlTraceDefinition_inputData; | |
97 | private static final String ACTION_ATTRIBUTE = Messages.CustomXmlTraceDefinition_action; | |
98 | private static final String FORMAT_ATTRIBUTE = Messages.CustomXmlTraceDefinition_format; | |
99 | private static final String OUTPUT_COLUMN_ELEMENT = Messages.CustomXmlTraceDefinition_outputColumn; | |
100 | ||
a0a88f65 | 101 | /** Top-level input element */ |
be222f56 PT |
102 | public InputElement rootInputElement; |
103 | ||
a0a88f65 AM |
104 | /** |
105 | * Default constructor | |
106 | */ | |
be222f56 | 107 | public CustomXmlTraceDefinition() { |
332527a4 | 108 | this(TmfTraceType.CUSTOM_XML_CATEGORY, "", null, new ArrayList<OutputColumn>(), ""); //$NON-NLS-1$ //$NON-NLS-2$ |
be222f56 PT |
109 | } |
110 | ||
a0a88f65 AM |
111 | /** |
112 | * Full constructor | |
113 | * | |
332527a4 PT |
114 | * @param traceType |
115 | * Name of the trace type | |
a0a88f65 AM |
116 | * @param rootElement |
117 | * The top-level XML element | |
118 | * @param outputs | |
119 | * The list of output columns | |
120 | * @param timeStampOutputFormat | |
121 | * The timestamp format to use | |
332527a4 | 122 | * @deprecated Use {@link #CustomXmlTraceDefinition(String, String, InputElement, List, String)} |
a0a88f65 | 123 | */ |
332527a4 PT |
124 | @Deprecated |
125 | public CustomXmlTraceDefinition(String traceType, InputElement rootElement, | |
a0a88f65 | 126 | List<OutputColumn> outputs, String timeStampOutputFormat) { |
332527a4 PT |
127 | this.definitionName = traceType; |
128 | this.rootInputElement = rootElement; | |
129 | this.outputs = outputs; | |
130 | this.timeStampOutputFormat = timeStampOutputFormat; | |
131 | } | |
132 | ||
133 | /** | |
134 | * Full constructor | |
135 | * | |
136 | * @param category | |
137 | * Category of the trace type | |
138 | * @param traceType | |
139 | * Name of the trace type | |
140 | * @param rootElement | |
141 | * The top-level XML element | |
142 | * @param outputs | |
143 | * The list of output columns | |
144 | * @param timeStampOutputFormat | |
145 | * The timestamp format to use | |
146 | * @since 3.1 | |
147 | */ | |
148 | public CustomXmlTraceDefinition(String category, String traceType, InputElement rootElement, | |
149 | List<OutputColumn> outputs, String timeStampOutputFormat) { | |
150 | this.categoryName = category; | |
151 | this.definitionName = traceType; | |
be222f56 PT |
152 | this.rootInputElement = rootElement; |
153 | this.outputs = outputs; | |
154 | this.timeStampOutputFormat = timeStampOutputFormat; | |
155 | } | |
156 | ||
a0a88f65 AM |
157 | /** |
158 | * Wrapper for input XML elements | |
159 | */ | |
be222f56 | 160 | public static class InputElement { |
a0a88f65 AM |
161 | |
162 | /** Name of the element */ | |
be222f56 | 163 | public String elementName; |
a0a88f65 AM |
164 | |
165 | /** Indicates if this is a log entry */ | |
be222f56 | 166 | public boolean logEntry; |
a0a88f65 AM |
167 | |
168 | /** Name of the input element */ | |
be222f56 | 169 | public String inputName; |
a0a88f65 AM |
170 | |
171 | /** Input action */ | |
be222f56 | 172 | public int inputAction; |
a0a88f65 AM |
173 | |
174 | /** Input format */ | |
be222f56 | 175 | public String inputFormat; |
a0a88f65 AM |
176 | |
177 | /** XML attributes of this element */ | |
be222f56 | 178 | public List<InputAttribute> attributes; |
a0a88f65 AM |
179 | |
180 | /** Parent element */ | |
be222f56 | 181 | public InputElement parentElement; |
a0a88f65 AM |
182 | |
183 | /** Following element in the file */ | |
be222f56 | 184 | public InputElement nextElement; |
a0a88f65 AM |
185 | |
186 | /** Child elements */ | |
be222f56 PT |
187 | public List<InputElement> childElements; |
188 | ||
a0a88f65 AM |
189 | /** |
190 | * Default (empty) constructor | |
191 | */ | |
70b7bc9c MK |
192 | public InputElement() { |
193 | } | |
be222f56 | 194 | |
a0a88f65 AM |
195 | /** |
196 | * Constructor | |
197 | * | |
198 | * @param elementName | |
199 | * Element name | |
200 | * @param logEntry | |
201 | * If this element is a log entry | |
202 | * @param inputName | |
203 | * Name of the the input | |
204 | * @param inputAction | |
205 | * Input action | |
206 | * @param inputFormat | |
207 | * Input format | |
208 | * @param attributes | |
209 | * XML attributes of this element | |
210 | */ | |
211 | public InputElement(String elementName, boolean logEntry, | |
212 | String inputName, int inputAction, String inputFormat, | |
213 | List<InputAttribute> attributes) { | |
be222f56 PT |
214 | this.elementName = elementName; |
215 | this.logEntry = logEntry; | |
216 | this.inputName = inputName; | |
217 | this.inputAction = inputAction; | |
218 | this.inputFormat = inputFormat; | |
219 | this.attributes = attributes; | |
220 | } | |
221 | ||
a0a88f65 AM |
222 | /** |
223 | * Add a XML attribute to the element | |
224 | * | |
225 | * @param attribute | |
226 | * The attribute to add | |
227 | */ | |
be222f56 PT |
228 | public void addAttribute(InputAttribute attribute) { |
229 | if (attributes == null) { | |
507b1336 | 230 | attributes = new ArrayList<>(1); |
be222f56 PT |
231 | } |
232 | attributes.add(attribute); | |
233 | } | |
234 | ||
a0a88f65 AM |
235 | /** |
236 | * Add a child element to this one. | |
237 | * | |
238 | * @param input | |
239 | * The input element to add as child | |
240 | */ | |
be222f56 PT |
241 | public void addChild(InputElement input) { |
242 | if (childElements == null) { | |
507b1336 | 243 | childElements = new ArrayList<>(1); |
be222f56 PT |
244 | } else if (childElements.size() > 0) { |
245 | InputElement last = childElements.get(childElements.size() - 1); | |
246 | last.nextElement = input; | |
247 | } | |
248 | childElements.add(input); | |
249 | input.parentElement = this; | |
250 | } | |
251 | ||
a0a88f65 AM |
252 | /** |
253 | * Set the following input element. | |
254 | * | |
255 | * @param input | |
256 | * The input element to add as next element | |
257 | */ | |
be222f56 PT |
258 | public void addNext(InputElement input) { |
259 | if (parentElement != null) { | |
260 | int index = parentElement.childElements.indexOf(this); | |
261 | parentElement.childElements.add(index + 1, input); | |
262 | InputElement next = nextElement; | |
263 | nextElement = input; | |
264 | input.nextElement = next; | |
265 | } | |
266 | input.parentElement = this.parentElement; | |
267 | } | |
268 | ||
a0a88f65 AM |
269 | /** |
270 | * Move this element up in its parent's list of children. | |
271 | */ | |
be222f56 PT |
272 | public void moveUp() { |
273 | if (parentElement != null) { | |
274 | int index = parentElement.childElements.indexOf(this); | |
275 | if (index > 0) { | |
70b7bc9c | 276 | parentElement.childElements.add(index - 1, parentElement.childElements.remove(index)); |
be222f56 PT |
277 | parentElement.childElements.get(index).nextElement = nextElement; |
278 | nextElement = parentElement.childElements.get(index); | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
a0a88f65 AM |
283 | /** |
284 | * Move this element down in its parent's list of children. | |
285 | */ | |
be222f56 PT |
286 | public void moveDown() { |
287 | if (parentElement != null) { | |
288 | int index = parentElement.childElements.indexOf(this); | |
289 | if (index < parentElement.childElements.size() - 1) { | |
70b7bc9c | 290 | parentElement.childElements.add(index + 1, parentElement.childElements.remove(index)); |
be222f56 PT |
291 | nextElement = parentElement.childElements.get(index).nextElement; |
292 | parentElement.childElements.get(index).nextElement = this; | |
293 | } | |
294 | } | |
295 | } | |
296 | ||
297 | } | |
298 | ||
a0a88f65 AM |
299 | /** |
300 | * Wrapper for XML element attributes | |
301 | */ | |
be222f56 | 302 | public static class InputAttribute { |
a0a88f65 AM |
303 | |
304 | /** Name of the XML attribute */ | |
be222f56 | 305 | public String attributeName; |
a0a88f65 AM |
306 | |
307 | /** Input name */ | |
be222f56 | 308 | public String inputName; |
a0a88f65 AM |
309 | |
310 | /** Input action */ | |
be222f56 | 311 | public int inputAction; |
a0a88f65 AM |
312 | |
313 | /** Input format */ | |
be222f56 PT |
314 | public String inputFormat; |
315 | ||
a0a88f65 AM |
316 | /** |
317 | * Default (empty) constructor | |
318 | */ | |
70b7bc9c MK |
319 | public InputAttribute() { |
320 | } | |
be222f56 | 321 | |
a0a88f65 AM |
322 | /** |
323 | * Constructor | |
324 | * | |
325 | * @param attributeName | |
326 | * Name of the XML attribute | |
327 | * @param inputName | |
328 | * Input name | |
329 | * @param inputAction | |
330 | * Input action | |
331 | * @param inputFormat | |
332 | * Input format | |
333 | */ | |
334 | public InputAttribute(String attributeName, String inputName, | |
335 | int inputAction, String inputFormat) { | |
be222f56 PT |
336 | this.attributeName = attributeName; |
337 | this.inputName = inputName; | |
338 | this.inputAction = inputAction; | |
339 | this.inputFormat = inputFormat; | |
340 | } | |
341 | } | |
342 | ||
a0a88f65 | 343 | @Override |
be222f56 PT |
344 | public void save() { |
345 | save(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME); | |
346 | } | |
347 | ||
70b7bc9c | 348 | @Override |
be222f56 PT |
349 | public void save(String path) { |
350 | try { | |
351 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
352 | DocumentBuilder db = dbf.newDocumentBuilder(); | |
353 | ||
354 | // The following allows xml parsing without access to the dtd | |
cb1cf0e8 | 355 | db.setEntityResolver(createEmptyEntityResolver()); |
be222f56 PT |
356 | |
357 | // The following catches xml parsing exceptions | |
cb1cf0e8 | 358 | db.setErrorHandler(createErrorHandler()); |
be222f56 PT |
359 | |
360 | Document doc = null; | |
361 | File file = new File(path); | |
362 | if (file.canRead()) { | |
363 | doc = db.parse(file); | |
70b7bc9c | 364 | if (!doc.getDocumentElement().getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) { |
be222f56 PT |
365 | return; |
366 | } | |
367 | } else { | |
368 | doc = db.newDocument(); | |
369 | Node node = doc.createElement(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT); | |
370 | doc.appendChild(node); | |
371 | } | |
372 | ||
373 | Element root = doc.getDocumentElement(); | |
374 | ||
332527a4 PT |
375 | Element oldDefinitionElement = findDefinitionElement(root, categoryName, definitionName); |
376 | if (oldDefinitionElement != null) { | |
377 | root.removeChild(oldDefinitionElement); | |
be222f56 PT |
378 | } |
379 | Element definitionElement = doc.createElement(DEFINITION_ELEMENT); | |
380 | root.appendChild(definitionElement); | |
332527a4 | 381 | definitionElement.setAttribute(CATEGORY_ATTRIBUTE, categoryName); |
be222f56 PT |
382 | definitionElement.setAttribute(NAME_ATTRIBUTE, definitionName); |
383 | ||
384 | Element formatElement = doc.createElement(TIME_STAMP_OUTPUT_FORMAT_ELEMENT); | |
385 | definitionElement.appendChild(formatElement); | |
386 | formatElement.appendChild(doc.createTextNode(timeStampOutputFormat)); | |
387 | ||
388 | if (rootInputElement != null) { | |
389 | definitionElement.appendChild(createInputElementElement(rootInputElement, doc)); | |
390 | } | |
391 | ||
392 | if (outputs != null) { | |
393 | for (OutputColumn output : outputs) { | |
394 | Element outputColumnElement = doc.createElement(OUTPUT_COLUMN_ELEMENT); | |
395 | definitionElement.appendChild(outputColumnElement); | |
396 | outputColumnElement.setAttribute(NAME_ATTRIBUTE, output.name); | |
397 | } | |
398 | } | |
399 | ||
400 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); | |
401 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ | |
402 | ||
70b7bc9c | 403 | // initialize StreamResult with File object to save to file |
be222f56 PT |
404 | StreamResult result = new StreamResult(new StringWriter()); |
405 | DOMSource source = new DOMSource(doc); | |
406 | transformer.transform(source, result); | |
407 | String xmlString = result.getWriter().toString(); | |
408 | ||
507b1336 AM |
409 | try (FileWriter writer = new FileWriter(file);) { |
410 | writer.write(xmlString); | |
411 | } | |
52885aeb | 412 | |
4b3b667b | 413 | TmfTraceType.addCustomTraceType(CustomXmlTrace.class, categoryName, definitionName); |
52885aeb | 414 | |
cb1cf0e8 | 415 | } catch (ParserConfigurationException | TransformerFactoryConfigurationError | TransformerException | IOException | SAXException e) { |
47aafe74 | 416 | Activator.logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$ |
be222f56 PT |
417 | } |
418 | } | |
419 | ||
420 | private Element createInputElementElement(InputElement inputElement, Document doc) { | |
421 | Element inputElementElement = doc.createElement(INPUT_ELEMENT_ELEMENT); | |
422 | inputElementElement.setAttribute(NAME_ATTRIBUTE, inputElement.elementName); | |
423 | ||
424 | if (inputElement.logEntry) { | |
425 | inputElementElement.setAttribute(LOG_ENTRY_ATTRIBUTE, Boolean.toString(inputElement.logEntry)); | |
426 | } | |
427 | ||
428 | if (inputElement.parentElement != null) { | |
429 | Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT); | |
430 | inputElementElement.appendChild(inputDataElement); | |
431 | inputDataElement.setAttribute(NAME_ATTRIBUTE, inputElement.inputName); | |
432 | inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(inputElement.inputAction)); | |
433 | if (inputElement.inputFormat != null) { | |
434 | inputDataElement.setAttribute(FORMAT_ATTRIBUTE, inputElement.inputFormat); | |
435 | } | |
436 | } | |
437 | ||
438 | if (inputElement.attributes != null) { | |
439 | for (InputAttribute attribute : inputElement.attributes) { | |
440 | Element inputAttributeElement = doc.createElement(ATTRIBUTE_ELEMENT); | |
441 | inputElementElement.appendChild(inputAttributeElement); | |
442 | inputAttributeElement.setAttribute(NAME_ATTRIBUTE, attribute.attributeName); | |
443 | Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT); | |
444 | inputAttributeElement.appendChild(inputDataElement); | |
445 | inputDataElement.setAttribute(NAME_ATTRIBUTE, attribute.inputName); | |
446 | inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(attribute.inputAction)); | |
447 | if (attribute.inputFormat != null) { | |
448 | inputDataElement.setAttribute(FORMAT_ATTRIBUTE, attribute.inputFormat); | |
449 | } | |
450 | } | |
451 | } | |
452 | ||
453 | if (inputElement.childElements != null) { | |
454 | for (InputElement childInputElement : inputElement.childElements) { | |
455 | inputElementElement.appendChild(createInputElementElement(childInputElement, doc)); | |
456 | } | |
457 | } | |
458 | ||
459 | return inputElementElement; | |
460 | } | |
461 | ||
a0a88f65 AM |
462 | /** |
463 | * Load all the XML trace definitions in the default definitions file. | |
464 | * | |
465 | * @return The loaded trace definitions | |
466 | */ | |
be222f56 | 467 | public static CustomXmlTraceDefinition[] loadAll() { |
03573754 AM |
468 | File defaultFile = new File(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME); |
469 | File legacyFile = new File(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME_LEGACY); | |
470 | ||
471 | /* | |
472 | * If there is no file at the expected location, check the legacy | |
473 | * location instead. | |
474 | */ | |
475 | if (!defaultFile.exists() && legacyFile.exists()) { | |
476 | CustomXmlTraceDefinition[] oldDefs = loadAll(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME_LEGACY); | |
477 | for (CustomXmlTraceDefinition def : oldDefs) { | |
478 | /* Save in the new location */ | |
479 | def.save(); | |
480 | } | |
481 | } | |
482 | ||
70b7bc9c | 483 | Set<CustomXmlTraceDefinition> defs = new TreeSet<>(new Comparator<CustomXmlTraceDefinition>() { |
70b7bc9c MK |
484 | @Override |
485 | public int compare(CustomXmlTraceDefinition o1, CustomXmlTraceDefinition o2) { | |
c22ca172 PT |
486 | int result = o1.categoryName.compareTo(o2.categoryName); |
487 | if (result != 0) { | |
488 | return result; | |
489 | } | |
70b7bc9c MK |
490 | return o1.definitionName.compareTo(o2.definitionName); |
491 | } | |
492 | }); | |
493 | defs.addAll(Arrays.asList(loadAll(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME))); | |
494 | defs.addAll(Arrays.asList(loadAll(CUSTOM_XML_TRACE_DEFINITIONS_DEFAULT_PATH_NAME))); | |
495 | return defs.toArray(new CustomXmlTraceDefinition[0]); | |
be222f56 PT |
496 | } |
497 | ||
a0a88f65 AM |
498 | /** |
499 | * Load all the XML trace definitions in the given definitions file. | |
500 | * | |
501 | * @param path | |
502 | * Path to the definitions file to load | |
503 | * @return The loaded trace definitions | |
504 | */ | |
be222f56 PT |
505 | public static CustomXmlTraceDefinition[] loadAll(String path) { |
506 | try { | |
507 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
508 | DocumentBuilder db = dbf.newDocumentBuilder(); | |
509 | ||
510 | // The following allows xml parsing without access to the dtd | |
cb1cf0e8 | 511 | db.setEntityResolver(createEmptyEntityResolver()); |
be222f56 PT |
512 | |
513 | // The following catches xml parsing exceptions | |
cb1cf0e8 | 514 | db.setErrorHandler(createErrorHandler()); |
be222f56 PT |
515 | |
516 | File file = new File(path); | |
517 | if (!file.canRead()) { | |
518 | return new CustomXmlTraceDefinition[0]; | |
519 | } | |
520 | Document doc = db.parse(file); | |
521 | ||
522 | Element root = doc.getDocumentElement(); | |
70b7bc9c | 523 | if (!root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) { |
be222f56 PT |
524 | return new CustomXmlTraceDefinition[0]; |
525 | } | |
526 | ||
507b1336 | 527 | ArrayList<CustomXmlTraceDefinition> defList = new ArrayList<>(); |
be222f56 PT |
528 | NodeList nodeList = root.getChildNodes(); |
529 | for (int i = 0; i < nodeList.getLength(); i++) { | |
530 | Node node = nodeList.item(i); | |
531 | if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) { | |
532 | CustomXmlTraceDefinition def = extractDefinition((Element) node); | |
533 | if (def != null) { | |
534 | defList.add(def); | |
535 | } | |
536 | } | |
537 | } | |
538 | return defList.toArray(new CustomXmlTraceDefinition[0]); | |
cb1cf0e8 | 539 | } catch (ParserConfigurationException | SAXException | IOException e) { |
47aafe74 | 540 | Activator.logError("Error loading all in CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$ |
be222f56 PT |
541 | } |
542 | return new CustomXmlTraceDefinition[0]; | |
543 | } | |
544 | ||
a0a88f65 AM |
545 | /** |
546 | * Load the given trace definition. | |
547 | * | |
548 | * @param definitionName | |
549 | * Name of the XML trace definition to load | |
550 | * @return The loaded trace definition | |
332527a4 | 551 | * @deprecated Use {@link #load(String, String)} |
a0a88f65 | 552 | */ |
332527a4 | 553 | @Deprecated |
be222f56 | 554 | public static CustomXmlTraceDefinition load(String definitionName) { |
332527a4 PT |
555 | return load(TmfTraceType.CUSTOM_XML_CATEGORY, definitionName); |
556 | } | |
557 | ||
558 | /** | |
559 | * Load the given trace definition. | |
560 | * | |
561 | * @param categoryName | |
562 | * Category of the definition to load | |
563 | * @param definitionName | |
564 | * Name of the XML trace definition to load | |
565 | * @return The loaded trace definition | |
566 | * @since 3.1 | |
567 | */ | |
568 | public static CustomXmlTraceDefinition load(String categoryName, String definitionName) { | |
be222f56 PT |
569 | try { |
570 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
571 | DocumentBuilder db = dbf.newDocumentBuilder(); | |
572 | ||
573 | // The following allows xml parsing without access to the dtd | |
a0a88f65 | 574 | EntityResolver resolver = new EntityResolver() { |
be222f56 | 575 | @Override |
a0a88f65 | 576 | public InputSource resolveEntity(String publicId, String systemId) { |
be222f56 PT |
577 | String empty = ""; //$NON-NLS-1$ |
578 | ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes()); | |
579 | return new InputSource(bais); | |
580 | } | |
581 | }; | |
582 | db.setEntityResolver(resolver); | |
583 | ||
584 | // The following catches xml parsing exceptions | |
a0a88f65 | 585 | db.setErrorHandler(new ErrorHandler() { |
be222f56 | 586 | @Override |
70b7bc9c MK |
587 | public void error(SAXParseException saxparseexception) throws SAXException { |
588 | } | |
a0a88f65 | 589 | |
be222f56 | 590 | @Override |
70b7bc9c MK |
591 | public void warning(SAXParseException saxparseexception) throws SAXException { |
592 | } | |
a0a88f65 | 593 | |
be222f56 | 594 | @Override |
a0a88f65 | 595 | public void fatalError(SAXParseException saxparseexception) throws SAXException { |
be222f56 | 596 | throw saxparseexception; |
a0a88f65 AM |
597 | } |
598 | }); | |
be222f56 | 599 | |
332527a4 | 600 | CustomXmlTraceDefinition value = lookupXmlDefinition(categoryName, definitionName, db, CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME); |
70b7bc9c | 601 | if (value == null) { |
332527a4 | 602 | value = lookupXmlDefinition(categoryName, definitionName, db, CUSTOM_XML_TRACE_DEFINITIONS_DEFAULT_PATH_NAME); |
be222f56 | 603 | } |
70b7bc9c | 604 | return value; |
cb1cf0e8 | 605 | } catch (ParserConfigurationException | SAXException | IOException e) { |
47aafe74 | 606 | Activator.logError("Error loading CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$ |
be222f56 PT |
607 | } |
608 | return null; | |
609 | } | |
610 | ||
332527a4 | 611 | private static CustomXmlTraceDefinition lookupXmlDefinition(String categoryName, String definitionName, DocumentBuilder db, String source) throws SAXException, IOException { |
70b7bc9c MK |
612 | File file = new File(source); |
613 | if (!file.exists()) { | |
614 | return null; | |
615 | } | |
616 | ||
617 | Document doc = db.parse(file); | |
618 | ||
619 | Element root = doc.getDocumentElement(); | |
620 | if (!root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) { | |
621 | return null; | |
622 | } | |
623 | ||
332527a4 PT |
624 | Element definitionElement = findDefinitionElement(root, categoryName, definitionName); |
625 | if (definitionElement != null) { | |
626 | return extractDefinition(definitionElement); | |
627 | } | |
628 | return null; | |
629 | } | |
630 | ||
631 | private static Element findDefinitionElement(Element root, String categoryName, String definitionName) { | |
70b7bc9c MK |
632 | NodeList nodeList = root.getChildNodes(); |
633 | for (int i = 0; i < nodeList.getLength(); i++) { | |
634 | Node node = nodeList.item(i); | |
332527a4 PT |
635 | if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) { |
636 | Element element = (Element) node; | |
637 | String categoryAttribute = element.getAttribute(CATEGORY_ATTRIBUTE); | |
638 | if (categoryAttribute.isEmpty()) { | |
639 | categoryAttribute = TmfTraceType.CUSTOM_XML_CATEGORY; | |
640 | } | |
641 | String nameAttribute = element.getAttribute(NAME_ATTRIBUTE); | |
642 | if (categoryName.equals(categoryAttribute) && | |
643 | definitionName.equals(nameAttribute)) { | |
644 | return element; | |
645 | } | |
70b7bc9c MK |
646 | } |
647 | } | |
648 | return null; | |
649 | } | |
650 | ||
a0a88f65 AM |
651 | /** |
652 | * Extract a trace definition from an XML element. | |
653 | * | |
654 | * @param definitionElement | |
655 | * Definition element | |
656 | * @return The extracted trace definition | |
657 | */ | |
be222f56 PT |
658 | public static CustomXmlTraceDefinition extractDefinition(Element definitionElement) { |
659 | CustomXmlTraceDefinition def = new CustomXmlTraceDefinition(); | |
660 | ||
332527a4 PT |
661 | def.categoryName = definitionElement.getAttribute(CATEGORY_ATTRIBUTE); |
662 | if (def.categoryName.isEmpty()) { | |
663 | def.categoryName = TmfTraceType.CUSTOM_XML_CATEGORY; | |
664 | } | |
be222f56 | 665 | def.definitionName = definitionElement.getAttribute(NAME_ATTRIBUTE); |
332527a4 | 666 | if (def.definitionName.isEmpty()) { |
be222f56 PT |
667 | return null; |
668 | } | |
669 | ||
670 | NodeList nodeList = definitionElement.getChildNodes(); | |
671 | for (int i = 0; i < nodeList.getLength(); i++) { | |
672 | Node node = nodeList.item(i); | |
673 | String nodeName = node.getNodeName(); | |
674 | if (nodeName.equals(TIME_STAMP_OUTPUT_FORMAT_ELEMENT)) { | |
675 | Element formatElement = (Element) node; | |
676 | def.timeStampOutputFormat = formatElement.getTextContent(); | |
677 | } else if (nodeName.equals(INPUT_ELEMENT_ELEMENT)) { | |
678 | InputElement inputElement = extractInputElement((Element) node); | |
679 | if (inputElement != null) { | |
680 | if (def.rootInputElement == null) { | |
681 | def.rootInputElement = inputElement; | |
682 | } else { | |
683 | return null; | |
684 | } | |
685 | } | |
686 | } else if (nodeName.equals(OUTPUT_COLUMN_ELEMENT)) { | |
687 | Element outputColumnElement = (Element) node; | |
688 | OutputColumn outputColumn = new OutputColumn(); | |
689 | outputColumn.name = outputColumnElement.getAttribute(NAME_ATTRIBUTE); | |
690 | def.outputs.add(outputColumn); | |
691 | } | |
692 | } | |
693 | return def; | |
694 | } | |
695 | ||
696 | private static InputElement extractInputElement(Element inputElementElement) { | |
697 | InputElement inputElement = new InputElement(); | |
698 | inputElement.elementName = inputElementElement.getAttribute(NAME_ATTRIBUTE); | |
699 | inputElement.logEntry = (Boolean.toString(true).equals(inputElementElement.getAttribute(LOG_ENTRY_ATTRIBUTE))) ? true : false; | |
700 | NodeList nodeList = inputElementElement.getChildNodes(); | |
701 | for (int i = 0; i < nodeList.getLength(); i++) { | |
702 | Node node = nodeList.item(i); | |
703 | String nodeName = node.getNodeName(); | |
704 | if (nodeName.equals(INPUT_DATA_ELEMENT)) { | |
705 | Element inputDataElement = (Element) node; | |
706 | inputElement.inputName = inputDataElement.getAttribute(NAME_ATTRIBUTE); | |
707 | inputElement.inputAction = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE)); | |
708 | inputElement.inputFormat = inputDataElement.getAttribute(FORMAT_ATTRIBUTE); | |
709 | } else if (nodeName.equals(ATTRIBUTE_ELEMENT)) { | |
710 | Element attributeElement = (Element) node; | |
711 | InputAttribute attribute = new InputAttribute(); | |
712 | attribute.attributeName = attributeElement.getAttribute(NAME_ATTRIBUTE); | |
713 | NodeList attributeNodeList = attributeElement.getChildNodes(); | |
714 | for (int j = 0; j < attributeNodeList.getLength(); j++) { | |
715 | Node attributeNode = attributeNodeList.item(j); | |
716 | String attributeNodeName = attributeNode.getNodeName(); | |
717 | if (attributeNodeName.equals(INPUT_DATA_ELEMENT)) { | |
718 | Element inputDataElement = (Element) attributeNode; | |
719 | attribute.inputName = inputDataElement.getAttribute(NAME_ATTRIBUTE); | |
720 | attribute.inputAction = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE)); | |
721 | attribute.inputFormat = inputDataElement.getAttribute(FORMAT_ATTRIBUTE); | |
722 | } | |
723 | } | |
724 | inputElement.addAttribute(attribute); | |
725 | } else if (nodeName.equals(INPUT_ELEMENT_ELEMENT)) { | |
726 | Element childInputElementElement = (Element) node; | |
727 | InputElement childInputElement = extractInputElement(childInputElementElement); | |
728 | if (childInputElement != null) { | |
729 | inputElement.addChild(childInputElement); | |
730 | } | |
731 | } | |
732 | } | |
733 | return inputElement; | |
734 | } | |
735 | ||
a0a88f65 | 736 | /** |
332527a4 | 737 | * Delete a definition from the currently loaded ones. |
a0a88f65 AM |
738 | * |
739 | * @param definitionName | |
332527a4 PT |
740 | * The name of the definition to delete |
741 | * @deprecated Use {@link #delete(String, String)} | |
a0a88f65 | 742 | */ |
332527a4 | 743 | @Deprecated |
be222f56 | 744 | public static void delete(String definitionName) { |
332527a4 PT |
745 | delete(TmfTraceType.CUSTOM_XML_CATEGORY, definitionName); |
746 | } | |
747 | ||
748 | /** | |
749 | * Delete a definition from the currently loaded ones. | |
750 | * | |
751 | * @param categoryName | |
752 | * The category of the definition to delete | |
753 | * @param definitionName | |
754 | * The name of the definition to delete | |
755 | * @since 3.1 | |
756 | */ | |
757 | public static void delete(String categoryName, String definitionName) { | |
be222f56 PT |
758 | try { |
759 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |
760 | DocumentBuilder db = dbf.newDocumentBuilder(); | |
761 | ||
762 | // The following allows xml parsing without access to the dtd | |
a0a88f65 | 763 | EntityResolver resolver = new EntityResolver() { |
be222f56 | 764 | @Override |
a0a88f65 | 765 | public InputSource resolveEntity(String publicId, String systemId) { |
be222f56 PT |
766 | String empty = ""; //$NON-NLS-1$ |
767 | ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes()); | |
768 | return new InputSource(bais); | |
769 | } | |
770 | }; | |
771 | db.setEntityResolver(resolver); | |
772 | ||
773 | // The following catches xml parsing exceptions | |
a0a88f65 | 774 | db.setErrorHandler(new ErrorHandler() { |
be222f56 | 775 | @Override |
70b7bc9c MK |
776 | public void error(SAXParseException saxparseexception) throws SAXException { |
777 | } | |
a0a88f65 | 778 | |
be222f56 | 779 | @Override |
70b7bc9c MK |
780 | public void warning(SAXParseException saxparseexception) throws SAXException { |
781 | } | |
a0a88f65 | 782 | |
be222f56 | 783 | @Override |
a0a88f65 | 784 | public void fatalError(SAXParseException saxparseexception) throws SAXException { |
be222f56 | 785 | throw saxparseexception; |
a0a88f65 AM |
786 | } |
787 | }); | |
be222f56 PT |
788 | |
789 | File file = new File(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME); | |
790 | Document doc = db.parse(file); | |
791 | ||
792 | Element root = doc.getDocumentElement(); | |
70b7bc9c | 793 | if (!root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) { |
be222f56 PT |
794 | return; |
795 | } | |
796 | ||
332527a4 PT |
797 | Element definitionElement = findDefinitionElement(root, categoryName, definitionName); |
798 | if (definitionElement != null) { | |
799 | root.removeChild(definitionElement); | |
be222f56 PT |
800 | } |
801 | ||
802 | Transformer transformer = TransformerFactory.newInstance().newTransformer(); | |
803 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ | |
804 | ||
70b7bc9c | 805 | // initialize StreamResult with File object to save to file |
be222f56 PT |
806 | StreamResult result = new StreamResult(new StringWriter()); |
807 | DOMSource source = new DOMSource(doc); | |
808 | transformer.transform(source, result); | |
809 | String xmlString = result.getWriter().toString(); | |
810 | ||
507b1336 AM |
811 | try (FileWriter writer = new FileWriter(file);) { |
812 | writer.write(xmlString); | |
813 | } | |
52885aeb | 814 | |
4b3b667b | 815 | TmfTraceType.removeCustomTraceType(CustomXmlTrace.class, categoryName, definitionName); |
0621dbae | 816 | // Check if default definition needs to be reloaded |
4b3b667b | 817 | TmfTraceType.addCustomTraceType(CustomXmlTrace.class, categoryName, definitionName); |
52885aeb | 818 | |
cb1cf0e8 | 819 | } catch (ParserConfigurationException | SAXException | IOException | TransformerFactoryConfigurationError | TransformerException e) { |
47aafe74 | 820 | Activator.logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$ |
be222f56 PT |
821 | } |
822 | } | |
823 | } |