1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.module
;
16 import java
.io
.FileInputStream
;
17 import java
.io
.FileOutputStream
;
18 import java
.io
.IOException
;
20 import java
.nio
.channels
.FileChannel
;
21 import java
.util
.ArrayList
;
22 import java
.util
.List
;
24 import javax
.xml
.XMLConstants
;
25 import javax
.xml
.parsers
.DocumentBuilder
;
26 import javax
.xml
.parsers
.DocumentBuilderFactory
;
27 import javax
.xml
.parsers
.ParserConfigurationException
;
28 import javax
.xml
.transform
.Source
;
29 import javax
.xml
.transform
.stream
.StreamSource
;
30 import javax
.xml
.validation
.Schema
;
31 import javax
.xml
.validation
.SchemaFactory
;
32 import javax
.xml
.validation
.Validator
;
34 import org
.eclipse
.core
.runtime
.IPath
;
35 import org
.eclipse
.core
.runtime
.IStatus
;
36 import org
.eclipse
.core
.runtime
.Path
;
37 import org
.eclipse
.core
.runtime
.Status
;
38 import org
.eclipse
.jdt
.annotation
.NonNull
;
39 import org
.eclipse
.jdt
.annotation
.Nullable
;
40 import org
.eclipse
.osgi
.util
.NLS
;
41 import org
.eclipse
.tracecompass
.internal
.tmf
.analysis
.xml
.core
.Activator
;
42 import org
.eclipse
.tracecompass
.tmf
.analysis
.xml
.core
.stateprovider
.TmfXmlStrings
;
43 import org
.w3c
.dom
.Document
;
44 import org
.w3c
.dom
.Element
;
45 import org
.w3c
.dom
.Node
;
46 import org
.w3c
.dom
.NodeList
;
47 import org
.xml
.sax
.SAXException
;
48 import org
.xml
.sax
.SAXParseException
;
51 * Class containing some utilities for the XML plug-in packages: for example, it
52 * manages the XML files and validates them
54 * @author Geneviève Bastien
56 public class XmlUtils
{
58 /** Sub-directory of the plug-in where XML files are stored */
59 private static final String XML_DIRECTORY
= "xml_files"; //$NON-NLS-1$
61 /** Name of the XSD schema file */
62 private static final String XSD
= "xmlDefinition.xsd"; //$NON-NLS-1$
64 /** Make this class non-instantiable */
70 * Get the path where the XML files are stored. Create it if it does not
73 * @return path to XML files
75 public static IPath
getXmlFilesPath() {
76 IPath path
= Activator
.getDefault().getStateLocation();
77 path
= path
.addTrailingSeparator().append(XML_DIRECTORY
);
79 /* Check if directory exists, otherwise create it */
80 File dir
= path
.toFile();
81 if (!dir
.exists() || !dir
.isDirectory()) {
89 * Validate the XML file input with the XSD schema
92 * XML file to validate
93 * @return True if the XML validates
95 public static IStatus
xmlValidate(File xmlFile
) {
96 URL url
= XmlUtils
.class.getResource(XSD
);
97 SchemaFactory schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
98 Source xmlSource
= new StreamSource(xmlFile
);
100 Schema schema
= schemaFactory
.newSchema(url
);
101 Validator validator
= schema
.newValidator();
102 validator
.validate(xmlSource
);
103 } catch (SAXParseException e
) {
104 String error
= NLS
.bind(Messages
.XmlUtils_XmlParseError
, e
.getLineNumber(), e
.getLocalizedMessage());
105 Activator
.logError(error
);
106 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
107 } catch (SAXException e
) {
108 String error
= NLS
.bind(Messages
.XmlUtils_XmlValidationError
, e
.getLocalizedMessage());
109 Activator
.logError(error
);
110 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
111 } catch (IOException e
) {
112 String error
= Messages
.XmlUtils_XmlValidateError
;
113 Activator
.logError("IO exception occurred", e
); //$NON-NLS-1$
114 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
116 return Status
.OK_STATUS
;
120 * Adds an XML file to the plugin's path. The XML file should have been
121 * validated using the {@link XmlUtils#xmlValidate(File)} method before
122 * calling this method.
125 * The XML file to add
126 * @return Whether the file was successfully added
128 public static IStatus
addXmlFile(File fromFile
) {
130 /* Copy file to path */
131 File toFile
= getXmlFilesPath().addTrailingSeparator().append(fromFile
.getName()).toFile();
134 if (!toFile
.exists()) {
135 toFile
.createNewFile();
137 } catch (IOException e
) {
138 String error
= Messages
.XmlUtils_ErrorCopyingFile
;
139 Activator
.logError(error
, e
);
140 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
143 try (FileInputStream fis
= new FileInputStream(fromFile
);
144 FileOutputStream fos
= new FileOutputStream(toFile
);
145 FileChannel source
= fis
.getChannel();
146 FileChannel destination
= fos
.getChannel();) {
147 destination
.transferFrom(source
, 0, source
.size());
148 } catch (IOException e
) {
149 String error
= Messages
.XmlUtils_ErrorCopyingFile
;
150 Activator
.logError(error
, e
);
151 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
153 return Status
.OK_STATUS
;
157 * Get only the XML element children of an XML element.
160 * The parent element to get children from
161 * @return The list of children Element of the parent
163 public static List
<@Nullable Element
> getChildElements(Element parent
) {
164 NodeList childNodes
= parent
.getChildNodes();
165 List
<@Nullable Element
> childElements
= new ArrayList
<>();
166 for (int index
= 0; index
< childNodes
.getLength(); index
++) {
167 if (childNodes
.item(index
).getNodeType() == Node
.ELEMENT_NODE
) {
168 childElements
.add((Element
) childNodes
.item(index
));
171 return childElements
;
175 * Get the XML children element of an XML element, but only those of a
179 * The parent element to get the children from
181 * The tag of the elements to return
182 * @return The list of children {@link Element} of the parent
184 public static List
<@NonNull Element
> getChildElements(Element parent
, String elementTag
) {
185 /* get the state providers and find the corresponding one */
186 NodeList nodes
= parent
.getElementsByTagName(elementTag
);
187 List
<@NonNull Element
> childElements
= new ArrayList
<>();
189 for (int i
= 0; i
< nodes
.getLength(); i
++) {
190 Element node
= (Element
) nodes
.item(i
);
191 if (node
.getParentNode().equals(parent
)) {
192 childElements
.add(node
);
195 return childElements
;
199 * Return the node element corresponding to the requested type in the file.
201 * TODO: Nothing prevents from having duplicate type -> id in a same file.
202 * That should not be allowed. If you want an element with the same ID as
203 * another one, it should be in a different file and we should check it at
207 * The absolute path to the XML file
209 * The type of top level element to search for
211 * The ID of the desired element
212 * @return The XML element or <code>null</code> if not found
214 public static Element
getElementInFile(String filePath
, @NonNull String elementType
, @NonNull String elementId
) {
216 if (filePath
== null) {
220 IPath path
= new Path(filePath
);
221 File file
= path
.toFile();
222 if (file
== null || !file
.exists() || !file
.isFile() || !xmlValidate(file
).isOK()) {
227 /* Load the XML File */
228 DocumentBuilderFactory dbFactory
= DocumentBuilderFactory
.newInstance();
229 DocumentBuilder dBuilder
;
231 dBuilder
= dbFactory
.newDocumentBuilder();
232 Document doc
= dBuilder
.parse(file
);
233 doc
.getDocumentElement().normalize();
235 /* get the state providers and find the corresponding one */
236 NodeList nodes
= doc
.getElementsByTagName(elementType
);
237 Element foundNode
= null;
239 for (int i
= 0; i
< nodes
.getLength(); i
++) {
240 Element node
= (Element
) nodes
.item(i
);
241 String id
= node
.getAttribute(TmfXmlStrings
.ID
);
242 if (id
.equals(elementId
)) {
247 } catch (ParserConfigurationException
| SAXException
| IOException e
) {