blob: 7542b4dd4ee834b3ce3de94519f23176f95cb6d9 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}