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