xml: bug 497272 Populate views from built-in XML files
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.analysis.xml.core / src / org / eclipse / tracecompass / internal / tmf / analysis / xml / core / module / XmlUtils.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2016 É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 API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
16
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.net.URL;
23 import java.nio.channels.FileChannel;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.xml.XMLConstants;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.transform.Source;
34 import javax.xml.transform.stream.StreamSource;
35 import javax.xml.validation.Schema;
36 import javax.xml.validation.SchemaFactory;
37 import javax.xml.validation.Validator;
38
39 import org.eclipse.core.runtime.FileLocator;
40 import org.eclipse.core.runtime.IConfigurationElement;
41 import org.eclipse.core.runtime.IPath;
42 import org.eclipse.core.runtime.ISafeRunnable;
43 import org.eclipse.core.runtime.IStatus;
44 import org.eclipse.core.runtime.Path;
45 import org.eclipse.core.runtime.Platform;
46 import org.eclipse.core.runtime.SafeRunner;
47 import org.eclipse.core.runtime.Status;
48 import org.eclipse.jdt.annotation.NonNull;
49 import org.eclipse.jdt.annotation.Nullable;
50 import org.eclipse.osgi.util.NLS;
51 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.Activator;
52 import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings;
53 import org.osgi.framework.Bundle;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.Element;
56 import org.w3c.dom.Node;
57 import org.w3c.dom.NodeList;
58 import org.xml.sax.SAXException;
59 import org.xml.sax.SAXParseException;
60
61 /**
62 * Class containing some utilities for the XML plug-in packages: for example, it
63 * manages the XML files and validates them
64 *
65 * @author Geneviève Bastien
66 */
67 public class XmlUtils {
68
69 /** Sub-directory of the plug-in where XML files are stored */
70 private static final String XML_DIRECTORY = "xml_files"; //$NON-NLS-1$
71
72 /** Name of the XSD schema file */
73 private static final String XSD = "xmlDefinition.xsd"; //$NON-NLS-1$
74
75 /** Extension point ID and attributes */
76 private static final String TMF_XML_BUILTIN_ID = "org.eclipse.linuxtools.tmf.analysis.xml.core.files"; //$NON-NLS-1$
77 private static final String XML_FILE_ELEMENT = "xmlfile"; //$NON-NLS-1$
78 private static final String XML_FILE_ATTRIB = "file"; //$NON-NLS-1$
79
80 /**
81 * Extension for XML files
82 */
83 public static final String XML_EXTENSION = "xml"; //$NON-NLS-1$
84
85 /** Make this class non-instantiable */
86 private XmlUtils() {
87
88 }
89
90 /**
91 * Get the path where the XML files are stored. Create it if it does not
92 * exist
93 *
94 * @return path to XML files
95 */
96 public static IPath getXmlFilesPath() {
97 IPath path = Activator.getDefault().getStateLocation();
98 path = path.addTrailingSeparator().append(XML_DIRECTORY);
99
100 /* Check if directory exists, otherwise create it */
101 File dir = path.toFile();
102 if (!dir.exists() || !dir.isDirectory()) {
103 dir.mkdirs();
104 }
105
106 return path;
107 }
108
109 /**
110 * Validate the XML file input with the XSD schema
111 *
112 * @param xmlFile
113 * XML file to validate
114 * @return True if the XML validates
115 */
116 public static IStatus xmlValidate(File xmlFile) {
117 URL url = XmlUtils.class.getResource(XSD);
118 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
119 Source xmlSource = new StreamSource(xmlFile);
120 try {
121 Schema schema = schemaFactory.newSchema(url);
122 Validator validator = schema.newValidator();
123 validator.validate(xmlSource);
124 } catch (SAXParseException e) {
125 String error = NLS.bind(Messages.XmlUtils_XmlParseError, e.getLineNumber(), e.getLocalizedMessage());
126 Activator.logError(error);
127 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, error, e);
128 } catch (SAXException e) {
129 String error = NLS.bind(Messages.XmlUtils_XmlValidationError, e.getLocalizedMessage());
130 Activator.logError(error);
131 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, error, e);
132 } catch (IOException e) {
133 String error = Messages.XmlUtils_XmlValidateError;
134 Activator.logError("IO exception occurred", e); //$NON-NLS-1$
135 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, error, e);
136 }
137 return Status.OK_STATUS;
138 }
139
140 /**
141 * Adds an XML file to the plugin's path. The XML file should have been
142 * validated using the {@link XmlUtils#xmlValidate(File)} method before
143 * calling this method.
144 *
145 * @param fromFile
146 * The XML file to add
147 * @return Whether the file was successfully added
148 */
149 public static IStatus addXmlFile(File fromFile) {
150
151 /* Copy file to path */
152 File toFile = getXmlFilesPath().addTrailingSeparator().append(fromFile.getName()).toFile();
153
154 return copyXmlFile(fromFile, toFile);
155 }
156
157 /**
158 * List all files under the XML analysis files path. It returns a map where
159 * the key is the file name.
160 *
161 * @return A map with all the XML analysis files
162 */
163 public static synchronized @NonNull Map<String, File> listFiles() {
164 IPath pathToFiles = XmlUtils.getXmlFilesPath();
165 File folder = pathToFiles.toFile();
166
167 Map<String, File> fileMap = new HashMap<>();
168 if ((folder.isDirectory() && folder.exists())) {
169 File[] listOfFiles = folder.listFiles();
170 if (listOfFiles != null) {
171 for (File file : listOfFiles) {
172 IPath path = new Path(file.getName());
173 if (path.getFileExtension().equals(XML_EXTENSION)) {
174 fileMap.put(file.getName(), file);
175 }
176 }
177 } else {
178 Activator.logError("I/O error occured while accessing files in folder " + folder.getPath()); //$NON-NLS-1$
179 }
180 }
181 return Collections.unmodifiableMap(fileMap);
182 }
183
184 /**
185 * List all files advertised through the builtin extension point. It returns a map where the key is the file name.
186 *
187 * @return A map with all the XMl analysis builtin files
188 */
189 public static synchronized @NonNull Map<String, IPath> listBuiltinFiles() {
190 /* Get the XML files advertised through the extension point */
191 IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(TMF_XML_BUILTIN_ID);
192 Map<String, IPath> map = new HashMap<>();
193 for (IConfigurationElement element : elements) {
194 if (element.getName().equals(XML_FILE_ELEMENT)) {
195 final String filename = element.getAttribute(XML_FILE_ATTRIB);
196 final String name = element.getContributor().getName();
197 // Run this in a safe runner in case there is an exception
198 // (IOException, FileNotFoundException, NPE, etc).
199 // This makes sure other extensions are not prevented from
200 // working if one is faulty.
201 SafeRunner.run(new ISafeRunnable() {
202
203 @Override
204 public void run() throws IOException {
205 if (name != null) {
206 Bundle bundle = Platform.getBundle(name);
207 if (bundle != null) {
208 URL xmlUrl = bundle.getResource(filename);
209 if (xmlUrl == null) {
210 throw new FileNotFoundException(filename);
211 }
212 URL locatedURL = FileLocator.toFileURL(xmlUrl);
213 map.put(filename, new Path(locatedURL.getPath()));
214 }
215 }
216 }
217
218 @Override
219 public void handleException(Throwable exception) {
220 // Handled sufficiently in SafeRunner
221 }
222 });
223 }
224 }
225 return map;
226 }
227
228 /**
229 * Delete an XML analysis file
230 *
231 * @param name
232 * The XML file to delete
233 */
234 public static void deleteFile(String name) {
235 Map<String, File> files = listFiles();
236 File file = files.get(name);
237 if (file == null) {
238 return;
239 }
240 file.delete();
241 }
242
243 /**
244 * Export an XML analysis file to an external path
245 *
246 * @param from
247 * The name of the file to export
248 * @param to
249 * The full path of the file to write to
250 * @return Whether the file was successfully exported
251 */
252 public static IStatus exportXmlFile(String from, String to) {
253
254 /* Copy file to path */
255 File fromFile = getXmlFilesPath().addTrailingSeparator().append(from).toFile();
256
257 if (!fromFile.exists()) {
258 Activator.logError("Failed to find XML analysis file " + fromFile.getName()); //$NON-NLS-1$
259 return Status.CANCEL_STATUS;
260 }
261
262 File toFile = new File(to);
263
264 return copyXmlFile(fromFile, toFile);
265 }
266
267 private static IStatus copyXmlFile(File fromFile, File toFile) {
268 try {
269 if (!toFile.exists()) {
270 toFile.createNewFile();
271 }
272 } catch (IOException e) {
273 String error = Messages.XmlUtils_ErrorCopyingFile;
274 Activator.logError(error, e);
275 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, error, e);
276 }
277
278 try (FileInputStream fis = new FileInputStream(fromFile);
279 FileOutputStream fos = new FileOutputStream(toFile);
280 FileChannel source = fis.getChannel();
281 FileChannel destination = fos.getChannel();) {
282 destination.transferFrom(source, 0, source.size());
283 } catch (IOException e) {
284 String error = Messages.XmlUtils_ErrorCopyingFile;
285 Activator.logError(error, e);
286 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, error, e);
287 }
288 return Status.OK_STATUS;
289 }
290
291 /**
292 * Get the IDs of all the analysis described in a single file
293 *
294 * @param fileName
295 * The file name
296 * @return The list of IDs
297 */
298 public static List<String> getAnalysisIdsFromFile(String fileName) {
299 List<String> ids = new ArrayList<>();
300 File file = getXmlFilesPath().addTrailingSeparator().append(fileName).toFile();
301 if (file.exists()) {
302 try {
303 Document doc = getDocumentFromFile(file);
304
305 /* get State Providers modules */
306 NodeList stateproviderNodes = doc.getElementsByTagName(TmfXmlStrings.STATE_PROVIDER);
307 for (int i = 0; i < stateproviderNodes.getLength(); i++) {
308 ids.add(nullToEmptyString(((Element) stateproviderNodes.item(i)).getAttribute(TmfXmlStrings.ID)));
309 }
310
311 /* get patterns modules */
312 NodeList patternNodes = doc.getElementsByTagName(TmfXmlStrings.PATTERN);
313 for (int i = 0; i < patternNodes.getLength(); i++) {
314 ids.add(nullToEmptyString(((Element) patternNodes.item(i)).getAttribute(TmfXmlStrings.ID)));
315 }
316 } catch (ParserConfigurationException | SAXException | IOException e) {
317 Activator.logError("Failed to get analyses IDs from " + fileName); //$NON-NLS-1$
318 }
319 }
320 return ids;
321 }
322
323 /**
324 * Load the XML File
325 *
326 * @param file
327 * The XML file
328 * @return The document representing the XML file
329 * @throws ParserConfigurationException
330 * if a DocumentBuilder cannot be created
331 * @throws SAXException
332 * If any parse errors occur.
333 * @throws IOException
334 * If any IO errors occur.
335 */
336 public static Document getDocumentFromFile(File file) throws ParserConfigurationException, SAXException, IOException {
337 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
338 Document doc = dbFactory.newDocumentBuilder().parse(file);
339 doc.getDocumentElement().normalize();
340 return doc;
341 }
342
343 /**
344 * Get only the XML element children of an XML element.
345 *
346 * @param parent
347 * The parent element to get children from
348 * @return The list of children Element of the parent
349 */
350 public static @NonNull List<@Nullable Element> getChildElements(Element parent) {
351 NodeList childNodes = parent.getChildNodes();
352 List<@Nullable Element> childElements = new ArrayList<>();
353 for (int index = 0; index < childNodes.getLength(); index++) {
354 if (childNodes.item(index).getNodeType() == Node.ELEMENT_NODE) {
355 childElements.add((Element) childNodes.item(index));
356 }
357 }
358 return childElements;
359 }
360
361 /**
362 * Get the XML children element of an XML element, but only those of a
363 * certain type
364 *
365 * @param parent
366 * The parent element to get the children from
367 * @param elementTag
368 * The tag of the elements to return
369 * @return The list of children {@link Element} of the parent
370 */
371 public static List<@NonNull Element> getChildElements(Element parent, String elementTag) {
372 /* get the state providers and find the corresponding one */
373 NodeList nodes = parent.getElementsByTagName(elementTag);
374 List<@NonNull Element> childElements = new ArrayList<>();
375
376 for (int i = 0; i < nodes.getLength(); i++) {
377 Element node = (Element) nodes.item(i);
378 if (node.getParentNode().equals(parent)) {
379 childElements.add(node);
380 }
381 }
382 return childElements;
383 }
384
385 /**
386 * Return the node element corresponding to the requested type in the file.
387 *
388 * TODO: Nothing prevents from having duplicate type -> id in a same file.
389 * That should not be allowed. If you want an element with the same ID as
390 * another one, it should be in a different file and we should check it at
391 * validation time.
392 *
393 * @param filePath
394 * The absolute path to the XML file
395 * @param elementType
396 * The type of top level element to search for
397 * @param elementId
398 * The ID of the desired element
399 * @return The XML element or <code>null</code> if not found
400 */
401 public static Element getElementInFile(String filePath, @NonNull String elementType, @NonNull String elementId) {
402
403 if (filePath == null) {
404 return null;
405 }
406
407 IPath path = new Path(filePath);
408 File file = path.toFile();
409 if (file == null || !file.exists() || !file.isFile() || !xmlValidate(file).isOK()) {
410 return null;
411 }
412
413 try {
414 Document doc = getDocumentFromFile(file);
415
416 /* get the state providers and find the corresponding one */
417 NodeList nodes = doc.getElementsByTagName(elementType);
418 Element foundNode = null;
419
420 for (int i = 0; i < nodes.getLength(); i++) {
421 Element node = (Element) nodes.item(i);
422 String id = node.getAttribute(TmfXmlStrings.ID);
423 if (id.equals(elementId)) {
424 foundNode = node;
425 }
426 }
427 return foundNode;
428 } catch (ParserConfigurationException | SAXException | IOException e) {
429 return null;
430 }
431
432 }
433 }
This page took 0.041949 seconds and 5 git commands to generate.