/* * 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.monkey.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.AndroidPlugin; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.i18n.AndroidNLS; /** * This class provides methods to manage monkey options */ public class MonkeyOptionsMgt implements IMonkeyOptionsConstants { /** * List of all monkey options groups (the monkey options themselves will * be accessed through the use of a method in the group object that returns * the monkey options in that group) */ private static List<MonkeyOptionsGroup> monkeyOptionsGroupsList = null; /** * List of all monkey options, indexed by their names, for fast access */ private static Map<String, MonkeyOption> monkeyOptionsMap = new HashMap<String, MonkeyOption>(); /* * Load the monkey options / groups list */ static { load(); } /** * Get the monkey options groups list * * @return monkey options groups list */ public static List<MonkeyOptionsGroup> getMonkeyOptionsGroupsList() { return monkeyOptionsGroupsList; } /** * Read all groups and monkey options available for editing from a XML * and stores the information in the correspondent beans */ public static void load() { try { // Clear monkey options groups list monkeyOptionsGroupsList = new ArrayList<MonkeyOptionsGroup>(); // Define XML path InputStream xmlStream = AndroidPlugin.getDefault().getBundle().getEntry(MONKEY_OPTIONS_XML_PATH) .openStream(); // Load XML DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(xmlStream); /* * Iterate through Monkey Groups */ Element rootNode = document.getDocumentElement(); NodeList monkeyOptionsGroups = rootNode.getElementsByTagName(GROUP_TAG); for (int i = 0; i < monkeyOptionsGroups.getLength(); i++) { /* * Create group */ Element group = (Element) monkeyOptionsGroups.item(i); String strKey = group.getAttributeNode(GROUP_TAG_ID).getNodeValue(); strKey = Platform.getResourceString(AndroidPlugin.getDefault().getBundle(), strKey.trim()); MonkeyOptionsGroup monkeyOptionsGroup = new MonkeyOptionsGroup(strKey); monkeyOptionsGroup.setTitle(monkeyOptionsGroup.getId()); /* * Iterate through Monkey Options in this group */ NodeList monkeyOptions = group.getElementsByTagName(MONKEY_OPT_TAG); monkeyOptionsGroup.setMonkeyOptions(new ArrayList<MonkeyOption>()); // clear monkey options for (int j = 0; j < monkeyOptions.getLength(); j++) { /* * Create monkey option */ Element option = (Element) monkeyOptions.item(j); MonkeyOption monkeyOption = new MonkeyOption(option.getAttributeNode(MONKEY_OPT_TAG_NAME) .getNodeValue(), getMonkeyOptionType(option.getAttributeNode( MONKEY_OPT_TAG_TYPE).getNodeValue())); // name and type strKey = option.getAttributeNode(MONKEY_OPT_TAG_FRIENDLY_NAME).getNodeValue(); strKey = Platform.getResourceString(AndroidPlugin.getDefault().getBundle(), strKey.trim()); monkeyOption.setUserFriendlyName(strKey); // friendly name strKey = option.getElementsByTagName(MONKEY_OPT_TAG_DESCRIPTION).item(0) .getTextContent(); strKey = Platform.getResourceString(AndroidPlugin.getDefault().getBundle(), strKey.trim()); monkeyOption.setDescription(strKey); // description if (option.getAttributeNode(MONKEY_OPT_TAG_TYPE_DETAILS) != null) { monkeyOption.setTypeDetails(option.getAttributeNode( MONKEY_OPT_TAG_TYPE_DETAILS).getNodeValue()); // type details } // Iterate through monkey option pre-defined values, if any NodeList preDefinedValuesContainer = option.getElementsByTagName(PREDEFINED_VALUES_TAG); monkeyOption.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); monkeyOption.getPreDefinedValues() .add(preDefinedValue.getTextContent()); } } /* * Add monkey options to the group */ monkeyOptionsGroup.getMonkeyOptions().add(monkeyOption); monkeyOptionsMap.put(monkeyOption.getName(), monkeyOption); } /* * Add groups to the groups list */ monkeyOptionsGroupsList.add(monkeyOptionsGroup); } } catch (Exception e) { StudioLogger.error("Failed to load monkey options"); } } /** * Validate the values assigned for the monkey 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 Monkey Groups */ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList()) { /* * Iterate through monkey options in this group */ for (MonkeyOption monkeyOption : group.getMonkeyOptions()) { /* * Check if the Monkey Option is checked */ if (monkeyOption.isChecked() && (status.isOK())) { String name = monkeyOption.getName(); // monkey option name String ufname = monkeyOption.getUserFriendlyName(); // user-friendly monkey option name String value = monkeyOption.getValue(); // monkey option value String typeDetails = monkeyOption.getTypeDetails(); // monkey option type detail /* * General validation: no quotes in values */ if ((!monkeyOption.getName().equals(OTHERS_OTHER)) && (value.indexOf("\"") >= 0)) { msg = NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_NoQuotes, ufname); } else { /* * Call the appropriate validation method */ switch (monkeyOption.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, AndroidPlugin.PLUGIN_ID, msg); break; } } } } return status; } /** * Validate the monkey option value for an monkey option of "text" type * * @param name the monkey option name * @param ufname the user-friendly monkey option name * @param value the current assigned value for the monkey option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the monkey 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(AndroidNLS.ERR_PropertiesMainComposite_Monkey_TextBlank, ufName); } return msg; } /** * Validate the monkey option value for an monkey option of "number" type * * @param name the monkey option name * @param ufname the user-friendly monkey option name * @param value the current assigned value for the monkey option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the monkey 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(AndroidNLS.ERR_PropertiesMainComposite_Monkey_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( AndroidNLS.ERR_PropertiesMainComposite_Monkey_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( AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberIntRange, new String[] { ufName, valueRange[0], valueRange[1] }); } } } } catch (NumberFormatException ex) { // it's not a number msg = NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberMustBeInteger, ufName); } } return msg; } /** * Validate the monkey option value for an monkey option of "path" type * * @param name the monkey option name * @param ufname the user-friendly monkey option name * @param value the current assigned value for the monkey option * @param typeDetails any special requirements that the assigned value must be match * @return null if the value assigned for the monkey 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(AndroidNLS.ERR_PropertiesMainComposite_Monkey_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(AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathDirNotExist, ufName); } else { if (file.isFile()) { // it's not a folder msg = NLS.bind( AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathMustBeDir, ufName); } } } /* * Validate file */ else { if (!file.exists()) { // the file doesn't exist msg = NLS.bind( AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathFileNotExist, ufName); } else { // it's not a file if (file.isDirectory()) { msg = NLS.bind( AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathMustBeFile, ufName); } // it doesn't have the correct extension else { if (!typeDetails.equals("." + (new Path(value)).getFileExtension())) { msg = NLS.bind( AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathIncorrectFileType, new String[] { ufName, typeDetails }); } } } } } return msg; } /** * Generates the list of parameters that shall to be sent to adb shell * * @return the list of parameters that shall to be sent to the adb shell */ public static String getParamList() { String paramList = ""; /* * Iterate through Monkey Groups */ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList()) { /* * Iterate through Monkey Options in this group */ int monkeyOptionType; for (MonkeyOption monkeyOption : group.getMonkeyOptions()) { monkeyOptionType = monkeyOption.getType(); if (monkeyOption.isChecked()) // check if the monkey option is being used { if (monkeyOptionType == TYPE_NONE) { paramList += ((paramList.equals("")) ? "" : " ") + monkeyOption.getName(); } else { if ((monkeyOption.getName().equals(OTHERS_OTHER))) { paramList += ((paramList.equals("")) ? "" : " ") + monkeyOption.getValue(); } else { if ((monkeyOption.getName().equals(CATEGORY_OPTION))) { String[] values = monkeyOption.getValue().split(" "); for (int i = 0; i < values.length; i++) { if (values[i].trim().length() > 0) { paramList += ((paramList.equals("")) ? "" : " ") + monkeyOption.getName() + " " + values[i]; } } } else { String value = monkeyOption.getValue(); if (!value.equals("")) { 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("")) ? "" : " ") + monkeyOption.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 Monkey Groups */ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList()) { /* * Iterate through monkey options in this group */ String soValue = ""; for (MonkeyOption monkeyOption : group.getMonkeyOptions()) { soValue = properties.getProperty(monkeyOption.getName()); if (soValue != null) { monkeyOption.setChecked(true); monkeyOption.setValue(soValue); } else { monkeyOption.setChecked(false); monkeyOption.setValue(""); } monkeyOption.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 Monkey Groups */ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList()) { /* * Iterate through monkey options in this group */ String soName, soValue = ""; int soType, shift = 0; for (MonkeyOption monkeyOption : group.getMonkeyOptions()) { soName = monkeyOption.getName(); soType = monkeyOption.getType(); if (commandLine.startsWith(soName)) { if (soType == TYPE_NONE) { soValue = new Boolean(true).toString(); shift = soName.length() + 1; } else { if ((monkeyOption.getName().equals(CATEGORY_OPTION))) { String soValueCat = ""; while (commandLine.startsWith(CATEGORY_OPTION)) { commandLine = commandLine.substring(soName.length() + 1, commandLine.length()); ParameterBean param = getNextParameterValue(commandLine); soValue = param.getValue(); shift = param.getLastPosition() + 1; soValueCat = (soValueCat.equals("") ? soValueCat : soValueCat + " ") + soValue; if (shift < (commandLine.length() - 1)) { commandLine = commandLine.substring(shift, commandLine.length()); } else { commandLine = ""; } } shift = 0; soValue = soValueCat; } else { commandLine = commandLine.substring(soName.length() + 1, commandLine.length()); ParameterBean param = getNextParameterValue(commandLine); soValue = param.getValue(); shift = param.getLastPosition() + 1; } } properties.put(monkeyOption.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 monkey option from a string * to a number * * @param type string that represents the type * @return number that represents the type */ private static int getMonkeyOptionType(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 * monkey 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; } } }