/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2013 Gorilla Logic, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package com.gorillalogic.monkeytalk.java.tools;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.gorillalogic.monkeytalk.api.meta.API;
import com.gorillalogic.monkeytalk.api.meta.Action;
import com.gorillalogic.monkeytalk.api.meta.Arg;
import com.gorillalogic.monkeytalk.api.meta.Component;
/**
* Generate Java API. Alas, the Java API can't directly use the interfaces as they are currently
* written, so we just generate a new set of interfaces from the original interfaces.
*/
public class JavaAPIGenerator {
/** exclude these component from the Java API */
private static final List<String> EXCLUDE_COMPONENTS = Arrays.asList("Application",
"Verifiable", "Vars", "Suite", "Test", "Setup", "Teardown", "MTObject", "Doc", "Debug",
"System", "Globals");
private static final String LICENSE = "/* MonkeyTalk - a cross-platform functional testing tool\n"
+ " Copyright (C) 2013 Gorilla Logic, Inc.\n\n"
+ " This program is free software: you can redistribute it and/or modify\n"
+ " it under the terms of the GNU Affero General Public License as published by\n"
+ " the Free Software Foundation, either version 3 of the License, or\n"
+ " (at your option) any later version.\n\n"
+ " This program is distributed in the hope that it will be useful,\n"
+ " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ " GNU Affero General Public License for more details.\n\n"
+ " You should have received a copy of the GNU Affero General Public License\n"
+ " along with this program. If not, see <http://www.gnu.org/licenses/>. */\n";
public static void main(String[] args) {
if (args.length < 1 || args.length > 2) {
usage("you must specify the target dir");
}
File target = new File(args[0]);
if (!target.exists()) {
usage("target dir must exist");
} else if (target.isFile()) {
usage("target dir must be a directory");
}
try {
generateAPI(target);
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
}
private static void generateAPI(File dir) throws IOException {
System.out.println("generating Java API:");
generateApplication(dir);
for (Component c : API.getComponents()) {
if (EXCLUDE_COMPONENTS.contains(c.getName())) {
continue;
}
generateComponent(c, dir);
}
}
private static void generateApplication(File dir) throws IOException {
System.out.println(" Application");
List<Action> actions = new ArrayList<Action>();
// add method for each component
for (Component c : API.getComponents()) {
if (EXCLUDE_COMPONENTS.contains(c.getName())) {
continue;
}
String name = c.getName().substring(0, 1).toLowerCase() + c.getName().substring(1);
Action a = new Action(name, c.getDescription(), Arrays.asList(new Arg("monkeyId",
"the monkeyId", "String")), c.getName(), "the " + c.getName() + " component");
actions.add(a);
}
// add 'raw' method
Action a = new Action("raw", "Send a raw text MonkeyTalk command to the app under test.",
Arrays.asList(new Arg("command", "the MonkeyTalk command", "String")), "String",
"the return value (as from a Get action), or {@code null} if it doesn't exist");
actions.add(a);
String app = makeInterface("Application",
"Helper for the MonkeyTalk application under test.", actions, false, false);
writeFile(new File(dir, "Application.java"), app);
}
private static void generateComponent(Component c, File dir) throws IOException {
System.out.println(" " + c.getName());
String comp = makeInterface(c.getName(), c.getDescription(), c.getActions(), false, true);
writeFile(new File(dir, c.getName() + ".java"), comp);
}
private static String makeInterface(String interfaceName, String description,
List<Action> actions, boolean includeModsVarArgs, boolean includeModsMap) {
List<String> methods = new ArrayList<String>();
String m;
StringBuilder sb = new StringBuilder();
sb.append("/**\n * " + description + "\n */\n");
sb.append("public interface " + interfaceName + " {\n");
for (Action a : actions) {
String name = a.getName().substring(0, 1).toLowerCase() + a.getName().substring(1);
// first, the empty method: foo()
m = makeMethod(name, a.getDescription(), a.getReturnType(),
(a.getReturnType() == "void" ? "" : (a.getReturnDescription() != null
&& a.getReturnDescription().length() > 0 ? a.getReturnDescription()
: "the " + a.getReturnType() + " component")), null, null);
if (!methods.contains(name + "()")) {
methods.add(name + "()");
sb.append(m);
}
// second, the mods methods...
if (includeModsVarArgs) {
m = makeMethod(name, a.getDescription(), a.getReturnType(),
(a.getReturnType() == "void" ? "" : (a.getReturnDescription() != null
&& a.getReturnDescription().length() > 0 ? a.getReturnDescription()
: "the " + a.getReturnType() + " component")), "String... mods",
Arrays.asList(new Arg("mods", "the MonkeyTalk modifiers", "String", true)));
if (!methods.contains(name + "(String... mods)")) {
methods.add(name + "(String... mods)");
sb.append(m);
}
}
if (includeModsMap) {
m = makeMethod(name, a.getDescription(), a.getReturnType(),
(a.getReturnType() == "void" ? "" : (a.getReturnDescription() != null
&& a.getReturnDescription().length() > 0 ? a.getReturnDescription()
: "the " + a.getReturnType() + " component")),
"Map<String, String> mods", Arrays.asList(new Arg("mods",
"the MonkeyTalk modifiers", "Map<String, String>")));
if (!methods.contains(name + "(Map<String, String> mods)")) {
methods.add(name + "(Map<String, String> mods)");
sb.append(m);
}
}
// third, all the methods appending any args one at a time
StringBuilder args = new StringBuilder();
List<Arg> argsList = new ArrayList<Arg>();
for (int i = 0; i < a.getArgs().size(); i++) {
// if action is Get or ExecAndReturn, then ignore the first arg (return var name)
if (i == 0 && ("get".equals(name) || "execAndReturn".equals(name))) {
continue;
}
Arg arg = a.getArgs().get(i);
argsList.add(arg);
args.append(args.length() > 0 ? ", " : "");
if (arg.isVarArgs()) {
StringBuilder args2 = new StringBuilder(args);
args2.append(arg.getType() + "... " + arg.getName());
m = makeMethod(name, a.getDescription(), a.getReturnType(),
a.getReturnDescription(), args2.toString(), argsList);
if (!methods.contains(name + "(" + args2.toString() + ")")) {
methods.add(name + "(" + args2.toString() + ")");
sb.append(m);
}
String type = ("int".equals(arg.getType()) ? "Integer" : arg.getType());
args.append("List<" + type + "> " + arg.getName());
} else {
args.append(arg.toParam());
}
m = makeMethod(name, a.getDescription(), a.getReturnType(),
a.getReturnDescription(), args.toString(), argsList);
if (!methods.contains(name + "(" + args.toString() + ")")) {
methods.add(name + "(" + args.toString() + ")");
sb.append(m);
}
// and the mods methods...
if (includeModsVarArgs) {
List<Arg> argsListModsVarArgs = new ArrayList<Arg>(argsList);
argsListModsVarArgs.add(new Arg("mods", "the MonkeyTalk modifiers", "String",
true));
m = makeMethod(name, a.getDescription(), a.getReturnType(),
a.getReturnDescription(), args.toString() + ", String... mods",
argsListModsVarArgs);
if (!methods.contains(name + "(" + args.toString() + ", String... mods)")) {
methods.add(name + "(" + args.toString() + ", String... mods)");
sb.append(m);
}
}
if (includeModsMap) {
List<Arg> argsListModsMap = new ArrayList<Arg>(argsList);
argsListModsMap.add(new Arg("mods", "the MonkeyTalk modifiers",
"Map<String, String>"));
m = makeMethod(name, a.getDescription(), a.getReturnType(),
a.getReturnDescription(), args.toString()
+ ", Map<String, String> mods", argsListModsMap);
if (!methods.contains(name + "(" + args.toString()
+ ", Map<String, String> mods)")) {
methods.add(name + "(" + args.toString() + ", Map<String, String> mods)");
sb.append(m);
}
}
}
sb.append("\n");
}
sb.delete(sb.length() - 1, sb.length());
sb.append("}\n");
if (sb.indexOf("Map<") != -1) {
sb.insert(0, "import java.util.Map;\n\n");
}
if (sb.indexOf("List<") != -1) {
sb.insert(0, "import java.util.List;\n");
}
sb.insert(0, "package com.gorillalogic.monkeytalk.java.api;\n\n");
sb.insert(0, LICENSE);
return sb.toString();
}
private static String makeMethod(String name, String description, String returnType,
String returnTypeDescription, String args, List<Arg> argList) {
System.out.println(" " + name + "(" + (args != null ? args : "") + ")");
StringBuilder paramDoc = new StringBuilder();
if (argList != null) {
for (Arg a : argList) {
paramDoc.append("\t * @param ").append(a.getName()).append(" ")
.append(a.getDescription()).append("\n");
}
}
String returnDoc = (returnTypeDescription != null && returnTypeDescription.length() > 0 ? "\t * @return "
+ returnTypeDescription + "\n"
: "");
return "\t/**\n\t * " + description + "\n" + paramDoc + returnDoc + "\t */\n\tpublic "
+ returnType + " " + name + "(" + (args != null ? args : "") + ");\n";
}
private static void writeFile(File f, String contents) throws IOException {
BufferedWriter out = new BufferedWriter(new FileWriter(f));
out.write(contents);
out.close();
}
private static void usage(String err) {
if (err != null) {
System.err.println("ERROR: " + err);
}
System.out.println("Usage: java JavaAPIGenerator <target>");
if (err != null) {
System.exit(1);
}
}
}