1 /*******************************************************************************
2  * Copyright (c) 2008, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  *
12  * Contributors:
13  *     IBM Corporation - initial API and implementation
14  *     Alexander Fedorov <alexander.fedorov@arsysop.ru> - Bug 541188
15  *******************************************************************************/
16 package org.eclipse.pde.internal.core.target;
17 
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.text.MessageFormat;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 import javax.xml.parsers.DocumentBuilder;
25 import javax.xml.parsers.DocumentBuilderFactory;
26 import javax.xml.parsers.ParserConfigurationException;
27 import javax.xml.transform.OutputKeys;
28 import javax.xml.transform.Transformer;
29 import javax.xml.transform.TransformerException;
30 import javax.xml.transform.TransformerFactory;
31 import javax.xml.transform.dom.DOMSource;
32 import javax.xml.transform.stream.StreamResult;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IStatus;
35 import org.eclipse.core.runtime.Status;
36 import org.eclipse.pde.core.target.ITargetDefinition;
37 import org.eclipse.pde.core.target.ITargetPlatformService;
38 import org.eclipse.pde.internal.core.ICoreConstants;
39 import org.eclipse.pde.internal.core.PDECore;
40 import org.w3c.dom.DOMException;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.w3c.dom.ProcessingInstruction;
46 import org.xml.sax.InputSource;
47 import org.xml.sax.SAXException;
48 import org.xml.sax.helpers.DefaultHandler;
49 
50 /**
51  * Provides static methods that will serialize and deserialize xml representing a target definition
52  *
53  * @see ITargetDefinition
54  */
55 public class TargetDefinitionPersistenceHelper {
56 
57 	/**
58 	 * Constants for XML element names and attributes
59 	 */
60 	static final String ROOT = "target"; //$NON-NLS-1$
61 	static final String ATTR_NAME = "name"; //$NON-NLS-1$
62 	static final String LOCATIONS = "locations"; //$NON-NLS-1$
63 	static final String LOCATION = "location"; //$NON-NLS-1$
64 	static final String ATTR_LOCATION_PATH = "path"; //$NON-NLS-1$
65 	static final String ATTR_LOCATION_TYPE = "type"; //$NON-NLS-1$
66 	static final String ATTR_USE_DEFAULT = "useDefault"; //$NON-NLS-1$
67 	static final String INCLUDE_BUNDLES = "includeBundles"; //$NON-NLS-1$
68 	static final String ENVIRONMENT = "environment"; //$NON-NLS-1$
69 	static final String OS = "os"; //$NON-NLS-1$
70 	static final String WS = "ws"; //$NON-NLS-1$
71 	static final String ARCH = "arch"; //$NON-NLS-1$
72 	static final String NL = "nl"; //$NON-NLS-1$
73 	static final String TARGET_JRE = "targetJRE"; //$NON-NLS-1$
74 	static final String EXEC_ENV = "execEnv"; //$NON-NLS-1$
75 	static final String JRE_NAME = "jreName"; //$NON-NLS-1$
76 	static final String ARGUMENTS = "launcherArgs"; //$NON-NLS-1$
77 	static final String PROGRAM_ARGS = "programArgs"; //$NON-NLS-1$
78 	static final String VM_ARGS = "vmArgs"; //$NON-NLS-1$
79 	static final String IMPLICIT = "implicitDependencies"; //$NON-NLS-1$
80 	static final String PLUGIN = "plugin"; //$NON-NLS-1$
81 	static final String PDE_INSTRUCTION = "pde"; //$NON-NLS-1$
82 	static final String ATTR_ID = "id"; //$NON-NLS-1$
83 	static final String INSTALLABLE_UNIT = "unit"; //$NON-NLS-1$
84 	static final String REPOSITORY = "repository"; //$NON-NLS-1$
85 	static final String ATTR_INCLUDE_MODE = "includeMode"; //$NON-NLS-1$
86 	public static final String MODE_SLICER = "slicer"; //$NON-NLS-1$
87 	public static final String MODE_PLANNER = "planner"; //$NON-NLS-1$
88 	static final String ATTR_INCLUDE_ALL_PLATFORMS = "includeAllPlatforms"; //$NON-NLS-1$
89 	static final String ATTR_INCLUDE_SOURCE = "includeSource"; //$NON-NLS-1$
90 	static final String ATTR_INCLUDE_CONFIGURE_PHASE = "includeConfigurePhase"; //$NON-NLS-1$
91 	static final String ATTR_VERSION = "version"; //$NON-NLS-1$
92 	static final String ATTR_CONFIGURATION = "configuration"; //$NON-NLS-1$
93 	static final String ATTR_SEQUENCE_NUMBER = "sequenceNumber"; //$NON-NLS-1$
94 	static final String CONTENT = "content"; //$NON-NLS-1$
95 	static final String ATTR_USE_ALL = "useAllPlugins"; //$NON-NLS-1$
96 	static final String PLUGINS = "plugins"; //$NON-NLS-1$
97 	static final String FEATURES = "features"; //$NON-NLS-1$
98 	static final String FEATURE = "feature"; //$NON-NLS-1$
99 	static final String EXTRA_LOCATIONS = "extraLocations"; //$NON-NLS-1$
100 	private static ITargetPlatformService fTargetService;
101 
102 	/**
103 	 * Serializes a target definition to xml and writes the xml to the given
104 	 * stream
105 	 *
106 	 * @param definition
107 	 *            target definition to serialize
108 	 * @param output
109 	 *            output stream to write xml to
110 	 * @throws CoreException
111 	 * @throws ParserConfigurationException
112 	 * @throws TransformerException
113 	 * @throws IOException
114 	 * @throws SAXException
115 	 */
persistXML(ITargetDefinition definition, OutputStream output)116 	public static void persistXML(ITargetDefinition definition, OutputStream output)
117 			throws CoreException, ParserConfigurationException, TransformerException, IOException, SAXException {
118 		Document document = definition.getDocument();
119 		NodeList childNodes = document.getChildNodes();
120 		int length = childNodes.getLength();
121 		if (length == 0) {
122 			return;
123 		}
124 		StreamResult outputTarget = new StreamResult(output);
125 		TransformerFactory factory = TransformerFactory.newInstance();
126 		Transformer transformer = factory.newTransformer();
127 		transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
128 		transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
129 		transformer.setOutputProperty(OutputKeys.STANDALONE, "no"); //$NON-NLS-1$
130 		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); //$NON-NLS-1$
131 		transformer.transform(new DOMSource(childNodes.item(0)), outputTarget);
132 		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //$NON-NLS-1$
133 		transformer.setOutputProperty(OutputKeys.INDENT, "no"); //$NON-NLS-1$
134 		for (int i = 1; i < length; i++) {
135 			transformer.transform(new DOMSource(childNodes.item(i)), outputTarget);
136 		}
137 	}
138 
139 	/**
140 	 * Parses an xml document from the input stream and deserializes it into a
141 	 * target definition.
142 	 *
143 	 * @param definition
144 	 *            definition to be filled with the result of deserialization
145 	 * @param input
146 	 *            stream to get xml input from
147 	 * @throws CoreException
148 	 * @throws ParserConfigurationException
149 	 * @throws IOException
150 	 * @throws SAXException
151 	 */
initFromXML(ITargetDefinition definition, InputStream input)152 	public static void initFromXML(ITargetDefinition definition, InputStream input)
153 			throws CoreException, ParserConfigurationException, SAXException, IOException {
154 		DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
155 		parser.setErrorHandler(new DefaultHandler());
156 		Document doc = parser.parse(new InputSource(input));
157 
158 		Element root = doc.getDocumentElement();
159 		if (!root.getNodeName().equalsIgnoreCase(ROOT)) {
160 			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, Messages.TargetDefinitionPersistenceHelper_0));
161 		}
162 
163 		String version = null;
164 		NodeList list = doc.getChildNodes();
165 		for (int i = 0; i < list.getLength(); ++i) {
166 			Node node = list.item(i);
167 			if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
168 				ProcessingInstruction instruction = (ProcessingInstruction) node;
169 				if (instruction.getTarget() == PDE_INSTRUCTION) {
170 					String data = instruction.getData();
171 					Pattern pattern = Pattern.compile(ATTR_VERSION + "=\"(.*)\""); //$NON-NLS-1$
172 					Matcher matcher = pattern.matcher(data);
173 					if (matcher.matches()) {
174 						version = matcher.group(1);
175 						break;
176 					}
177 				}
178 			}
179 		}
180 		// Avoid doing the rebuilding of each part of the document, just set the
181 		// document at the end
182 		definition.setDocument(null);
183 		// Select the correct helper class to use
184 		// Note: If file structure is updated, make sure to update both default cases
185 		if (version == null || version.length() == 0) {
186 			// No version, default to latest
187 			TargetPersistence38Helper.initFromDoc(definition, root);
188 		} else if (version.equals(ICoreConstants.TARGET38)) {
189 			TargetPersistence38Helper.initFromDoc(definition, root);
190 		} else if (version.equals(ICoreConstants.TARGET36)) { // it can not be 3.7
191 			TargetPersistence36Helper.initFromDoc(definition, root);
192 		} else if (version.equals(ICoreConstants.TARGET35)) {
193 			TargetPersistence35Helper.initFromDoc(definition, root);
194 		} else if (version.compareTo(ICoreConstants.TARGET34) <= 0) {
195 			TargetPersistence34Helper.initFromDoc(definition, root);
196 		} else {
197 			// Version doesn't match any known file structure, default to latest
198 			String name = root.getAttribute(TargetDefinitionPersistenceHelper.ATTR_NAME);
199 			PDECore.log(new Status(IStatus.WARNING, PDECore.PLUGIN_ID, MessageFormat.format(Messages.TargetDefinitionPersistenceHelper_2, version, name)));
200 			TargetPersistence38Helper.initFromDoc(definition, root);
201 		}
202 		definition.setDocument(doc);
203 	}
204 
getTargetPlatformService()205 	static ITargetPlatformService getTargetPlatformService() throws CoreException {
206 		if (fTargetService == null) {
207 			fTargetService = PDECore.getDefault().acquireService(ITargetPlatformService.class);
208 			if (fTargetService == null) {
209 				throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, Messages.TargetDefinitionPersistenceHelper_1));
210 			}
211 		}
212 		return fTargetService;
213 	}
214 
215 	/**
216 	 * Returns the value of any text nodes stored as children of the given element
217 	 * @param element the element to check for text content
218 	 * @return string containing text content of element or empty string
219 	 * @throws DOMException
220 	 */
getTextContent(Element element)221 	static String getTextContent(Element element) throws DOMException {
222 		NodeList children = element.getChildNodes();
223 		StringBuilder result = new StringBuilder();
224 		for (int i = 0; i < children.getLength(); ++i) {
225 			Node currentNode = children.item(i);
226 			if (currentNode.getNodeType() == Node.TEXT_NODE) {
227 				result.append(currentNode.getNodeValue());
228 			}
229 		}
230 		return result.toString();
231 	}
232 }
233