001package daikon.tools.jtb; 002 003import static java.nio.charset.StandardCharsets.UTF_8; 004 005import java.io.File; 006import java.io.Reader; 007import java.io.StringReader; 008import java.io.StringWriter; 009import java.io.Writer; 010import java.nio.file.Files; 011import java.nio.file.Paths; 012import java.util.ArrayList; 013import java.util.List; 014import jtb.*; 015import jtb.syntaxtree.*; 016import jtb.visitor.*; 017import org.checkerframework.checker.lock.qual.GuardSatisfied; 018import org.checkerframework.dataflow.qual.SideEffectFree; 019 020/** 021 * The wrapped result of parsing a .java source file. The packageName and className arguments can be 022 * obtained from root, but they are returned here for convenience. 023 */ 024public class ParseResults { 025 public String packageName; 026 027 public String fileName; 028 029 public List<TypeDeclaration> roots = new ArrayList<>(); 030 031 public CompilationUnit compilationUnit; 032 033 private ParseResults(String packageName, String fileName, CompilationUnit compilationUnit) { 034 this.packageName = packageName; 035 this.fileName = fileName; 036 this.compilationUnit = compilationUnit; 037 } 038 039 @SideEffectFree 040 @Override 041 public String toString(@GuardSatisfied ParseResults this) { 042 return "package name: " + packageName + ", file name: " + fileName; 043 } 044 045 /** If one of the files declares an interfaces, an error will occur. */ 046 public static List<ParseResults> parse(List<String> javaFileNames) { 047 return parse(javaFileNames, false); 048 } 049 050 public static List<ParseResults> parse(List<String> javaFileNames, boolean discardComments) { 051 052 List<ParseResults> retval = new ArrayList<>(); 053 054 for (String javaFileName : javaFileNames) { 055 ParseResults results = parse(javaFileName, discardComments); 056 retval.add(results); 057 } 058 059 return retval; 060 } 061 062 public static ParseResults parse(String javaFileName) { 063 return parse(javaFileName, false); 064 } 065 066 public static ParseResults parse(String javaFileName, boolean discardComments) { 067 068 CompilationUnit compilationUnit; 069 070 System.out.println("Parsing file " + javaFileName); 071 072 File file = new File(javaFileName); 073 String fileName = file.getName(); 074 assert fileName.endsWith(".java") 075 : "Found a java-file argument that doesn't end in .java: " + file; 076 077 try (Reader input = Files.newBufferedReader(Paths.get(javaFileName), UTF_8)) { 078 JavaParser parser = new JavaParser(input); 079 compilationUnit = parser.CompilationUnit(); 080 } catch (Exception e) { 081 e.printStackTrace(); 082 throw new Error(e); 083 } 084 085 try { 086 // To discard comments, we dump the AST without special 087 // tokens, and then we read it again in the same way as 088 // before. 089 if (discardComments) { 090 Writer output = new StringWriter(); 091 TreeDumper dumper = new TreeDumper(output); 092 dumper.printSpecials(false); // Do not print specials <==> discard comments 093 compilationUnit.accept(new TreeFormatter()); 094 compilationUnit.accept(dumper); 095 output.close(); 096 097 try (Reader input = new StringReader(output.toString())) { 098 JavaParser parser = new JavaParser(input); 099 compilationUnit = parser.CompilationUnit(); 100 } 101 } 102 103 } catch (Exception e) { 104 e.printStackTrace(); 105 throw new Error(e); 106 } 107 108 // Construct the package name. 109 String packageNameString; 110 // CompilationUnit: 111 // f0 -> [ PackageDeclaration() ] 112 // f1 -> ( ImportDeclaration() )* 113 // f2 -> ( TypeDeclaration() )* 114 // f3 -> ( <"\u001a"> )? 115 // f4 -> ( <STUFF_TO_IGNORE: ~[]> )? 116 // f5 -> <EOF> 117 // PackageDeclaration: 118 // f0 -> Modifiers() 119 // f1 -> "package" 120 // f2 -> Name() 121 // f3 -> ";" 122 NodeOptional packageDeclarationMaybe = compilationUnit.f0; 123 if (packageDeclarationMaybe.present()) { 124 PackageDeclaration packageDeclaration = (PackageDeclaration) packageDeclarationMaybe.node; 125 Name packageName = packageDeclaration.f2; 126 StringWriter stringWriter = new StringWriter(); 127 TreeDumper dumper = new TreeDumper(stringWriter); 128 dumper.visit(packageName); 129 packageNameString = stringWriter.toString().trim(); 130 } else { 131 packageNameString = ""; 132 } 133 134 ParseResults results = new ParseResults(packageNameString, fileName, compilationUnit); 135 136 // Find the class name. 137 NodeListOptional typeDeclarationMaybe = compilationUnit.f2; 138 for (int j = 0; j < typeDeclarationMaybe.size(); j++) { 139 140 TypeDeclaration typeDeclaration = (TypeDeclaration) typeDeclarationMaybe.elementAt(j); 141 142 // { 143 // NodeSequence sequence = (NodeSequence) typeDeclaration.f0.choice; 144 // NodeChoice nodeChoice = (NodeChoice) sequence.elementAt(1); 145 // ClassOrInterfaceDeclaration decl = (ClassOrInterfaceDeclaration) nodeChoice.choice; 146 // assert !Ast.isInterface(decl) 147 // : "Do not give .java files that declare interfaces " 148 // + "to the instrumenter: " + javaFileName; 149 // } 150 151 results.roots.add(typeDeclaration); 152 } 153 154 return results; 155 } 156}