• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package util.build;
18 
19 import com.android.dex.util.FileUtils;
20 
21 import dot.junit.AllTests;
22 
23 import junit.framework.TestCase;
24 import junit.framework.TestResult;
25 import junit.framework.TestSuite;
26 import junit.textui.TestRunner;
27 
28 import java.io.BufferedWriter;
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.FileOutputStream;
32 import java.io.FileReader;
33 import java.io.IOException;
34 import java.io.OutputStreamWriter;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.LinkedHashMap;
41 import java.util.List;
42 import java.util.Scanner;
43 import java.util.Set;
44 import java.util.TreeSet;
45 import java.util.Map.Entry;
46 import java.util.regex.MatchResult;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49 
50 /**
51  * Main class to generate data from the test suite to later run from a shell
52  * script. the project's home folder.<br>
53  * <project-home>/src must contain the java sources<br>
54  * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
55  * (one Main class for each test method in the Test_... class
56  */
57 public class BuildDalvikSuite {
58 
59     public static boolean DEBUG = true;
60 
61     private static String JAVASRC_FOLDER = "";
62     private static String MAIN_SRC_OUTPUT_FOLDER = "";
63 
64     // the folder for the generated junit-files for the cts host (which in turn
65     // execute the real vm tests using adb push/shell etc)
66     private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = "";
67     private static String OUTPUT_FOLDER = "";
68     private static String COMPILED_CLASSES_FOLDER = "";
69 
70     private static String CLASSES_OUTPUT_FOLDER = "";
71     private static String HOSTJUNIT_CLASSES_OUTPUT_FOLDER = "";
72 
73     private static String CLASS_PATH = "";
74 
75     private static String restrictTo = null; // e.g. restrict to "opcodes.add_double"
76 
77     private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests";
78 
79     private int testClassCnt = 0;
80     private int testMethodsCnt = 0;
81 
82     /*
83      * using a linked hashmap to keep the insertion order for iterators.
84      * the junit suite/tests adding order is used to generate the order of the
85      * report.
86      * a map. key: fully qualified class name, value: a list of test methods for
87      * the given class
88      */
89     private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String,
90     List<String>>();
91 
92     private class MethodData {
93         String methodBody, constraint, title;
94     }
95 
96     /**
97      * @param args
98      *            args 0 must be the project root folder (where src, lib etc.
99      *            resides)
100      * @throws IOException
101      */
main(String[] args)102     public static void main(String[] args) throws IOException {
103 
104         if (args.length > 5) {
105             JAVASRC_FOLDER = args[0];
106             OUTPUT_FOLDER = args[1];
107             CLASS_PATH = args[2];
108             MAIN_SRC_OUTPUT_FOLDER = args[3];
109             CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes";
110 
111             COMPILED_CLASSES_FOLDER = args[4];
112 
113             HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5];
114             HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes";
115 
116             if (args.length > 6) {
117                 // optional: restrict to e.g. "opcodes.add_double"
118                 restrictTo = args[6];
119                 System.out.println("restricting build to: " + restrictTo);
120             }
121 
122         } else {
123             System.out.println("usage: java-src-folder output-folder classpath " +
124                     "generated-main-files compiled_output generated-main-files " +
125             "[restrict-to-opcode]");
126             System.exit(-1);
127         }
128 
129         long start = System.currentTimeMillis();
130         BuildDalvikSuite cat = new BuildDalvikSuite();
131         cat.compose();
132         long end = System.currentTimeMillis();
133 
134         System.out.println("elapsed seconds: " + (end - start) / 1000);
135     }
136 
compose()137     public void compose() throws IOException {
138         System.out.println("Collecting all junit tests...");
139         new TestRunner() {
140             @Override
141             protected TestResult createTestResult() {
142                 return new TestResult() {
143                     @Override
144                     protected void run(TestCase test) {
145                         addToTests(test);
146                     }
147 
148                 };
149             }
150         }.doRun(AllTests.suite());
151 
152         // for each combination of TestClass and method, generate a Main_testN1
153         // class in the respective package.
154         // for the report make sure all N... tests are called first, then B,
155         // then E, then VFE test methods.
156         // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
157         // File Main_testN1.java in package dxc.junit.opcodes.aaload.
158         //
159         handleTests();
160     }
161 
162     private void addToTests(TestCase test) {
163 
164         String packageName = test.getClass().getPackage().getName();
165         packageName = packageName.substring(packageName.lastIndexOf('.'));
166 
167 
168         String method = test.getName(); // e.g. testVFE2
169         String fqcn = test.getClass().getName(); // e.g.
170         // dxc.junit.opcodes.iload_3.Test_iload_3
171 
172         // ignore all tests not belonging to the given restriction
173         if (restrictTo != null && !fqcn.contains(restrictTo)) return;
174 
175         testMethodsCnt++;
176         List<String> li = map.get(fqcn);
177         if (li == null) {
178             testClassCnt++;
179             li = new ArrayList<String>();
180             map.put(fqcn, li);
181         }
182         li.add(method);
183     }
184     private String curJunitFileName = null;
185     private String curJunitFileData = "";
186 
187     private JavacBuildStep javacHostJunitBuildStep;
188 
189     private void flushHostJunitFile() {
190         if (curJunitFileName != null) {
191             File toWrite = new File(curJunitFileName);
192             String absPath = toWrite.getAbsolutePath();
193             // add to java source files for later compilation
194             javacHostJunitBuildStep.addSourceFile(absPath);
195             // write file
196             curJunitFileData += "\n}\n";
197             writeToFileMkdir(toWrite, curJunitFileData);
198             curJunitFileName = null;
199             curJunitFileData = "";
200         }
201     }
202 
203     private void openCTSHostFileFor(String pName, String classOnlyName) {
204         // flush previous JunitFile
205         flushHostJunitFile();
206         String sourceName = "JUnit_" + classOnlyName;
207 
208         // prepare current testcase-file
209         curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" +
210         sourceName + ".java";
211         curJunitFileData = getWarningMessage() +
212         "package " + pName + ";\n" +
213         "import java.io.IOException;\n" +
214         "import com.android.tradefed.testtype.IAbi;\n" +
215         "import com.android.tradefed.testtype.IAbiReceiver;\n" +
216         "import com.android.tradefed.testtype.DeviceTestCase;\n" +
217         "import com.android.tradefed.util.AbiFormatter;\n" +
218         "\n" +
219         "public class " + sourceName + " extends DeviceTestCase implements IAbiReceiver {\n";
220     }
221 
222     private String getShellExecJavaLine(String classpath, String mainclass) {
223       String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " +
224               "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
225               classpath, mainclass);
226       return "    String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mAbi.getBitness());\n" +
227               "    String res = getDevice().executeShellCommand(cmd);\n" +
228               "    // A sucessful adb shell command returns an empty string.\n" +
229               "    assertEquals(cmd, \"\", res);";
230     }
231 
232     private String getWarningMessage() {
233         return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
234     }
235 
236     private void addCTSHostMethod(String pName, String method, MethodData md,
237             Set<String> dependentTestClassNames) {
238         curJunitFileData += "public void " + method + "() throws Exception {\n";
239         final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
240                 TARGET_JAR_ROOT_PATH);
241 
242         // push class with Main jar.
243         String mjar = "Main_" + method + ".jar";
244         String pPath = pName.replaceAll("\\.","/");
245         String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
246 
247         String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
248         for (String depFqcn : dependentTestClassNames) {
249             int lastDotPos = depFqcn.lastIndexOf('.');
250             String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
251             String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
252                     sourceName);
253             cp += ":" + targetName;
254             // dot.junit.opcodes.invoke_interface_range.ITest
255             // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
256         }
257 
258         //"dot.junit.opcodes.add_double_2addr.Main_testN2";
259         String mainclass = pName + ".Main_" + method;
260         curJunitFileData += getShellExecJavaLine(cp, mainclass);
261         curJunitFileData += "\n}\n\n";
262     }
263 
264     private void handleTests() throws IOException {
265         System.out.println("collected " + testMethodsCnt + " test methods in " +
266                 testClassCnt + " junit test classes");
267         String datafileContent = "";
268         Set<BuildStep> targets = new TreeSet<BuildStep>();
269 
270         javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
271 
272 
273         JavacBuildStep javacBuildStep = new JavacBuildStep(
274                 CLASSES_OUTPUT_FOLDER, CLASS_PATH);
275 
276         for (Entry<String, List<String>> entry : map.entrySet()) {
277 
278             String fqcn = entry.getKey();
279             int lastDotPos = fqcn.lastIndexOf('.');
280             String pName = fqcn.substring(0, lastDotPos);
281             String classOnlyName = fqcn.substring(lastDotPos + 1);
282             String instPrefix = "new " + classOnlyName + "()";
283 
284             openCTSHostFileFor(pName, classOnlyName);
285 
286             curJunitFileData += "\n" +
287                     "protected IAbi mAbi;\n" +
288                     "@Override\n" +
289                     "public void setAbi(IAbi abi) {\n" +
290                     "    mAbi = abi;\n" +
291                     "}\n\n";
292 
293             List<String> methods = entry.getValue();
294             Collections.sort(methods, new Comparator<String>() {
295                 public int compare(String s1, String s2) {
296                     // TODO sort according: test ... N, B, E, VFE
297                     return s1.compareTo(s2);
298                 }
299             });
300             for (String method : methods) {
301                 // e.g. testN1
302                 if (!method.startsWith("test")) {
303                     throw new RuntimeException("no test method: " + method);
304                 }
305 
306                 // generate the Main_xx java class
307 
308                 // a Main_testXXX.java contains:
309                 // package <packagenamehere>;
310                 // public class Main_testxxx {
311                 // public static void main(String[] args) {
312                 // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
313                 // }
314                 // }
315                 MethodData md = parseTestMethod(pName, classOnlyName, method);
316                 String methodContent = md.methodBody;
317 
318                 Set<String> dependentTestClassNames = parseTestClassName(pName,
319                         classOnlyName, methodContent);
320 
321                 addCTSHostMethod(pName, method, md, dependentTestClassNames);
322 
323 
324                 if (dependentTestClassNames.isEmpty()) {
325                     continue;
326                 }
327 
328 
329                 String content = getWarningMessage() +
330                 "package " + pName + ";\n" +
331                 "import " + pName + ".d.*;\n" +
332                 "import dot.junit.*;\n" +
333                 "public class Main_" + method + " extends DxAbstractMain {\n" +
334                 "    public static void main(String[] args) throws Exception {" +
335                 methodContent + "\n}\n";
336 
337                 String fileName = getFileName(pName, method, ".java");
338                 File sourceFile = getFileFromPackage(pName, method);
339 
340                 File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
341                         getFileName(pName, method, ".class"));
342                 // if (sourceFile.lastModified() > classFile.lastModified()) {
343                 writeToFile(sourceFile, content);
344                 javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
345 
346                 BuildStep dexBuildStep = generateDexBuildStep(
347                         CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
348                 targets.add(dexBuildStep);
349                 // }
350 
351 
352                 // prepare the entry in the data file for the bash script.
353                 // e.g.
354                 // main class to execute; opcode/constraint; test purpose
355                 // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
356                 // (#1)
357 
358                 char ca = method.charAt("test".length()); // either N,B,E,
359                 // or V (VFE)
360                 String comment;
361                 switch (ca) {
362                 case 'N':
363                     comment = "Normal #" + method.substring(5);
364                     break;
365                 case 'B':
366                     comment = "Boundary #" + method.substring(5);
367                     break;
368                 case 'E':
369                     comment = "Exception #" + method.substring(5);
370                     break;
371                 case 'V':
372                     comment = "Verifier #" + method.substring(7);
373                     break;
374                 default:
375                     throw new RuntimeException("unknown test abbreviation:"
376                             + method + " for " + fqcn);
377                 }
378 
379                 String line = pName + ".Main_" + method + ";";
380                 for (String className : dependentTestClassNames) {
381                     line += className + " ";
382                 }
383 
384 
385                 // test description
386                 String[] pparts = pName.split("\\.");
387                 // detail e.g. add_double
388                 String detail = pparts[pparts.length-1];
389                 // type := opcode | verify
390                 String type = pparts[pparts.length-2];
391 
392                 String description;
393                 if ("format".equals(type)) {
394                     description = "format";
395                 } else if ("opcodes".equals(type)) {
396                     // Beautify name, so it matches the actual mnemonic
397                     detail = detail.replaceAll("_", "-");
398                     detail = detail.replace("-from16", "/from16");
399                     detail = detail.replace("-high16", "/high16");
400                     detail = detail.replace("-lit8", "/lit8");
401                     detail = detail.replace("-lit16", "/lit16");
402                     detail = detail.replace("-4", "/4");
403                     detail = detail.replace("-16", "/16");
404                     detail = detail.replace("-32", "/32");
405                     detail = detail.replace("-jumbo", "/jumbo");
406                     detail = detail.replace("-range", "/range");
407                     detail = detail.replace("-2addr", "/2addr");
408 
409                     // Unescape reserved words
410                     detail = detail.replace("opc-", "");
411 
412                     description = detail;
413                 } else if ("verify".equals(type)) {
414                     description = "verifier";
415                 } else {
416                     description = type + " " + detail;
417                 }
418 
419                 String details = (md.title != null ? md.title : "");
420                 if (md.constraint != null) {
421                     details = " Constraint " + md.constraint + ", " + details;
422                 }
423                 if (details.length() != 0) {
424                     details = details.substring(0, 1).toUpperCase()
425                             + details.substring(1);
426                 }
427 
428                 line += ";" + description + ";" + comment + ";" + details;
429 
430                 datafileContent += line + "\n";
431                 generateBuildStepFor(pName, method, dependentTestClassNames,
432                         targets);
433             }
434 
435 
436         }
437 
438         // write latest HOSTJUNIT generated file.
439         flushHostJunitFile();
440 
441         File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
442         scriptDataDir.mkdirs();
443         writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
444 
445         if (!javacHostJunitBuildStep.build()) {
446             System.out.println("main javac cts-host-hostjunit-classes build step failed");
447             System.exit(1);
448         }
449 
450         if (javacBuildStep.build()) {
451             for (BuildStep buildStep : targets) {
452                 if (!buildStep.build()) {
453                     System.out.println("building failed. buildStep: " +
454                             buildStep.getClass().getName() + ", " + buildStep);
455                     System.exit(1);
456                 }
457             }
458         } else {
459             System.out.println("main javac dalvik-cts-buildutil build step failed");
460             System.exit(1);
461         }
462     }
463 
464     private void generateBuildStepFor(String pName, String method,
465             Set<String> dependentTestClassNames, Set<BuildStep> targets) {
466 
467 
468         for (String dependentTestClassName : dependentTestClassNames) {
469             generateBuildStepForDependant(dependentTestClassName, targets);
470         }
471     }
472 
473     private void generateBuildStepForDependant(String dependentTestClassName,
474             Set<BuildStep> targets) {
475 
476         File sourceFolder = new File(JAVASRC_FOLDER);
477         String fileName = dependentTestClassName.replace('.', '/').trim();
478 
479         if (new File(sourceFolder, fileName + ".dfh").exists()) {
480 
481             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
482                     JAVASRC_FOLDER, fileName + ".dfh");
483             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
484                     OUTPUT_FOLDER, fileName + ".dex");
485 
486             DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
487 
488             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
489                     OUTPUT_FOLDER, fileName + ".jar");
490             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
491                     "classes.dex", jarFile, true);
492             jarBuildStep.addChild(buildStep);
493 
494             targets.add(jarBuildStep);
495             return;
496         }
497 
498         if (new File(sourceFolder, fileName + ".d").exists()) {
499 
500             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
501                     JAVASRC_FOLDER, fileName + ".d");
502             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
503                     OUTPUT_FOLDER, fileName + ".dex");
504 
505             DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
506 
507             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
508                     OUTPUT_FOLDER, fileName + ".jar");
509 
510             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
511                     "classes.dex", jarFile, true);
512             jarBuildStep.addChild(buildStep);
513             targets.add(jarBuildStep);
514             return;
515         }
516 
517         if (new File(sourceFolder, fileName + ".java").exists()) {
518 
519             BuildStep dexBuildStep = generateDexBuildStep(
520                     COMPILED_CLASSES_FOLDER, fileName);
521             targets.add(dexBuildStep);
522             return;
523         }
524 
525         try {
526             if (Class.forName(dependentTestClassName) != null) {
527 
528                 BuildStep dexBuildStep = generateDexBuildStep(
529                         COMPILED_CLASSES_FOLDER, fileName);
530                 targets.add(dexBuildStep);
531                 return;
532             }
533         } catch (ClassNotFoundException e) {
534             // do nothing
535         }
536 
537         throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
538                 dependentTestClassName + ";" + fileName);
539     }
540 
541     private BuildStep generateDexBuildStep(String classFileFolder,
542             String classFileName) {
543         BuildStep.BuildFile classFile = new BuildStep.BuildFile(
544                 classFileFolder, classFileName + ".class");
545 
546         BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
547                 classFileName + "_tmp.jar");
548 
549         JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
550                 ".class", tmpJarFile, false);
551 
552         BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
553                 classFileName + ".jar");
554 
555         DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
556                 true);
557 
558         dexBuildStep.addChild(jarBuildStep);
559         return dexBuildStep;
560 
561     }
562 
563     /**
564      * @param pName
565      * @param classOnlyName
566      * @param methodSource
567      * @return testclass names
568      */
569     private Set<String> parseTestClassName(String pName, String classOnlyName,
570             String methodSource) {
571         Set<String> entries = new HashSet<String>();
572         String opcodeName = classOnlyName.substring(5);
573 
574         Scanner scanner = new Scanner(methodSource);
575 
576         String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
577                 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
578 
579         String token = null;
580         for (String pattern : patterns) {
581             token = scanner.findWithinHorizon(pattern, methodSource.length());
582             if (token != null) {
583                 break;
584             }
585         }
586 
587         if (token == null) {
588             System.err.println("warning: failed to find dependent test class name: " + pName +
589                     ", " + classOnlyName + " in methodSource:\n" + methodSource);
590             return entries;
591         }
592 
593         MatchResult result = scanner.match();
594 
595         entries.add((pName + ".d." + result.group(1)).trim());
596 
597         // search additional @uses directives
598         Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
599         Matcher m = p.matcher(methodSource);
600         while (m.find()) {
601             String res = m.group(1);
602             entries.add(res.trim());
603         }
604 
605         // search for " load(\"...\" " and add as dependency
606         Pattern loadPattern = Pattern.compile("load\\(\"([^\"]*)\"", Pattern.MULTILINE);
607         Matcher loadMatcher = loadPattern.matcher(methodSource);
608         while (loadMatcher.find()) {
609             String res = loadMatcher.group(1);
610             entries.add(res.trim());
611         }
612 
613         // search for " loadAndRun(\"...\" " and add as dependency
614         Pattern loadAndRunPattern = Pattern.compile("loadAndRun\\(\"([^\"]*)\"", Pattern.MULTILINE);
615         Matcher loadAndRunMatcher = loadAndRunPattern.matcher(methodSource);
616         while (loadAndRunMatcher.find()) {
617             String res = loadAndRunMatcher.group(1);
618             entries.add(res.trim());
619         }
620 
621         // lines with the form @uses
622         // dot.junit.opcodes.add_double.jm.T_add_double_2
623         // one dependency per one @uses
624         // TODO
625 
626         return entries;
627     }
628 
629     private MethodData parseTestMethod(String pname, String classOnlyName,
630             String method) {
631 
632         String path = pname.replaceAll("\\.", "/");
633         String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
634         File f = new File(absPath);
635 
636         Scanner scanner;
637         try {
638             scanner = new Scanner(f);
639         } catch (FileNotFoundException e) {
640             throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
641                     ", msg:" + e.getMessage());
642         }
643 
644         String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
645 
646         String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
647         if (token == null) {
648             throw new RuntimeException("cannot find method source of 'public void " + method +
649                     "' in file '" + absPath + "'");
650         }
651 
652         MatchResult result = scanner.match();
653         result.start();
654         result.end();
655 
656         StringBuilder builder = new StringBuilder();
657         //builder.append(token);
658 
659         try {
660             FileReader reader = new FileReader(f);
661             reader.skip(result.end());
662 
663             char currentChar;
664             int blocks = 1;
665             while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
666                 switch (currentChar) {
667                     case '}': {
668                         blocks--;
669                         builder.append(currentChar);
670                         break;
671                     }
672                     case '{': {
673                         blocks++;
674                         builder.append(currentChar);
675                         break;
676                     }
677                     default: {
678                         builder.append(currentChar);
679                         break;
680                     }
681                 }
682             }
683             if (reader != null) {
684                 reader.close();
685             }
686         } catch (Exception e) {
687             throw new RuntimeException("failed to parse", e);
688         }
689 
690         // find the @title/@constraint in javadoc comment for this method
691         // using platform's default charset
692         String all = new String(FileUtils.readFile(f));
693         // System.out.println("grepping javadoc found for method " + method +
694         // " in " + pname + "," + classOnlyName);
695         String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
696         Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
697         Matcher m = p.matcher(all);
698         String title = null, constraint = null;
699         if (m.find()) {
700             String res = m.group(1);
701             // System.out.println("res: " + res);
702             // now grep @title and @constraint
703             Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
704             .matcher(res);
705             if (titleM.find()) {
706                 title = titleM.group(1).replaceAll("\\n     \\*", "");
707                 title = title.replaceAll("\\n", " ");
708                 title = title.trim();
709                 // System.out.println("title: " + title);
710             } else {
711                 System.err.println("warning: no @title found for method " + method + " in " + pname +
712                         "," + classOnlyName);
713             }
714             // constraint can be one line only
715             Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
716                     res);
717             if (constraintM.find()) {
718                 constraint = constraintM.group(1);
719                 constraint = constraint.trim();
720                 // System.out.println("constraint: " + constraint);
721             } else if (method.contains("VFE")) {
722                 System.err
723                 .println("warning: no @constraint for for a VFE method:" + method + " in " +
724                         pname + "," + classOnlyName);
725             }
726         } else {
727             System.err.println("warning: no javadoc found for method " + method + " in " + pname +
728                     "," + classOnlyName);
729         }
730         MethodData md = new MethodData();
731         md.methodBody = builder.toString();
732         md.constraint = constraint;
733         md.title = title;
734         if (scanner != null) {
735             scanner.close();
736         }
737         return md;
738     }
739 
740     private void writeToFileMkdir(File file, String content) {
741         File parent = file.getParentFile();
742         if (!parent.exists() && !parent.mkdirs()) {
743             throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
744         }
745         writeToFile(file, content);
746     }
747 
748     private void writeToFile(File file, String content) {
749         try {
750             if (file.length() == content.length()) {
751                 FileReader reader = new FileReader(file);
752                 char[] charContents = new char[(int) file.length()];
753                 reader.read(charContents);
754                 reader.close();
755                 String contents = new String(charContents);
756                 if (contents.equals(content)) {
757                     // System.out.println("skipping identical: "
758                     // + file.getAbsolutePath());
759                     return;
760                 }
761             }
762 
763             //System.out.println("writing file " + file.getAbsolutePath());
764 
765             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
766                     new FileOutputStream(file), "utf-8"));
767             bw.write(content);
768             bw.close();
769         } catch (Exception e) {
770             throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
771                     ", msg:" + e.getMessage());
772         }
773     }
774 
775     private File getFileFromPackage(String pname, String methodName)
776     throws IOException {
777         // e.g. dxc.junit.argsreturns.pargsreturn
778         String path = getFileName(pname, methodName, ".java");
779         String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
780         File dirPath = new File(absPath);
781         File parent = dirPath.getParentFile();
782         if (!parent.exists() && !parent.mkdirs()) {
783             throw new IOException("failed to create directory: " + absPath);
784         }
785         return dirPath;
786     }
787 
788     private String getFileName(String pname, String methodName,
789             String extension) {
790         String path = pname.replaceAll("\\.", "/");
791         return new File(path, "Main_" + methodName + extension).getPath();
792     }
793 }
794