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