| /******************************************************************************* |
| * Copyright (c) 2004, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.tests.session; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.FactoryConfigurationError; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.eclipse.core.runtime.Platform; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| public class SetupManager { |
| public class SetupException extends Exception { |
| /** |
| * All serializable objects should have a stable serialVersionUID |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| public SetupException(String message, Throwable cause) { |
| super(message, cause); |
| } |
| |
| public SetupException(String message) { |
| super(message); |
| } |
| } |
| |
| private static SetupManager instance; |
| private static final String SETUP_DEBUG = "setup.debug"; |
| private static final String SETUP_FILES = "setup.files"; |
| private static final String SETUP_OPTIONS = "setup.options"; |
| private static final String SETUP_OVERRIDE_ECLIPSEARGS = "setup.override.eclipseArgs"; |
| private static final String SETUP_OVERRIDE_SYSTEMPROPERTIES = "setup.override.systemProperties"; |
| private static final String SETUP_OVERRIDE_VMARGS = "setup.override.vmArgs"; |
| private String defaultOptionSetIds = ""; |
| private Map<String, Setup> setupById; |
| private Collection<Setup> setups; |
| |
| private static boolean contains(Object[] set, Object element) { |
| for (Object setElement : set) { |
| if (element.equals(setElement)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public synchronized static SetupManager getInstance() throws SetupException { |
| if (instance != null) { |
| return instance; |
| } |
| instance = new SetupManager(); |
| return instance; |
| } |
| |
| public static boolean inDebugMode() { |
| return Boolean.getBoolean(SETUP_DEBUG); |
| } |
| |
| public static void main(String[] args) throws Exception { |
| SetupManager manager = SetupManager.getInstance(); |
| System.out.println(manager.getDefaultSetup()); |
| } |
| |
| static String[] parseItems(String string) { |
| if (string == null) { |
| return new String[0]; |
| } |
| StringTokenizer tokenizer = new StringTokenizer(string, ","); //$NON-NLS-1$ |
| if (!tokenizer.hasMoreTokens()) { |
| return new String[0]; |
| } |
| String first = tokenizer.nextToken().trim(); |
| if (!tokenizer.hasMoreTokens()) { |
| return new String[] {first}; |
| } |
| ArrayList<String> items = new ArrayList<>(); |
| items.add(first); |
| do { |
| items.add(tokenizer.nextToken().trim()); |
| } while (tokenizer.hasMoreTokens()); |
| return items.toArray(new String[items.size()]); |
| } |
| |
| protected SetupManager() throws SetupException { |
| setups = new ArrayList<>(); |
| setupById = new HashMap<>(); |
| try { |
| loadSetups(); |
| } catch (SetupException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new SetupException("Problems initializing SetupManager", e); |
| } |
| } |
| |
| public Setup buildSetup(String[] optionSets) { |
| Setup defaultSetup = Setup.getDefaultSetup(this); |
| for (Setup customSetup : setups) { |
| if ((customSetup.getId() == null || contains(optionSets, customSetup.getId())) && customSetup.isSatisfied(optionSets)) { |
| defaultSetup.merge(customSetup); |
| } |
| } |
| defaultSetup.setEclipseArguments(parseOptions(System.getProperty(SETUP_OVERRIDE_ECLIPSEARGS))); |
| defaultSetup.setVMArguments(parseOptions(System.getProperty(SETUP_OVERRIDE_VMARGS))); |
| defaultSetup.setSystemProperties(parseOptions(System.getProperty(SETUP_OVERRIDE_SYSTEMPROPERTIES))); |
| return defaultSetup; |
| } |
| |
| private String getAttribute(NamedNodeMap attributes, String name) { |
| Node selected = attributes.getNamedItem(name); |
| return selected == null ? null : selected.getNodeValue(); |
| } |
| |
| private String[] getDefaultOptionSets() { |
| return parseItems(System.getProperty(SETUP_OPTIONS, defaultOptionSetIds)); |
| } |
| |
| /** |
| * Returns a brand new setup object configured according to the current |
| * default setup settings. |
| * |
| * @return a new setup object |
| */ |
| public Setup getDefaultSetup() { |
| return buildSetup(getDefaultOptionSets()); |
| } |
| |
| public Setup getSetup(String id) { |
| return setupById.get(id); |
| } |
| |
| private void loadEclipseArgument(Setup newSetup, Element toParse) { |
| newSetup.setEclipseArgument(toParse.getAttribute("option"), toParse.getAttribute("value")); |
| } |
| |
| private void loadProperty(Setup newSetup, Element toParse) { |
| newSetup.setSystemProperty(toParse.getAttribute("key"), toParse.getAttribute("value")); |
| } |
| |
| private void loadSetup(Element markup) { |
| NamedNodeMap attributes = markup.getAttributes(); |
| if (attributes == null) { |
| return; |
| } |
| Setup newSetup = new Setup(this); |
| newSetup.setId(getAttribute(attributes, "id")); |
| newSetup.setName(getAttribute(attributes, "name")); |
| String timeout = getAttribute(attributes, "timeout"); |
| newSetup.setBaseSetups(parseItems(getAttribute(attributes, "base"))); |
| newSetup.setRequiredSets(parseItems(getAttribute(attributes, "with"))); |
| |
| if (timeout != null) { |
| newSetup.setTimeout(Integer.parseInt(timeout)); |
| } |
| NodeList children = markup.getChildNodes(); |
| for (int i = 0; i < children.getLength(); i++) { |
| Node next = children.item(i); |
| if (!(next instanceof Element)) { |
| continue; |
| } |
| Element toParse = (Element) next; |
| switch (toParse.getTagName()) { |
| case "eclipseArg": |
| loadEclipseArgument(newSetup, toParse); |
| break; |
| case "vmArg": |
| loadVMArgument(newSetup, toParse); |
| break; |
| case "systemProperty": |
| loadProperty(newSetup, toParse); |
| break; |
| default: |
| break; |
| } |
| } |
| setups.add(newSetup); |
| if (newSetup.getId() != null) { |
| setupById.put(newSetup.getId(), newSetup); |
| } |
| } |
| |
| private void loadSetups() throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException, SetupException { |
| String setupFilesProperty = System.getProperty(SETUP_FILES); |
| boolean defaultLocation = false; |
| if (setupFilesProperty == null) { |
| setupFilesProperty = "default-setup.xml"; |
| defaultLocation = true; |
| } |
| String[] setupFileNames = parseItems(setupFilesProperty); |
| File[] setupFiles = new File[setupFileNames.length]; |
| int found = 0; |
| for (int i = 0; i < setupFiles.length; i++) { |
| setupFiles[found] = new File(setupFileNames[i]); |
| if (!setupFiles[found].isFile()) { |
| if (!defaultLocation) { |
| // warn if user-provided location does not exist |
| System.out.println("No setup files found at '" + setupFiles[i].getAbsolutePath() + "'. "); |
| } |
| continue; |
| } |
| found++; |
| } |
| if (found == 0) { |
| if (Platform.isRunning()) { |
| // No setup descriptions found, only the default setup will be available |
| return; |
| } |
| // no setup files found, and we are not running in Eclipse... |
| throw new SetupException("No setup descriptions found. Ensure you are specifying the path for an existing setup file (e.g. -Dsetup.files=<setup-file-location1>[...,<setup-file-locationN>])"); |
| } |
| DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| for (int fileIndex = 0; fileIndex < found; fileIndex++) { |
| Document doc = docBuilder.parse(setupFiles[fileIndex]); |
| Element root = doc.getDocumentElement(); |
| String setupDefaultOptionSets = root.getAttribute("default"); |
| if (setupDefaultOptionSets != null) { |
| defaultOptionSetIds = defaultOptionSetIds == null ? setupDefaultOptionSets : (defaultOptionSetIds + ',' + setupDefaultOptionSets); |
| } |
| NodeList optionSets = root.getChildNodes(); |
| for (int i = 0; i < optionSets.getLength(); i++) { |
| Node next = optionSets.item(i); |
| if (!(next instanceof Element)) { |
| continue; |
| } |
| Element toParse = (Element) next; |
| if (!toParse.getTagName().equals("optionSet")) { |
| continue; |
| } |
| loadSetup(toParse); |
| } |
| } |
| } |
| |
| private void loadVMArgument(Setup newSetup, Element toParse) { |
| newSetup.setVMArgument(toParse.getAttribute("option"), toParse.getAttribute("value")); |
| } |
| |
| /* |
| * Use a double equal sign to escape and equal to treat it from becoming |
| * a key/value pair |
| */ |
| private static Map<String, String> parseOptions(String options) { |
| if (options == null) { |
| return Collections.EMPTY_MAP; |
| } |
| Map<String, String> result = new HashMap<>(); |
| StringTokenizer tokenizer = new StringTokenizer(options.trim(), ";"); |
| while (tokenizer.hasMoreTokens()) { |
| String option = tokenizer.nextToken(); |
| int separatorIndex = option.indexOf('='); |
| if (separatorIndex == -1 || separatorIndex == option.length() - 1) { // property with no value defined |
| result.put(option, ""); |
| continue; |
| } |
| // the 90% case is that we won't have an escaped equals so check to see if we can short-circuit |
| if (!option.contains("==")) { |
| String key = option.substring(0, separatorIndex); |
| String value = option.substring(separatorIndex + 1); |
| result.put(key, value); |
| continue; |
| } |
| // otherwise we have an escaped equals somewhere in this option |
| int valueStart = -1; |
| // strip out the key (first non-escaped equal) |
| StringBuilder key = new StringBuilder(); |
| for (int i = 0; i < option.length(); i++) { |
| char c = option.charAt(i); |
| // if we don't have an equal sign, then just add it to the key |
| if (c != '=') { |
| key.append(c); |
| continue; |
| } |
| i++; |
| if (i >= option.length()) { |
| break; |
| } |
| char next = option.charAt(i); |
| if (next == '=') { |
| key.append('='); |
| continue; |
| } |
| // we had a single equal |
| valueStart = i; |
| break; |
| } |
| String value = ""; |
| // now get the value. replace == by = |
| if (valueStart > -1) { |
| value = option.substring(valueStart).replaceAll("==", "="); |
| } |
| result.put(key.toString(), value); |
| } |
| return result; |
| } |
| } |