package com.gorillalogic.monkeytalk.processor; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.gorillalogic.monkeytalk.api.js.tools.JSHelper; import com.gorillalogic.monkeytalk.processor.command.Vars; /** * Static helper class to handle global MonkeyTalk variables. */ public class Globals { public static final String ILLEGAL_MSG = "global " + Vars.ILLEGAL_MSG; public static final String RESERVED_MSG = "is a reserved global variable name"; private static final Pattern VARIABLES_IN_STRING = Pattern .compile("(\\S+=\".*?\"|\\S+='.*?'|\\S+=[^\\s]+)\\s*"); private static Map<String, String> globals; static { globals = new LinkedHashMap<String, String>(); } private Globals() { } /** * Clear all globals. */ public static void clear() { globals = new LinkedHashMap<String, String>(); } /** * Set the global variable given the name and value. If name or value is {@code null}, then do * nothing. Throws exception if the variable name is illegal. * * @param name * the variable name * @param value * the variable value */ public static void setGlobal(String name, String value) { if (name != null && value != null) { validateName(name, null); globals.put(name, value); } } /** * Set the globals from the given map of variables. Throws exception if any variable name in * given map is illegal. * * @param globals */ public static void setGlobals(Map<String, String> globals) { if (globals != null && globals.size() > 0) { for (Map.Entry<String, String> entry : globals.entrySet()) { Globals.setGlobal(entry.getKey(), entry.getValue()); } } } /** * Set the globals from the given properties file (typically the {@code globals.properties} * file). Throws exception if file is null, file is not found, file is folder, or if variable * name in file is illegal. * * @param f * the properties file */ public static void setGlobals(File f) throws IOException { if (f == null || !f.exists() || !f.isFile()) { return; } InputStream in = null; try { Properties props = new Properties(); in = new FileInputStream(f); props.load(in); for (String key : props.stringPropertyNames()) { validateName(key, "globals file '" + f.getName() + "' has"); String val = props.getProperty(key); Globals.setGlobal(key, val); } } finally { if (in != null) { try { in.close(); } catch (IOException ex) { // ignore } } } } /** * Parse the given string of global variables and return it as a map. Throws exception if * variable name in given string is illegal. Used to parse the {@code globals} attribute in the * {@code monkeytalk-ant} project. * * @param s * the string of global variables to be parsed * @return the map of variables */ public static Map<String, String> parse(String s) { Map<String, String> map = new LinkedHashMap<String, String>(); if (s != null && s.length() > 0) { Matcher m = VARIABLES_IN_STRING.matcher(s); while (m.find()) { String tok = m.group(1); String[] parts = tok.split("="); if (parts.length > 1) { validateName(parts[0], null); String val = parts[1]; if (val.startsWith("\"") && val.endsWith("\"")) { val = val.substring(1, val.length() - 1); } else if (val.startsWith("'") && val.endsWith("'")) { val = val.substring(1, val.length() - 1); } map.put(parts[0], val); } } } return map; } /** * Get the map of global variables. * * @return the globals */ public static Map<String, String> getGlobals() { return Collections.unmodifiableMap(globals); } /** * Get a global variable by name. Returns {@code null} if name does not exist. * * @param name * the variable name * @return the variable value */ public static String getGlobal(String name) { return globals.get(name); } /** * True if a global variable of the given name exists, otherwise false. * * @param name * the variable name * @return true if the global variable exists, otherwise false. */ public static boolean hasGlobal(String name) { return globals.containsKey(name); } /** * Delete a global variable by name * * @param name * the variable name * @return the deleted variable value (or {@code null} if not found) */ public static String deleteGlobal(String name) { return globals.remove(name); } /** * Validate the given variable name. Throws an exception if variable name is illegal, otherwise * does nothing. The given extra message is prepended to the exception message. Variable name * validation is done automatically by {@link Globals#setGlobal(String, String)}. * * @param name * the variable name * @param msg * the extra message * @throws RuntimeException * if variable name is illegal. */ public static void validateName(String name, String msg) throws RuntimeException { if (!name.matches(Vars.VALID_VARIABLE_PATTERN)) { throw new RuntimeException((msg != null ? msg + " " : "") + "illegal global variable '" + name + "' -- " + ILLEGAL_MSG); } if (JSHelper.RESERVED_WORDS.contains(name)) { throw new RuntimeException((msg != null ? msg + " " : "") + "illegal global variable '" + name + "' -- " + RESERVED_MSG); } } /** * Helper to output the entire global variables map as a String. * * @return the global variables as a string */ public static String asString() { StringBuilder sb = new StringBuilder("{"); for (Map.Entry<String, String> entry : globals.entrySet()) { sb.append(sb.length() > 1 ? ", " : " ").append(entry.getKey()).append(":'") .append(entry.getValue()).append("'"); } return sb.append(" }").toString(); } /** * Helper to output the entire global variables map as a String. * * @return the global variables as a string */ public static String asJavascript() { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : globals.entrySet()) { String val = entry.getValue(); if (val.contains("'")) { // escape single quotes... val = val.replace("'", "\\\'"); } sb.append("var ").append(entry.getKey()).append(" = '").append(val).append("';\n"); } return sb.toString(); } }