New branch for vm-tests that use cts-tf framework.

Change-Id: I9927dc09027fc2298a1b1f7ee6c918ed1d36723a
diff --git a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
new file mode 100644
index 0000000..f584687
--- /dev/null
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2011 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 util.build;
+
+import com.android.dx.util.FileUtils;
+
+import dot.junit.AllTests;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Map.Entry;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Main class to generate data from the test suite to later run from a shell
+ * script. the project's home folder.<br>
+ * <project-home>/src must contain the java sources<br>
+ * <project-home>/data/scriptdata will be generated<br>
+ * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
+ * (one Main class for each test method in the Test_... class
+ */
+public class BuildDalvikSuite {
+
+    public static boolean DEBUG = true;
+
+    private static String JAVASRC_FOLDER = "";
+    private static String MAIN_SRC_OUTPUT_FOLDER = "";
+
+    // the folder for the generated junit-files for the cts host (which in turn
+    // execute the real vm tests using adb push/shell etc)
+    private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = "";
+    private static String OUTPUT_FOLDER = "";
+    private static String COMPILED_CLASSES_FOLDER = "";
+
+    private static String CLASSES_OUTPUT_FOLDER = "";
+    private static String HOSTJUNIT_CLASSES_OUTPUT_FOLDER = "";
+
+    private static String CLASS_PATH = "";
+
+    private static String restrictTo = null; // e.g. restrict to
+    // "opcodes.add_double"
+
+    private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests";
+
+    private int testClassCnt = 0;
+    private int testMethodsCnt = 0;
+
+    /*
+     * using a linked hashmap to keep the insertion order for iterators.
+     * the junit suite/tests adding order is used to generate the order of the
+     * report.
+     * a map. key: fully qualified class name, value: a list of test methods for
+     * the given class
+     */
+    private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String,
+    List<String>>();
+
+    private class MethodData {
+        String methodBody, constraint, title;
+    }
+
+    /**
+     * @param args
+     *            args 0 must be the project root folder (where src, lib etc.
+     *            resides)
+     * @throws IOException
+     */
+    public static void main(String[] args) throws IOException {
+
+        if (args.length > 5) {
+            JAVASRC_FOLDER = args[0];
+            OUTPUT_FOLDER = args[1];
+            CLASS_PATH = args[2];
+            MAIN_SRC_OUTPUT_FOLDER = args[3];
+            CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes";
+
+            COMPILED_CLASSES_FOLDER = args[4];
+
+            HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5];
+            HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes";
+
+            if (args.length > 6) {
+                // optional: restrict to e.g. "opcodes.add_double"
+                restrictTo = args[6];
+                System.out.println("restricting build to: " + restrictTo);
+            }
+
+        } else {
+            System.out.println("usage: java-src-folder output-folder classpath " +
+                    "generated-main-files compiled_output generated-main-files " +
+            "[restrict-to-opcode]");
+            System.exit(-1);
+        }
+
+        long start = System.currentTimeMillis();
+        BuildDalvikSuite cat = new BuildDalvikSuite();
+        cat.compose();
+        long end = System.currentTimeMillis();
+
+        System.out.println("elapsed seconds: " + (end - start) / 1000);
+    }
+
+    public void compose() throws IOException {
+        System.out.println("Collecting all junit tests...");
+        new TestRunner() {
+            @Override
+            protected TestResult createTestResult() {
+                return new TestResult() {
+                    @Override
+                    protected void run(TestCase test) {
+                        addToTests(test);
+                    }
+
+                };
+            }
+        }.doRun(AllTests.suite());
+
+        // for each combination of TestClass and method, generate a Main_testN1
+        // etc.
+        // class in the respective package.
+        // for the report make sure all N... tests are called first, then B,
+        // then
+        // E, then VFE test methods.
+        // so we need x Main_xxxx methods in a package, and x entries in the
+        // global scriptdata file (read by a bash script for the tests)
+        // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
+        // File Main_testN1.java in package dxc.junit.opcodes.aaload
+        // and entry dxc.junit.opcodes.aaload.Main_testN1 in class execution
+        // table.
+        //
+        handleTests();
+    }
+
+    private void addToTests(TestCase test) {
+
+        String packageName = test.getClass().getPackage().getName();
+        packageName = packageName.substring(packageName.lastIndexOf('.'));
+
+
+        String method = test.getName(); // e.g. testVFE2
+        String fqcn = test.getClass().getName(); // e.g.
+        // dxc.junit.opcodes.iload_3.Test_iload_3
+
+        // ignore all tests not belonging to the given restriction
+        if (restrictTo != null && !fqcn.contains(restrictTo)) return;
+
+        testMethodsCnt++;
+        List<String> li = map.get(fqcn);
+        if (li == null) {
+            testClassCnt++;
+            li = new ArrayList<String>();
+            map.put(fqcn, li);
+        }
+        li.add(method);
+    }
+
+    private static final String ctsAllTestsB =
+        "package dot.junit;\n" +
+        "import junit.framework.Test;\n" +
+        "import junit.framework.TestSuite;\n" +
+        "import com.android.hosttest.DeviceTestSuite;\n" +
+        "\n" +
+        "public class AllJunitHostTests extends DeviceTestSuite {\n" +
+        "    public static final Test suite() {\n" +
+        "        TestSuite suite = new TestSuite(\"CTS Host tests for all " +
+        " dalvik vm opcodes\");\n";
+
+    private String curAllTestsData = ctsAllTestsB;
+    private String curJunitFileName = null;
+    private String curJunitFileData = "";
+
+    private JavacBuildStep javacHostJunitBuildStep;
+
+    private void flushHostJunitFile() {
+        if (curJunitFileName != null) {
+            File toWrite = new File(curJunitFileName);
+            String absPath = toWrite.getAbsolutePath();
+            // add to java source files for later compilation
+            javacHostJunitBuildStep.addSourceFile(absPath);
+            // write file
+            curJunitFileData += "\n}\n";
+            writeToFileMkdir(toWrite, curJunitFileData);
+            curJunitFileName = null;
+            curJunitFileData = "";
+        }
+    }
+
+    private void ctsFinish() {
+        flushHostJunitFile();
+        curAllTestsData += "return suite;\n}\n}\n";
+        // suite is in package dot.junit.
+        String allTestsFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/dot/junit/AllJunitHostTests.java";
+        File toWrite = new File(allTestsFileName);
+        writeToFileMkdir(toWrite, curAllTestsData);
+        javacHostJunitBuildStep.addSourceFile(toWrite.getAbsolutePath());
+        javacHostJunitBuildStep.addSourceFile(new File(
+                HOSTJUNIT_SRC_OUTPUT_FOLDER + "/dot/junit/DeviceUtil.java").
+                getAbsolutePath());
+    }
+
+    private void openCTSHostFileFor(String pName, String classOnlyName) {
+        String sourceName = "JUnit_" + classOnlyName;
+        // Add to AllTests.java
+        String suiteline = "suite.addTestSuite(" + pName + "." + sourceName +
+        ".class);\n";
+        curAllTestsData += suiteline;
+        // flush previous JunitFile
+        flushHostJunitFile();
+
+        // prepare current testcase-file
+        curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" +
+        sourceName + ".java";
+        curJunitFileData = getWarningMessage() +
+        "package " + pName + ";\n" +
+        "import java.io.IOException;\n" +
+        "import junit.framework.TestCase;\n" +
+        "import com.android.hosttest.DeviceTestCase;\n" +
+        "import dot.junit.DeviceUtil;\n" +
+        "\n" +
+        "public class " + sourceName + " extends DeviceTestCase {\n";
+    }
+
+    private String getADBExecJavaLine(String classpath, String mainclass) {
+        return "DeviceUtil.adbExec(getDevice(), \"" + TARGET_JAR_ROOT_PATH + "\", \"" + classpath +
+        "\", \"" + mainclass + "\");";
+    }
+
+    private String getWarningMessage() {
+        return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
+    }
+
+    private void addCTSHostMethod(String pName, String method, MethodData md,
+            Set<String> dependentTestClassNames) {
+        curJunitFileData += "public void " + method + "() throws Exception {\n";
+        final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
+                TARGET_JAR_ROOT_PATH);
+
+        // push class with Main jar.
+        String mjar = "Main_" + method + ".jar";
+        String pPath = pName.replaceAll("\\.","/");
+        String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
+
+        // for each dependency:
+        // adb push dot/junit/opcodes/add_double_2addr/Main_testN2.jar
+        // /data/local/tmp/Main_testN2.jar
+        String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
+        for (String depFqcn : dependentTestClassNames) {
+            int lastDotPos = depFqcn.lastIndexOf('.');
+            String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
+            String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
+                    sourceName);
+            cp += ":" + targetName;
+            // dot.junit.opcodes.invoke_interface_range.ITest
+            // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
+        }
+
+        //"dot.junit.opcodes.add_double_2addr.Main_testN2";
+        String mainclass = pName + ".Main_" + method;
+        curJunitFileData += "    " + getADBExecJavaLine(cp, mainclass);
+        curJunitFileData += "}\n\n";
+    }
+
+    private void handleTests() throws IOException {
+        System.out.println("collected " + testMethodsCnt + " test methods in " +
+                testClassCnt + " junit test classes");
+        String datafileContent = "";
+        Set<BuildStep> targets = new TreeSet<BuildStep>();
+
+        javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
+
+
+        JavacBuildStep javacBuildStep = new JavacBuildStep(
+                CLASSES_OUTPUT_FOLDER, CLASS_PATH);
+
+        for (Entry<String, List<String>> entry : map.entrySet()) {
+
+            String fqcn = entry.getKey();
+            int lastDotPos = fqcn.lastIndexOf('.');
+            String pName = fqcn.substring(0, lastDotPos);
+            String classOnlyName = fqcn.substring(lastDotPos + 1);
+            String instPrefix = "new " + classOnlyName + "()";
+
+            openCTSHostFileFor(pName, classOnlyName);
+
+            List<String> methods = entry.getValue();
+            Collections.sort(methods, new Comparator<String>() {
+                public int compare(String s1, String s2) {
+                    // TODO sort according: test ... N, B, E, VFE
+                    return s1.compareTo(s2);
+                }
+            });
+            for (String method : methods) {
+                // e.g. testN1
+                if (!method.startsWith("test")) {
+                    throw new RuntimeException("no test method: " + method);
+                }
+
+                // generate the Main_xx java class
+
+                // a Main_testXXX.java contains:
+                // package <packagenamehere>;
+                // public class Main_testxxx {
+                // public static void main(String[] args) {
+                // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
+                // }
+                // }
+                MethodData md = parseTestMethod(pName, classOnlyName, method);
+                String methodContent = md.methodBody;
+
+                Set<String> dependentTestClassNames = parseTestClassName(pName,
+                        classOnlyName, methodContent);
+
+                addCTSHostMethod(pName, method, md, dependentTestClassNames);
+
+
+                if (dependentTestClassNames.isEmpty()) {
+                    continue;
+                }
+
+
+                String content = getWarningMessage() +
+                "package " + pName + ";\n" +
+                "import " + pName + ".d.*;\n" +
+                "import dot.junit.*;\n" +
+                "public class Main_" + method + " extends DxAbstractMain {\n" +
+                "    public static void main(String[] args) throws Exception {" +
+                methodContent + "\n}\n";
+
+                String fileName = getFileName(pName, method, ".java");
+                File sourceFile = getFileFromPackage(pName, method);
+
+                File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
+                        getFileName(pName, method, ".class"));
+                // if (sourceFile.lastModified() > classFile.lastModified()) {
+                writeToFile(sourceFile, content);
+                javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
+
+                BuildStep dexBuildStep = generateDexBuildStep(
+                        CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
+                targets.add(dexBuildStep);
+                // }
+
+
+                // prepare the entry in the data file for the bash script.
+                // e.g.
+                // main class to execute; opcode/constraint; test purpose
+                // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
+                // (#1)
+
+                char ca = method.charAt("test".length()); // either N,B,E,
+                // or V (VFE)
+                String comment;
+                switch (ca) {
+                    case 'N':
+                        comment = "Normal #" + method.substring(5);
+                        break;
+                    case 'B':
+                        comment = "Boundary #" + method.substring(5);
+                        break;
+                    case 'E':
+                        comment = "Exception #" + method.substring(5);
+                        break;
+                    case 'V':
+                        comment = "Verifier #" + method.substring(7);
+                        break;
+                    default:
+                        throw new RuntimeException("unknown test abbreviation:" + method + " for " +
+                                fqcn);
+                }
+
+                String line = pName + ".Main_" + method + ";";
+                for (String className : dependentTestClassNames) {
+                    line += className + " ";
+                }
+
+
+                // test description
+                String[] pparts = pName.split("\\.");
+                // detail e.g. add_double
+                String detail = pparts[pparts.length-1];
+                // type := opcode | verify
+                String type = pparts[pparts.length-2];
+
+                String description;
+                if ("format".equals(type)) {
+                    description = "format";
+                } else if ("opcodes".equals(type)) {
+                    // Beautify name, so it matches the actual mnemonic
+                    detail = detail.replaceAll("_", "-");
+                    detail = detail.replace("-from16", "/from16");
+                    detail = detail.replace("-high16", "/high16");
+                    detail = detail.replace("-lit8", "/lit8");
+                    detail = detail.replace("-lit16", "/lit16");
+                    detail = detail.replace("-4", "/4");
+                    detail = detail.replace("-16", "/16");
+                    detail = detail.replace("-32", "/32");
+                    detail = detail.replace("-jumbo", "/jumbo");
+                    detail = detail.replace("-range", "/range");
+                    detail = detail.replace("-2addr", "/2addr");
+
+                    // Unescape reserved words
+                    detail = detail.replace("opc-", "");
+
+                    description = detail;
+                } else if ("verify".equals(type)) {
+                    description = "verifier";
+                } else {
+                    description = type + " " + detail;
+                }
+
+                String details = (md.title != null ? md.title : "");
+                if (md.constraint != null) {
+                    details = " Constraint " + md.constraint + ", " + details;
+                }
+                if (details.length() != 0) {
+                    details = details.substring(0, 1).toUpperCase() + details.substring(1);
+                }
+
+                line += ";" + description + ";" + comment + ";" + details;
+
+                datafileContent += line + "\n";
+                generateBuildStepFor(pName, method, dependentTestClassNames,
+                        targets);
+            }
+
+
+        }
+
+        // write latest HOSTJUNIT generated file and AllTests.java
+        ctsFinish();
+
+        File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
+        scriptDataDir.mkdirs();
+        writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
+
+        if (!javacHostJunitBuildStep.build()) {
+            System.out.println("main javac cts-host-hostjunit-classes build step failed");
+            System.exit(1);
+        }
+
+        if (javacBuildStep.build()) {
+            for (BuildStep buildStep : targets) {
+                if (!buildStep.build()) {
+                    System.out.println("building failed. buildStep: " +
+                            buildStep.getClass().getName() + ", " + buildStep);
+                    System.exit(1);
+                }
+            }
+        } else {
+            System.out.println("main javac dalvik-cts-buildutil build step failed");
+            System.exit(1);
+        }
+    }
+
+    private void generateBuildStepFor(String pName, String method,
+            Set<String> dependentTestClassNames, Set<BuildStep> targets) {
+
+
+        for (String dependentTestClassName : dependentTestClassNames) {
+            generateBuildStepForDependant(dependentTestClassName, targets);
+        }
+    }
+
+    private void generateBuildStepForDependant(String dependentTestClassName,
+            Set<BuildStep> targets) {
+
+        File sourceFolder = new File(JAVASRC_FOLDER);
+        String fileName = dependentTestClassName.replace('.', '/').trim();
+
+        if (new File(sourceFolder, fileName + ".dfh").exists()) {
+
+            BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
+                    JAVASRC_FOLDER, fileName + ".dfh");
+            BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".dex");
+
+            DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
+
+            BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".jar");
+            JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
+                    "classes.dex", jarFile, true);
+            jarBuildStep.addChild(buildStep);
+
+            targets.add(jarBuildStep);
+            return;
+        }
+
+        if (new File(sourceFolder, fileName + ".d").exists()) {
+
+            BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
+                    JAVASRC_FOLDER, fileName + ".d");
+            BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".dex");
+
+            DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
+
+            BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
+                    OUTPUT_FOLDER, fileName + ".jar");
+
+            JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
+                    "classes.dex", jarFile, true);
+            jarBuildStep.addChild(buildStep);
+            targets.add(jarBuildStep);
+            return;
+        }
+
+        if (new File(sourceFolder, fileName + ".java").exists()) {
+
+            BuildStep dexBuildStep = generateDexBuildStep(
+                    COMPILED_CLASSES_FOLDER, fileName);
+            targets.add(dexBuildStep);
+            return;
+        }
+
+        try {
+            if (Class.forName(dependentTestClassName) != null) {
+
+                BuildStep dexBuildStep = generateDexBuildStep(
+                        COMPILED_CLASSES_FOLDER, fileName);
+                targets.add(dexBuildStep);
+                return;
+            }
+        } catch (ClassNotFoundException e) {
+            // do nothing
+        }
+
+        throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
+                dependentTestClassName + ";" + fileName);
+    }
+
+    private BuildStep generateDexBuildStep(String classFileFolder,
+            String classFileName) {
+        BuildStep.BuildFile classFile = new BuildStep.BuildFile(
+                classFileFolder, classFileName + ".class");
+
+        BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
+                classFileName + "_tmp.jar");
+
+        JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
+                ".class", tmpJarFile, false);
+
+        BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
+                classFileName + ".jar");
+
+        DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
+                true);
+
+        dexBuildStep.addChild(jarBuildStep);
+        return dexBuildStep;
+
+    }
+
+    /**
+     * @param pName
+     * @param classOnlyName
+     * @param methodSource
+     * @return testclass names
+     */
+    private Set<String> parseTestClassName(String pName, String classOnlyName,
+            String methodSource) {
+        Set<String> entries = new HashSet<String>();
+        String opcodeName = classOnlyName.substring(5);
+
+        Scanner scanner = new Scanner(methodSource);
+
+        String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
+                "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
+
+        String token = null;
+        for (String pattern : patterns) {
+            token = scanner.findWithinHorizon(pattern, methodSource.length());
+            if (token != null) {
+                break;
+            }
+        }
+
+        if (token == null) {
+            System.err.println("warning: failed to find dependent test class name: " + pName +
+                    ", " + classOnlyName + " in methodSource:\n" + methodSource);
+            return entries;
+        }
+
+        MatchResult result = scanner.match();
+
+        entries.add((pName + ".d." + result.group(1)).trim());
+
+        // search additional @uses directives
+        Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
+        Matcher m = p.matcher(methodSource);
+        while (m.find()) {
+            String res = m.group(1);
+            entries.add(res.trim());
+        }
+
+        // lines with the form @uses
+        // dot.junit.opcodes.add_double.jm.T_add_double_2
+        // one dependency per one @uses
+        // TODO
+
+        return entries;
+    }
+
+    private MethodData parseTestMethod(String pname, String classOnlyName,
+            String method) {
+
+        String path = pname.replaceAll("\\.", "/");
+        String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
+        File f = new File(absPath);
+
+        Scanner scanner;
+        try {
+            scanner = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+        
+        String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
+        
+        String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
+        if (token == null) {
+            throw new RuntimeException("cannot find method source of 'public void " + method +
+                    "' in file '" + absPath + "'");
+        }
+
+        MatchResult result = scanner.match();
+        result.start();
+        result.end();
+
+        StringBuilder builder = new StringBuilder();
+        //builder.append(token);
+
+        try {
+            FileReader reader = new FileReader(f);
+            reader.skip(result.end());
+
+            char currentChar;
+            int blocks = 1;
+            while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
+                switch (currentChar) {
+                    case '}': {
+                        blocks--;
+                        builder.append(currentChar);
+                        break;
+                    }
+                    case '{': {
+                        blocks++;
+                        builder.append(currentChar);
+                        break;
+                    }
+                    default: {
+                        builder.append(currentChar);
+                        break;
+                    }
+                }
+            }
+            if (reader != null) {
+                reader.close();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("failed to parse", e);
+        }
+
+        // find the @title/@constraint in javadoc comment for this method
+        Scanner scanner2;
+        try {
+            // using platform's default charset
+            scanner2 = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+        // using platform's default charset
+        String all = new String(FileUtils.readFile(f));
+        // System.out.println("grepping javadoc found for method " + method +
+        // " in " + pname + "," + classOnlyName);
+        String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
+        Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
+        Matcher m = p.matcher(all);
+        String title = null, constraint = null;
+        if (m.find()) {
+            String res = m.group(1);
+            // System.out.println("res: " + res);
+            // now grep @title and @constraint
+            Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
+            .matcher(res);
+            if (titleM.find()) {
+                title = titleM.group(1).replaceAll("\\n     \\*", "");
+                title = title.replaceAll("\\n", " ");
+                title = title.trim();
+                // System.out.println("title: " + title);
+            } else {
+                System.err.println("warning: no @title found for method " + method + " in " + pname +
+                        "," + classOnlyName);
+            }
+            // constraint can be one line only
+            Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
+                    res);
+            if (constraintM.find()) {
+                constraint = constraintM.group(1);
+                constraint = constraint.trim();
+                // System.out.println("constraint: " + constraint);
+            } else if (method.contains("VFE")) {
+                System.err
+                .println("warning: no @constraint for for a VFE method:" + method + " in " +
+                        pname + "," + classOnlyName);
+            }
+        } else {
+            System.err.println("warning: no javadoc found for method " + method + " in " + pname +
+                    "," + classOnlyName);
+        }
+        MethodData md = new MethodData();
+        md.methodBody = builder.toString();
+        md.constraint = constraint;
+        md.title = title;
+        if (scanner != null) {
+            scanner.close();
+        }
+        if (scanner2 != null) {
+            scanner.close();
+        }
+        return md;
+    }
+
+    private void writeToFileMkdir(File file, String content) {
+        File parent = file.getParentFile();
+        if (!parent.exists() && !parent.mkdirs()) {
+            throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
+        }
+        writeToFile(file, content);
+    }
+
+    private void writeToFile(File file, String content) {
+        try {
+            if (file.length() == content.length()) {
+                FileReader reader = new FileReader(file);
+                char[] charContents = new char[(int) file.length()];
+                reader.read(charContents);
+                String contents = new String(charContents);
+                if (contents.equals(content)) {
+                    // System.out.println("skipping identical: "
+                    // + file.getAbsolutePath());
+                    return;
+                }
+            }
+
+            //System.out.println("writing file " + file.getAbsolutePath());
+
+            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
+                    new FileOutputStream(file), "utf-8"));
+            bw.write(content);
+            bw.close();
+        } catch (Exception e) {
+            throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
+                    ", msg:" + e.getMessage());
+        }
+    }
+
+    private File getFileFromPackage(String pname, String methodName)
+    throws IOException {
+        // e.g. dxc.junit.argsreturns.pargsreturn
+        String path = getFileName(pname, methodName, ".java");
+        String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
+        File dirPath = new File(absPath);
+        File parent = dirPath.getParentFile();
+        if (!parent.exists() && !parent.mkdirs()) {
+            throw new IOException("failed to create directory: " + absPath);
+        }
+        return dirPath;
+    }
+
+    private String getFileName(String pname, String methodName,
+            String extension) {
+        String path = pname.replaceAll("\\.", "/");
+        return new File(path, "Main_" + methodName + extension).getPath();
+    }
+}