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