/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2012 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.api.js.tools;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import com.gorillalogic.monkeytalk.utils.FileUtils;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.JavaSource;
import com.thoughtworks.qdox.model.Type;
public class JSAPIGenerator {
/**
* When true, ignore all meta API sources in the {@code com.gorillalogic.monkeytalk.api.flex}
* package when generating the MonkeyTalkAPI.js file. NOTE: To fully ignore or un-ignore Flex,
* you must also see {@code MetaAPIGenerator}.
*/
private static final boolean IGNORE_FLEX = true;
// Lib
private final static String MONKEY_TALK_CORE = "MONKEY_TALK_CORE";
private final static String CLASSES = "CLASSES";
// Class
private final static String CLASS_DESC = "CLASS_DESC";
private final static String CLASS_EXTENDS = "CLASS_EXTENDS";
private final static String CLASS_NAME = "CLASS_NAME";
private final static String CLASS_FACTORY = "CLASS_FACTORY";
private final static String METHODS = "METHODS";
private final static String METHOD_NAME = "METHOD_NAME";
private final static String METHOD_DESC = "METHOD_DESC";
private final static String METHOD_ARGS = "METHOD_ARGS";
private final static String METHOD_RETURN = "METHOD_RETURN";
private final static String METHOD_RETURN_DESC = "METHOD_RETURN_DESC";
private final static String PARAMS = "PARAMS";
private final static String PARAM_NAME = "PARAM_NAME";
private final static String PARAM_TYPE = "PARAM_TYPE";
private final static String PARAM_DESC = "PARAM_DESC";
private static final String GEN_TIME = "GEN_TIME";
// private static final Template lib = new Template("templates/lib.template.js");
// private static final Template core = new Template("templates/MonkeyTalkCore.js");
// private static final Template cls = new Template("templates/class.template.js");
// private static final Template mth = new Template("templates/method.template.js");
// private static final Template prm = new Template("templates/param.template.js");
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
public static String createAPI(File srcDir) {
Template lib = new Template("templates/lib.template.js");
Template core = new Template("templates/MonkeyTalkCore.js");
Template cls = new Template("templates/class.template.js");
Template mth = new Template("templates/method.template.js");
Template prm = new Template("templates/param.template.js");
lib.init();
core.init();
cls.init();
mth.init();
prm.init();
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSourceTree(srcDir);
HashMap<String, String[]> classMap = new HashMap<String, String[]>();
StringBuffer classes = new StringBuffer();
for (JavaSource src : builder.getSources()) {
if (IGNORE_FLEX && src.getPackageName().endsWith(".flex")) {
// for (JavaClass clazz : src.getClasses()) {
// System.out.println("IGNORE " + clazz.getFullyQualifiedName());
// }
continue;
}
for (JavaClass clazz : src.getClasses()) {
if (clazz.getTagByName("ignoreJS") != null) {
continue;
}
System.out.println(clazz.getFullyQualifiedName());
String name = clazz.getName();
String extendz = (clazz.getImplements() == null
|| clazz.getImplements().length == 0
|| clazz.getImplements()[0].getJavaClass() == null
|| clazz.getImplements()[0].getJavaClass().getName()
.equalsIgnoreCase("mtobject") ? "MTObject"
: clazz.getImplements()[0].getJavaClass().getName());
// System.err.println(name + " extends " + extendz);
cls.replace(CLASS_NAME, name);
cls.replace(CLASS_DESC, cleanString(clazz.getComment()));
cls.replace(CLASS_EXTENDS, extendz);
cls.replace(CLASS_FACTORY, Template.lowerCamel(name));
StringBuilder methods = new StringBuilder();
for (JavaMethod meth : clazz.getMethods()) {
if (meth.getTagByName("ignoreJS") != null) {
continue;
}
mth.replace(CLASS_NAME, clazz.getName());
mth.replace(METHOD_NAME, meth.getName());
mth.replace(METHOD_DESC, cleanString(meth.getComment()));
Type rt = meth.getReturnType();
String rtype = rt == null ? "" : rt.getJavaClass().getName();
mth.replace(METHOD_RETURN, rtype);
DocletTag returns = meth.getTagByName("returns");
String r = returns != null ? returns.getValue() : "";
mth.replace(METHOD_RETURN_DESC, r);
String params = addParams(meth, clazz.getName(), prm);
mth.replace(PARAMS, params.toString());
// Actual arg declaration
StringBuilder arglist = new StringBuilder();
for (JavaParameter parameter : meth.getParameters()) {
if (arglist.length() > 0) {
arglist.append(", ");
}
arglist.append(parameter.getName());
}
mth.replace(METHOD_ARGS, arglist.toString());
methods.append(mth.toString());
mth.init();
}
cls.replace(METHODS, methods.toString());
classMap.put(name, new String[] { extendz, cls.toString() });
cls.init();
}
}
for (String name : classMap.keySet()) {
writeClass(classMap, classes, name);
}
lib.replace(MONKEY_TALK_CORE, core.toString());
lib.replace(CLASSES, classes.toString());
lib.replace(GEN_TIME, sdf.format(new Date()));
return lib.toString();
}
private static String cleanString(String s) {
if (s == null) {
return "";
}
return s.trim().replaceAll("\"", "'").replaceAll("\\n", "").replaceAll("\\s+", " ");
}
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java JSAPIGenerator <src dir> <target>");
System.exit(1);
}
File srcDir = new File(args[0]);
if (!srcDir.exists()) {
System.err.println("ERROR: srcDir '" + args[0] + "' not found");
System.err.println("ERROR: workingDir='" + new File(".").getAbsolutePath() + "'");
System.exit(1);
}
if (!srcDir.isDirectory()) {
System.err.println("ERROR: srcDir '" + srcDir.getAbsolutePath() + "' not directory");
System.exit(1);
}
File target = new File(args[1]);
if (!target.exists()) {
System.err.println("ERROR: target '" + args[1] + "' not found");
System.exit(1);
}
if (!target.isFile()) {
System.err.println("ERROR: target '" + args[1] + "' not file");
System.exit(1);
}
String api = createAPI(srcDir);
try {
FileUtils.writeFile(target.getAbsolutePath(), api);
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
}
private static String addParams(JavaMethod meth, String cname, Template prm) {
// For JSDoc params
HashSet<String> args = new HashSet<String>();
StringBuffer params = new StringBuffer();
for (DocletTag param : meth.getTagsByName("param")) {
String value = param.getValue();
int idx = value.indexOf("\n");
if (idx == -1) {
throw new RuntimeException("ERROR: " + cname + "#" + meth.getName()
+ "() - bad @param javadoc");
}
String pname = value.substring(0, idx);
String pdesc = value.substring(idx + 1);
String pnameTrimmed = pname;
if (pnameTrimmed != null) {
pnameTrimmed = pnameTrimmed.trim();
}
JavaParameter p = meth.getParameterByName(pnameTrimmed);
if (p == null) {
throw new RuntimeException("ERROR: " + cname + "#" + meth.getName()
+ "() - @param \"" + pnameTrimmed + "\" has no matching method argument");
}
String ptype = p.getType().getJavaClass().getName();
prm.replace(PARAM_NAME, pname);
prm.replace(PARAM_TYPE, ptype);
prm.replace(PARAM_DESC, cleanString(pdesc));
params.append(prm.toString());
prm.init();
args.add(pname);
}
for (JavaParameter parameter : meth.getParameters()) {
if (!args.contains(parameter.getName())) {
throw new RuntimeException("Unable to find javadoc @param for actual param \""
+ parameter.getName() + "\" in method " + cname + "#" + meth.getName());
}
}
return params.toString();
}
private static void writeClass(HashMap<String, String[]> classMap, StringBuffer classes,
String name) {
String[] value = classMap.get(name);
if (value[1] == null) {
return;
}
String extendz = value[0];
if (!extendz.equals("MTObject")) {
writeClass(classMap, classes, extendz);
}
if (value[1] != null) {
classes.append(value[1]);
value[1] = null;
}
}
}