package abbot.script.swt;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.eclipse.swt.widgets.Widget;
import abbot.Log;
import abbot.finder.swt.MultipleWidgetsFoundException;
import abbot.finder.swt.WidgetNotFoundException;
import abbot.script.NoSuchReferenceException;
import abbot.script.parsers.Parser;
import abbot.swt.Resolver;
import abbot.tester.swt.WidgetTester;
/** Provide parsing of a String into an array of appropriately typed
* arguments. Arrays are indicated by square brackets, and arguments are
* separated by commas, e.g.<br>
* An empty String array (length zero): "[]"<br>
* Three arguments "one,two,three"<br>
* An array of three arguments, with embedded comma: "[one\,one,two,three]"<br>
* An argument with square brackets: "\[one\]"<br>
* A single null argument: "null"<br>
*/
public class ArgumentParser {
public static final String copyright = "Licensed Materials -- Property of IBM\n"+
"(c) Copyright International Business Machines Corporation, 2003\nUS Government "+
"Users Restricted Rights - Use, duplication or disclosure restricted by GSA "+
"ADP Schedule Contract with IBM Corp.";
private ArgumentParser() { }
/** Maps class names to their corresponding string parsers. */
private static HashMap parsers = new HashMap();
private static boolean isExtension(String name) {
return name.indexOf(".extensions.") != -1;
}
private static Parser findParser(String name, Class targetClass) {
Log.debug("Trying " + name + " for " + targetClass);
try {
Class cvtClass = isExtension(name)
? Class.forName(name, true, targetClass.getClassLoader())
: Class.forName(name);
Parser parser = (Parser)cvtClass.newInstance();
if (cvtClass.getName().indexOf(".extensions.") == -1)
parsers.put(targetClass, parser);
return parser;
}
catch(InstantiationException ie) {
Log.debug(ie);
}
catch(IllegalAccessException iae) {
Log.debug(iae);
}
catch(ClassNotFoundException cnf) {
Log.debug(cnf);
}
return null;
}
/** Set the parser for a given class. Returns the old one, if any. */
public static Parser setParser(Class cls, Parser parser) {
Parser old = (Parser)parsers.get(cls);
parsers.put(cls, parser);
return old;
}
/** Find a string parser for the given class. Returns null if none
* found.
*/
public static Parser getParser(Class cls) {
Parser parser = (Parser)parsers.get(cls);
// Load core testers with the current framework's class loader
// context, and anything else in the context of the code under test
if (parser == null) {
String base = WidgetTester.simpleClassName(cls);
String pkg = Parser.class.getPackage().getName();
parser = findParser(pkg + "." + base + "Parser", cls);
if (parser == null) {
parser = findParser(pkg + ".extensions."
+ base + "Parser", cls);
}
}
return parser;
}
/** Convert the given encoded String into an array of Strings.
* Interprets strings of the format "[el1,el2,el3]" to be a single (array)
* argument. <p>
* Explicit commas and square brackets in arguments must be escaped by
* preceding the character with a backslash ('\'). The string
* '(null)' is interpreted as the value null.
*/
public static String[] parseArgumentList(String encodedArgs) {
ArrayList alist = new ArrayList();
if (encodedArgs == null || "".equals(encodedArgs))
return new String[0];
StringTokenizer st = new StringTokenizer(encodedArgs, ",");
while (st.hasMoreTokens()) {
String str = st.nextToken();
// FIXME what if we want a string argument of "(null)/null"?
if ("null".equals(str) || "(null)".equals(str)) {
str = null;
}
else if ("\"\"".equals(str)) {
str = "";
}
// Patch back together escaped commas
else if (str.startsWith("[")) {
int count = 1;
// Replace escaped commas
while (str.endsWith("\\") && st.hasMoreTokens()) {
String next = st.nextToken();
if (next.endsWith("]") && !next.endsWith("\\]"))
--count;
str += "," + next;
}
if (st.countTokens() == 0 && str.endsWith("]"))
--count;
else while (st.hasMoreTokens() && count != 0) {
String next = st.nextToken();
if (next.startsWith("[") && !next.endsWith("\\["))
++count;
else if (next.endsWith("]") && !next.endsWith("\\]"))
--count;
str += "," + next;
}
if (count != 0) {
String msg = "Unterminated array '" + encodedArgs + "'";
throw new IllegalArgumentException(msg);
}
}
else {
while (str.endsWith("\\") && st.hasMoreTokens()) {
str = str.substring(0, str.length()-1)
+ "," + st.nextToken();
}
}
alist.add(str);
}
return (String[])alist.toArray(new String[alist.size()]);
}
/** Performs property substitutions on the argument priort to evaluating
* it. Substitutions are not recursive.
*/
public static String substitute(Resolver resolver, String arg) {
int i = 0;
int base = 0;
while ((i = arg.indexOf("${", base)) != -1) {
int end = arg.indexOf("}", i);
if (end == -1)
break;
String name = arg.substring(i + 2, end);
String value = resolver.getProperty(name);
Log.debug(name + "=" + value);
if (value != null) {
arg = replace(arg, "${" + name + "}", value);
}
base = i + 1;
}
return arg;
}
/** Evaluate the given set of arguments into the given set of types. */
public static Object[] eval(Resolver resolver,String[] args, Class[] params)
throws IllegalArgumentException,
NoSuchReferenceException,
WidgetNotFoundException {
Object[] plist = new Object[params.length];
for(int i=0;i < plist.length;i++) {
String arg = args[i];
plist[i] = eval(resolver, arg, params[i]);
}
return plist;
}
/** Convert the given string into the given class, if possible, using any
* available parsers if conversion to basic types fails.
* The Resolver could be a parser, but it would need to adapt
* automatically to whatever is the current context.<p>
* Performs property substitution on the argument prior to evaluating it.
*/
public static Object eval(Resolver resolver, String arg, Class cls)
throws IllegalArgumentException,
NoSuchReferenceException,
WidgetNotFoundException {
// Preform property substitution
arg = substitute(resolver, arg);
Parser parser;
Object result = null;
try {
if (arg == null) {
result = null;
}
else if (cls.equals(Boolean.class)
|| cls.equals(boolean.class)) {
result = Boolean.valueOf(arg);
}
else if (cls.equals(Short.class)
|| cls.equals(short.class)) {
result = Short.valueOf(arg);
}
else if (cls.equals(Integer.class)
|| cls.equals(int.class)) {
result = Integer.valueOf(arg);
}
else if (cls.equals(Long.class)
|| cls.equals(long.class)) {
result = Long.valueOf(arg);
}
else if (cls.equals(Float.class)
|| cls.equals(float.class)) {
result = Float.valueOf(arg);
}
else if (cls.equals(Double.class)
|| cls.equals(double.class)) {
result = Double.valueOf(arg);
}
else if (cls.equals(WidgetReference.class)) {
WidgetReference ref = resolver.getWidgetReference(arg);
if (ref == null)
throw new NoSuchReferenceException("The resolver "
+ resolver
+ " has no reference '"
+ arg + "'");
result = ref;
}
else if (Widget.class.isAssignableFrom(cls)) {
WidgetReference ref = resolver.getWidgetReference(arg);
if (ref == null)
throw new NoSuchReferenceException("The resolver "
+ resolver
+ " has no reference '"
+ arg + "'");
// Avoid requiring the user to wait for a Widget to become
// available, in most cases. In those cases where the
// Widget creation is particularly slow, an explicit wait
// can be added.
WidgetTester.waitForWidgetShowing(ref);
try{
result = WidgetTester.findWidget(ref);
//DefaultWidgetFinder.getFinder().findWidget(ref);
}
catch(MultipleWidgetsFoundException mwfe){
mwfe.printStackTrace();
}
}
else if (cls.equals(String.class)) {
result = unescapeBrackets(arg);
}
else if ((parser = getParser(cls)) != null) {
result = parser.parse(unescapeBrackets(arg));
}
else if (cls.isArray() && arg.startsWith("[")) {
String[] args =
parseArgumentList(arg.substring(1, arg.length()-1));
Class base = cls.getComponentType();
Object arr = Array.newInstance(base, args.length);
for (int i=0;i < args.length;i++) {
Object obj = eval(resolver, args[i], base);
Array.set(arr, i, obj);
}
result = arr;
}
else {
String msg = "Can't convert '" + arg
+ "' to " + cls.getName();
throw new IllegalArgumentException(msg);
}
return result;
}
catch(NumberFormatException nfe) {
String msg = "Can't convert '" + arg
+ "' to " + cls.getName();
throw new IllegalArgumentException(msg);
}
}
static String unescapeBrackets(String arg) {
return replace(replace(arg, "\\[", "["), "\\]", "]");
}
/** Replace all instances in the given String of s1 with s2. */
public static String replace(String str, String s1, String s2) {
StringBuffer sb = new StringBuffer(str);
int index = 0;
while ((index = sb.toString().indexOf(s1, index)) != -1) {
sb.delete(index, index + s1.length());
sb.insert(index, s2);
index += s2.length();
}
return sb.toString();
}
}