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