/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.emulator.device.instance.options; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.emulator.EmulatorPlugin; import com.motorola.studio.android.emulator.i18n.EmulatorNLS; /** * This class provides methods to manage startup options * */ public class StartupOptionsMgt implements IStartupOptionsConstants { /** * List of all startup options groups (the startup options themselves will * be accessed through the use of a method in the group object that returns * the startup options in that group) */ private static List<StartupOptionsGroup> startupOptionsGroupsList = null; /** * List of all startup options, indexed by their names, for fast access */ private static Map<String, StartupOption> startupOptionsMap = new HashMap<String, StartupOption>(); /* * Load the startup options / groups list */ static { load(); } /** * Get the startup options groups list * * @return startup options groups list */ public static List<StartupOptionsGroup> getStartupOptionsGroupsList() { return startupOptionsGroupsList; } /** * Read all groups and startup options available for editing from a XML * and stores the information in the correspondent beans */ public static void load() { try { // Clear startup options groups list startupOptionsGroupsList = new ArrayList<StartupOptionsGroup>(); // Define XML path InputStream xmlStream = EmulatorPlugin.getDefault().getBundle().getEntry(STARTUP_OPTIONS_XML_PATH) .openStream(); // Load XML DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(xmlStream); /* * Iterate through Startup Groups */ Element rootNode = document.getDocumentElement(); NodeList startupOptionsGroups = rootNode.getElementsByTagName(GROUP_TAG); for (int i = 0; i < startupOptionsGroups.getLength(); i++) { /* * Create group */ Element group = (Element) startupOptionsGroups.item(i); String strKey = group.getAttributeNode(GROUP_TAG_ID).getNodeValue(); strKey = Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(), strKey); StartupOptionsGroup startupOptionsGroup = new StartupOptionsGroup(strKey); startupOptionsGroup.setTitle(startupOptionsGroup.getId()); /* * Iterate through Startup Options in this group */ NodeList startupOptions = group.getElementsByTagName(STARTUP_OPT_TAG); startupOptionsGroup.setStartupOptions(new ArrayList<StartupOption>()); // clear startup options for (int j = 0; j < startupOptions.getLength(); j++) { /* * Create startup option */ Element option = (Element) startupOptions.item(j); StartupOption startupOption = new StartupOption(option.getAttributeNode(STARTUP_OPT_TAG_NAME) .getNodeValue(), getStartupOptionType(option.getAttributeNode( STARTUP_OPT_TAG_TYPE).getNodeValue())); // name and type strKey = option.getAttributeNode(STARTUP_OPT_TAG_FRIENDLY_NAME).getNodeValue(); strKey = Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(), strKey); startupOption.setUserFriendlyName(strKey); // friendly name strKey = option.getElementsByTagName(STARTUP_OPT_TAG_DESCRIPTION).item(0) .getTextContent(); strKey = Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(), strKey); startupOption.setDescription(strKey); // description if (option.getAttributeNode(STARTUP_OPT_TAG_TYPE_DETAILS) != null) { startupOption.setTypeDetails(option.getAttributeNode( STARTUP_OPT_TAG_TYPE_DETAILS).getNodeValue()); // type details } // Iterate through startup option pre-defined values, if any NodeList preDefinedValuesContainer = option.getElementsByTagName(PREDEFINED_VALUES_TAG); startupOption.setPreDefinedValues(new ArrayList<String>()); // clear pre-defined values if (preDefinedValuesContainer.getLength() > 0) { NodeList preDefinedValues = ((Element) preDefinedValuesContainer.item(0)) .getElementsByTagName(PREDEFINED_VALUE_TAG); for (int k = 0; k < preDefinedValues.getLength(); k++) { // Add pre-defined values to the option Element preDefinedValue = (Element) preDefinedValues.item(k); startupOption.getPreDefinedValues().add( preDefinedValue.getTextContent()); } } /* * Add startup options to the group */ startupOptionsGroup.getStartupOptions().add(startupOption); startupOptionsMap.put(startupOption.getName(), startupOption); } /* * Add groups to the groups list */ startupOptionsGroupsList.add(startupOptionsGroup); } } catch (Exception e) { StudioLogger.error("Failed to load startup options"); } } /** * Validate the values assigned for the startup options marked as checked (the ones that are * being used), according to its type and type details. * * @return Status object with the result of the validation */ public static Status validate() { Status status = (Status) Status.OK_STATUS; String msg = null; /* * Iterate through Startup Groups */ for (StartupOptionsGroup group : getStartupOptionsGroupsList()) { /* * Iterate through Startup Options in this group */ for (StartupOption startupOption : group.getStartupOptions()) { /* * Check if the Startup Option is checked */ if (startupOption.isChecked() && (status.isOK())) { String name = startupOption.getName(); // startup option name String ufname = startupOption.getUserFriendlyName(); // user-friendly startup option name String value = startupOption.getValue(); // startup option value String typeDetails = startupOption.getTypeDetails(); // startup option type detail /* * General validation: no quotes in values */ if ((!startupOption.getName().equals(OTHERS_OTHER)) && (value.indexOf("\"") >= 0)) { msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NoQuotes, ufname); } else { /* * Call the appropriate validation method */ switch (startupOption.getType()) { case TYPE_TEXT: msg = validateTextField(name, ufname, value, typeDetails); break; case TYPE_NUMBER: msg = validadeNumberField(name, ufname, value, typeDetails); break; case TYPE_PATH: msg = validadePathField(name, ufname, value, typeDetails); break; } } /* * If some validation has failed, return with an error message */ if (msg != null) { status = new Status(Status.ERROR, EmulatorPlugin.PLUGIN_ID, msg); break; } } } } return status; } /** * Validate the startup option value for an startup option of "text" type * * @param name the startup option name * @param ufname the user-friendly startup option name * @param value the current assigned value for the startup option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the startup option is a valid one or an error message otherwise */ private static String validateTextField(String name, String ufName, String value, String typeDetails) { String msg = null; // Check if the value is blank if ((value == null) || (value.equals(""))) { msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_TextBlank, ufName); } return msg; } /** * Validate the startup option value for an startup option of "number" type * * @param name the startup option name * @param ufname the user-friendly startup option name * @param value the current assigned value for the startup option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the startup option is a valid one or an error message otherwise */ private static String validadeNumberField(String name, String ufName, String value, String typeDetails) { String msg = null; // Check if the value is blank if ((value == null) || (value.equals(""))) { msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberRequired, ufName); } else { try { /* * Check if it's an Integer. * If it's not, an exception will be thrown */ int intValue = Integer.parseInt(value); /* * Check if it's positive */ if (intValue < 0) { msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger, ufName); } else { /* * Check if the value is in the correct range */ if (typeDetails != null) { String[] valueRange = typeDetails.split(";"); if ((intValue < Integer.parseInt(valueRange[0])) || (intValue > Integer.parseInt(valueRange[1]))) { // the value is not in the correct range msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberIntRange, new String[] { ufName, valueRange[0], valueRange[1] }); } } } } catch (NumberFormatException ex) { // it's not a number msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger, ufName); } } return msg; } /** * Validate the startup option value for an startup option of "path" type * * @param name the startup option name * @param ufname the user-friendly startup option name * @param value the current assigned value for the startup option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the startup option is a valid one or an error message otherwise */ private static String validadePathField(String name, String ufName, String value, String typeDetails) { String msg = null; if ((value == null) || (value.equals(""))) { msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathRequired, ufName); } else { File file = new File(value); /* * Validate folder */ if (typeDetails.equals(TYPE_PATH_DIR)) { /* * Check if the path exists */ if (!file.exists()) { // the folder doesn't exist msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist, ufName); } else { if (file.isFile()) { // it's not a folder msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir, ufName); } } } /* * Validate file */ else { if (!file.exists()) { // the file doesn't exist msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist, ufName); } else { // it's not a file if (file.isDirectory()) { msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile, ufName); } // it doesn't have the correct extension else { if (!typeDetails.equals("." + (new Path(value)).getFileExtension())) { msg = NLS.bind( EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType, new String[] { ufName, typeDetails }); } } } } } return msg; } /** * Generates the list of parameters that shall to be sent to the Emulator * * @return the list of parameters that shall to be sent to the Emulator */ public static String getParamList() { String paramList = ""; /* * Iterate through Startup Groups */ for (StartupOptionsGroup group : getStartupOptionsGroupsList()) { /* * Iterate through Startup Options in this group */ int startupOptionType; for (StartupOption startupOption : group.getStartupOptions()) { startupOptionType = startupOption.getType(); if (startupOption.isChecked()) // check if the startup option is being used { if (startupOptionType == TYPE_NONE) { paramList += ((paramList.equals("")) ? "" : " ") + startupOption.getName(); } else { if ((startupOption.getName().equals(OTHERS_OTHER))) { paramList += ((paramList.equals("")) ? "" : " ") + startupOption.getValue(); } else { String value = startupOption.getValue(); if (Platform.getOS().equals(Platform.OS_WIN32)) { if (value.contains(" ")) { value = "\"" + value + "\""; } } else { if (value.contains("\\")) { value = value.replace("\\", "\\\\"); } if (value.contains(" ")) { value = value.replace(" ", "\\ "); } } paramList += ((paramList.equals("")) ? "" : " ") + startupOption.getName() + (value.trim().length() > 0 ? " " + value : ""); } } } } } return paramList; } /** * Load values from a Properties object * * @param properties properties object containing the values that must be loaded into de model */ private static void loadValues(Properties properties) { /* * Iterate through Startup Groups */ for (StartupOptionsGroup group : getStartupOptionsGroupsList()) { /* * Iterate through Startup Options in this group */ String soValue = ""; for (StartupOption startupOption : group.getStartupOptions()) { soValue = properties.getProperty(startupOption.getName()); if (soValue != null) { startupOption.setChecked(true); startupOption.setValue(soValue); } else { startupOption.setChecked(false); startupOption.setValue(""); } startupOption.updateUI(); } } } /** * Create a properties object with the information contained in a command line * * @param commandLine the command line used to start the emulator * @return properties object with the information contained in a command line */ public static Properties parseCommandLine(String commandLine) { Properties properties = new Properties(); if (!commandLine.equals("")) { /* * Iterate through Startup Groups */ for (StartupOptionsGroup group : getStartupOptionsGroupsList()) { /* * Iterate through Startup Options in this group */ String soName, soValue = ""; int soType, shift = 0; for (StartupOption startupOption : group.getStartupOptions()) { soName = startupOption.getName(); soType = startupOption.getType(); if (commandLine.startsWith(soName)) { if (soType == TYPE_NONE) { soValue = new Boolean(true).toString(); shift = soName.length() + 1; } else { commandLine = commandLine .substring(soName.length() + 1, commandLine.length()); //int endValueIndex = commandLine.indexOf("\""); ParameterBean param = getNextParameterValue(commandLine); soValue = param.getValue(); shift = param.getLastPosition() + 1; } properties.put(startupOption.getName(), soValue); if (shift < commandLine.length() - 1) { commandLine = commandLine.substring(shift, commandLine.length()); } else { commandLine = ""; } } } } if (!commandLine.equals("")) { properties.put(OTHERS_OTHER, commandLine); } } return properties; } /** * Load values from a command line * * @param commandLine the command line used to start the emulator */ public static void loadFromCommandLine(String commandLine) { loadValues(parseCommandLine(commandLine)); } /** * Convert the type of the startup option from a string * to a number * * @param type string that represents the type * @return number that represents the type */ private static int getStartupOptionType(String type) { return (TYPE_MAP.get(type)).intValue(); } /** * Parses the next parameter value on a command line * * @param commandLine the command line * * @return a bean containing the parameter value and the last position looked at * the command line */ private static ParameterBean getNextParameterValue(String commandLine) { boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32); boolean escaped = false; boolean quoted = false; char c; String value = ""; int i; for (i = 0; i < commandLine.length(); i++) { c = commandLine.charAt(i); if (escaped) { value += c; escaped = false; } else if (c == '\\' && !isWin32) { escaped = true; } else if (c == '"' && isWin32) { if (value.length() == 0) { quoted = true; } else if (quoted) { break; } else { value += c; } } else if ((c == ' ') && (!quoted)) { break; } else { value += c; } } return new ParameterBean(value, ((quoted) ? i + 1 : i)); } /** * Bean used to identify a parameter value when parsing the * startup options * */ private static class ParameterBean { private final String value; private final int lastPosition; /** * Constructor * * @param value The parameter value * @param lastPosition The last position looked at the command line before stopping * the parse operation */ public ParameterBean(String value, int lastPosition) { this.value = value; this.lastPosition = lastPosition; } /** * Retrieves the parameter value * * @return the parameter value */ public String getValue() { return value; } /** * Retrieves the last position looked at the command line before stopping * the parse operation * * @return the last position looked at the command line before stopping * the parse operation */ public int getLastPosition() { return lastPosition; } } }