1 package annotations.tools; 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.IOException; 6 import java.io.PrintWriter; 7 import java.util.Collection; 8 import java.util.Collections; 9 import java.util.Map; 10 11 import com.google.common.collect.HashMultimap; 12 import com.google.common.collect.SetMultimap; 13 import com.sun.tools.javac.main.CommandLine; 14 15 import plume.FileIOException; 16 import annotations.Annotation; 17 import annotations.Annotations; 18 import annotations.el.ABlock; 19 import annotations.el.AClass; 20 import annotations.el.ADeclaration; 21 import annotations.el.AElement; 22 import annotations.el.AExpression; 23 import annotations.el.AField; 24 import annotations.el.AMethod; 25 import annotations.el.AScene; 26 import annotations.el.ATypeElement; 27 import annotations.el.ATypeElementWithType; 28 import annotations.el.AnnotationDef; 29 import annotations.el.DefException; 30 import annotations.el.ElementVisitor; 31 import annotations.field.AnnotationFieldType; 32 import annotations.io.IndexFileParser; 33 import annotations.io.IndexFileWriter; 34 35 /** 36 * Utility for merging index files, including multiple versions for the 37 * same class. 38 * 39 * @author dbro 40 */ 41 public class IndexFileMerger { main(String[] args)42 public static void main(String[] args) { 43 if (args.length < 1) { System.exit(0); } 44 45 final SetMultimap<String, String> annotatedFor = HashMultimap.create(); 46 String[] inputArgs; 47 48 // TODO: document assumptions 49 // collect annotations into scene 50 try { 51 try { 52 inputArgs = CommandLine.parse(args); 53 } catch (IOException ex) { 54 System.err.println(ex); 55 System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\"."); 56 System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'."); 57 System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)"); 58 System.exit(1); 59 return; // so compiler knows inputArgs defined after try/catch 60 } 61 62 File baseFile = new File(inputArgs[0]); 63 boolean byDir = baseFile.isDirectory(); 64 String basePath = baseFile.getCanonicalPath(); 65 AScene scene = new AScene(); 66 67 for (int i = byDir ? 1 : 0; i < inputArgs.length; i++) { 68 File inputFile = new File(inputArgs[i]); 69 String inputPath = inputFile.getCanonicalPath(); 70 String filename = inputFile.getName(); 71 72 if (byDir) { 73 if (!(filename.endsWith(".jaif") || filename.endsWith("jann"))) { 74 System.err.println("WARNING: ignoring non-JAIF " + filename); 75 continue; 76 } 77 if (!inputPath.startsWith(basePath)) { 78 System.err.println("WARNING: ignoring file outside base directory " 79 + filename); 80 continue; 81 } 82 83 // note which base subdirectory JAIF came from 84 String relPath = inputPath.substring(basePath.length()+1); // +1 '/' 85 int ix = relPath.indexOf(File.separator); 86 String subdir = ix < 0 ? relPath : relPath.substring(0, ix); 87 // trim .jaif or .jann and subdir, convert directory to package id 88 String classname = relPath.substring(0, relPath.lastIndexOf('.')) 89 .substring(relPath.indexOf('/')+1).replace(File.separator, "."); 90 annotatedFor.put(classname, "\"" + subdir + "\""); 91 } 92 93 try { 94 IndexFileParser.parseFile(inputPath, scene); 95 } catch (FileNotFoundException e) { 96 System.err.println("IndexFileMerger: can't read " 97 + inputPath); 98 System.exit(1); 99 } catch (FileIOException e) { 100 e.printStackTrace(); // TODO 101 System.exit(1); 102 } 103 } 104 105 if (!byDir) { 106 /* 107 // collect defs 108 Map<String, String> annoPkgs = new HashMap<String, String>(); 109 try { 110 new DefCollector(scene) { 111 @Override 112 protected void visitAnnotationDef(AnnotationDef d) { 113 String[] a = d.name.split("\\."); 114 if (a.length > 2 && a[a.length-2].matches("quals?")) { 115 String s = a[a.length-1]; 116 annoPkgs.put(s, d.name.substring(0)); 117 } 118 } 119 }.visit(); 120 } catch (DefException e) { 121 System.err.println("DefCollector failed!"); 122 e.printStackTrace(); 123 System.exit(1); 124 } 125 */ 126 127 for (Map.Entry<String, AClass> entry : scene.classes.entrySet()) { 128 // final String classname = entry.getKey(); 129 130 entry.getValue().accept(new ElementVisitor<Void, Void>() { 131 // Map<String, String> annoPkgs = new HashMap<String, String>(); 132 133 // Void process(AElement el) { 134 // for (Annotation anno : el.tlAnnotationsHere) { 135 // AnnotationDef def = anno.def(); 136 // String[] a = def.name.split("\\."); 137 // if ("AnnotatedFor".equals(a[a.length-1])) { 138 // @SuppressWarnings("unchecked") 139 // List<String> vals = 140 // (List<String>) anno.getFieldValue("value"); 141 // for (String val : vals) { 142 // annotatedFor.put(classname, val); 143 // } 144 // } else if (a.length > 2 && a[a.length-2].matches("quals?")) { 145 // annotatedFor.put(classname, a[a.length-3]); 146 // } 147 // } 148 // return null; 149 // } 150 151 Void visit(AElement el) { 152 if (el != null) { el.accept(this, null); } 153 return null; 154 } 155 156 <T, E extends AElement> Void visitMap(Map<T, E> map) { 157 if (map != null) { 158 for (E el : map.values()) { visit(el); } 159 } 160 return null; 161 } 162 163 @Override 164 public Void visitAnnotationDef(AnnotationDef d, Void v) { 165 // String[] a = d.name.split("\\."); 166 // if (a.length > 2 && a[a.length-2].matches("quals?")) { 167 // String s = a[a.length-1]; 168 // annoPkgs.put(s, d.name.substring(0)); 169 // } 170 return null; // process(d); 171 } 172 173 @Override 174 public Void visitBlock(ABlock el, Void v) { 175 visitMap(el.locals); 176 return visitExpression(el, v); 177 } 178 179 @Override 180 public Void visitClass(AClass el, Void v) { 181 visitMap(el.bounds); 182 visitMap(el.extendsImplements); 183 visitMap(el.instanceInits); 184 visitMap(el.staticInits); 185 visitMap(el.methods); 186 visitMap(el.fields); 187 visitMap(el.fieldInits); 188 return visitDeclaration(el, v); 189 } 190 191 @Override 192 public Void visitDeclaration(ADeclaration el, Void v) { 193 visitMap(el.insertAnnotations); 194 visitMap(el.insertTypecasts); 195 return visitElement(el, v); 196 } 197 198 @Override 199 public Void visitExpression(AExpression el, Void v) { 200 visitMap(el.calls); 201 visitMap(el.funs); 202 visitMap(el.instanceofs); 203 visitMap(el.news); 204 visitMap(el.refs); 205 visitMap(el.typecasts); 206 return visitElement(el, v); 207 } 208 209 @Override 210 public Void visitField(AField el, Void v) { 211 visit(el.init); 212 return visitDeclaration(el, v); 213 } 214 215 @Override 216 public Void visitMethod(AMethod el, Void v) { 217 visit(el.receiver); 218 visitMap(el.parameters); 219 visitMap(el.bounds); 220 visit(el.returnType); 221 visit(el.body); 222 visitMap(el.throwsException); 223 return visitDeclaration(el, v); 224 } 225 226 @Override 227 public Void visitTypeElement(ATypeElement el, Void v) { 228 visitMap(el.innerTypes); 229 return visitElement(el, v); 230 } 231 232 @Override 233 public Void visitTypeElementWithType(ATypeElementWithType el, 234 Void v) { 235 return visitTypeElement(el, v); 236 } 237 238 @Override 239 public Void visitElement(AElement el, Void v) { 240 visit(el.type); 241 return null; // process(el); 242 } 243 }, null); 244 } 245 } 246 247 // add AnnotatedFor to each annotated class 248 AnnotationFieldType stringArray = 249 AnnotationFieldType.fromClass(new String[0].getClass(), 250 Collections.<String, AnnotationDef>emptyMap()); 251 AnnotationDef afDef = 252 Annotations.createValueAnnotationDef("AnnotatedFor", 253 Collections.<Annotation>emptySet(), stringArray); 254 for (Map.Entry<String, Collection<String>> entry : 255 annotatedFor.asMap().entrySet()) { 256 String key = entry.getKey(); 257 Collection<String> values = entry.getValue(); 258 Annotation afAnno = new Annotation(afDef, Collections 259 .<String, Collection<String>>singletonMap("value", values)); 260 scene.classes.vivify(key).tlAnnotationsHere.add(afAnno); 261 } 262 scene.prune(); 263 annotatedFor.clear(); // for gc 264 265 try { 266 IndexFileWriter.write(scene, new PrintWriter(System.out, true)); 267 } catch (SecurityException e) { 268 e.printStackTrace(); 269 System.exit(1); 270 } catch (DefException e) { 271 e.printStackTrace(); 272 System.exit(1); 273 } 274 } catch (IOException e) { 275 e.printStackTrace(); 276 } 277 } 278 } 279