1 /* 2 * Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package com.sun.tools.javac.processing; 27 28 import javax.annotation.processing.*; 29 import javax.lang.model.*; 30 import javax.lang.model.element.*; 31 import static javax.lang.model.element.ElementKind.*; 32 import static javax.lang.model.element.NestingKind.*; 33 import javax.lang.model.type.*; 34 import javax.lang.model.util.*; 35 36 import java.io.PrintWriter; 37 import java.io.Writer; 38 import java.util.*; 39 40 /** 41 * A processor which prints out elements. Used to implement the 42 * -Xprint option; the included visitor class is used to implement 43 * Elements.printElements. 44 * 45 * <p><b>This is NOT part of any API supported by Sun Microsystems. 46 * If you write code that depends on this, you do so at your own risk. 47 * This code and its internal interfaces are subject to change or 48 * deletion without notice.</b> 49 */ 50 @SupportedAnnotationTypes("*") 51 // TODO: Change to version 7 based visitors when available 52 @SupportedSourceVersion(SourceVersion.RELEASE_7) 53 public class PrintingProcessor extends AbstractProcessor { 54 PrintWriter writer; 55 56 public PrintingProcessor() { 57 super(); 58 writer = new PrintWriter(System.out); 59 } 60 61 public void setWriter(Writer w) { 62 writer = new PrintWriter(w); 63 } 64 65 @Override 66 public boolean process(Set<? extends TypeElement> tes, 67 RoundEnvironment renv) { 68 69 for(Element element : renv.getRootElements()) { 70 print(element); 71 } 72 73 // Just print the elements, nothing more to do. 74 return true; 75 } 76 77 void print(Element element) { 78 new PrintingElementVisitor(writer, processingEnv.getElementUtils()). 79 visit(element).flush(); 80 } 81 82 /** 83 * Used for the -Xprint option and called by Elements.printElements 84 */ 85 public static class PrintingElementVisitor 86 extends SimpleElementVisitor6<PrintingElementVisitor, Boolean> { 87 int indentation; // Indentation level; 88 final PrintWriter writer; 89 final Elements elementUtils; 90 91 public PrintingElementVisitor(Writer w, Elements elementUtils) { 92 super(); 93 this.writer = new PrintWriter(w); 94 this.elementUtils = elementUtils; 95 indentation = 0; 96 } 97 98 @Override 99 protected PrintingElementVisitor defaultAction(Element e, Boolean newLine) { 100 if (newLine != null && newLine) 101 writer.println(); 102 printDocComment(e); 103 printModifiers(e); 104 return this; 105 } 106 107 @Override 108 public PrintingElementVisitor visitExecutable(ExecutableElement e, Boolean p) { 109 ElementKind kind = e.getKind(); 110 111 if (kind != STATIC_INIT && 112 kind != INSTANCE_INIT) { 113 Element enclosing = e.getEnclosingElement(); 114 115 // Don't print out the constructor of an anonymous class 116 if (kind == CONSTRUCTOR && 117 enclosing != null && 118 NestingKind.ANONYMOUS == 119 // Use an anonymous class to determine anonymity! 120 (new SimpleElementVisitor6<NestingKind, Void>() { 121 @Override 122 public NestingKind visitType(TypeElement e, Void p) { 123 return e.getNestingKind(); 124 } 125 }).visit(enclosing)) 126 return this; 127 128 defaultAction(e, true); 129 printFormalTypeParameters(e, true); 130 131 switch(kind) { 132 case CONSTRUCTOR: 133 // Print out simple name of the class 134 writer.print(e.getEnclosingElement().getSimpleName()); 135 break; 136 137 case METHOD: 138 writer.print(e.getReturnType().toString()); 139 writer.print(" "); 140 writer.print(e.getSimpleName().toString()); 141 break; 142 } 143 144 writer.print("("); 145 printParameters(e); 146 writer.print(")"); 147 AnnotationValue defaultValue = e.getDefaultValue(); 148 if (defaultValue != null) 149 writer.print(" default " + defaultValue); 150 151 printThrows(e); 152 writer.println(";"); 153 } 154 return this; 155 } 156 157 158 @Override 159 public PrintingElementVisitor visitType(TypeElement e, Boolean p) { 160 ElementKind kind = e.getKind(); 161 NestingKind nestingKind = e.getNestingKind(); 162 163 if (NestingKind.ANONYMOUS == nestingKind) { 164 // Print out an anonymous class in the style of a 165 // class instance creation expression rather than a 166 // class declaration. 167 writer.print("new "); 168 169 // If the anonymous class implements an interface 170 // print that name, otherwise print the superclass. 171 List<? extends TypeMirror> interfaces = e.getInterfaces(); 172 if (!interfaces.isEmpty()) 173 writer.print(interfaces.get(0)); 174 else 175 writer.print(e.getSuperclass()); 176 177 writer.print("("); 178 // Anonymous classes that implement an interface can't 179 // have any constructor arguments. 180 if (interfaces.isEmpty()) { 181 // Print out the parameter list from the sole 182 // constructor. For now, don't try to elide any 183 // synthetic parameters by determining if the 184 // anonymous class is in a static context, etc. 185 List<? extends ExecutableElement> constructors = 186 ElementFilter.constructorsIn(e.getEnclosedElements()); 187 188 if (!constructors.isEmpty()) 189 printParameters(constructors.get(0)); 190 } 191 writer.print(")"); 192 } else { 193 if (nestingKind == TOP_LEVEL) { 194 PackageElement pkg = elementUtils.getPackageOf(e); 195 if (!pkg.isUnnamed()) 196 writer.print("package " + pkg.getQualifiedName() + ";\n"); 197 } 198 199 defaultAction(e, true); 200 201 switch(kind) { 202 case ANNOTATION_TYPE: 203 writer.print("@interface"); 204 break; 205 default: 206 writer.print(kind.toString().toLowerCase()); 207 } 208 writer.print(" "); 209 writer.print(e.getSimpleName()); 210 211 printFormalTypeParameters(e, false); 212 213 // Print superclass information if informative 214 if (kind == CLASS) { 215 TypeMirror supertype = e.getSuperclass(); 216 if (supertype.getKind() != TypeKind.NONE) { 217 TypeElement e2 = (TypeElement) 218 ((DeclaredType) supertype).asElement(); 219 if (e2.getSuperclass().getKind() != TypeKind.NONE) 220 writer.print(" extends " + supertype); 221 } 222 } 223 224 printInterfaces(e); 225 } 226 writer.println(" {"); 227 indentation++; 228 229 if (kind == ENUM) { 230 List<Element> enclosedElements = 231 new ArrayList<Element>(e.getEnclosedElements()); 232 List<Element> enumConstants = new ArrayList<Element>(); 233 for(Element element : enclosedElements) { 234 if (element.getKind() == ENUM_CONSTANT) 235 enumConstants.add(element); 236 } 237 238 int i; 239 for(i = 0; i < enumConstants.size()-1; i++) { 240 this.visit(enumConstants.get(i), true); 241 writer.print(","); 242 } 243 if (i >= 0 ) { 244 this.visit(enumConstants.get(i), true); 245 writer.print(";"); 246 } 247 248 enclosedElements.removeAll(enumConstants); 249 for(Element element : enclosedElements) 250 this.visit(element); 251 } else { 252 for(Element element : e.getEnclosedElements()) 253 this.visit(element); 254 } 255 256 indentation--; 257 indent(); 258 writer.println("}"); 259 return this; 260 } 261 262 @Override 263 public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) { 264 ElementKind kind = e.getKind(); 265 defaultAction(e, newLine); 266 267 if (kind == ENUM_CONSTANT) 268 writer.print(e.getSimpleName()); 269 else { 270 writer.print(e.asType().toString() + " " + e.getSimpleName() ); 271 Object constantValue = e.getConstantValue(); 272 if (constantValue != null) { 273 writer.print(" = "); 274 writer.print(elementUtils.getConstantExpression(constantValue)); 275 } 276 writer.println(";"); 277 } 278 return this; 279 } 280 281 @Override 282 public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) { 283 writer.print(e.getSimpleName()); 284 return this; 285 } 286 287 // Should we do more here? 288 @Override 289 public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) { 290 defaultAction(e, false); 291 if (!e.isUnnamed()) 292 writer.println("package " + e.getQualifiedName() + ";"); 293 else 294 writer.println("// Unnamed package"); 295 return this; 296 } 297 298 public void flush() { 299 writer.flush(); 300 } 301 302 private void printDocComment(Element e) { 303 String docComment = elementUtils.getDocComment(e); 304 305 if (docComment != null) { 306 // Break comment into lines 307 java.util.StringTokenizer st = new StringTokenizer(docComment, 308 "\n\r"); 309 indent(); 310 writer.println("/**"); 311 312 while(st.hasMoreTokens()) { 313 indent(); 314 writer.print(" *"); 315 writer.println(st.nextToken()); 316 } 317 318 indent(); 319 writer.println(" */"); 320 } 321 } 322 323 private void printModifiers(Element e) { 324 ElementKind kind = e.getKind(); 325 if (kind == PARAMETER) { 326 printAnnotationsInline(e); 327 } else { 328 printAnnotations(e); 329 indent(); 330 } 331 332 if (kind == ENUM_CONSTANT) 333 return; 334 335 Set<Modifier> modifiers = new LinkedHashSet<Modifier>(); 336 modifiers.addAll(e.getModifiers()); 337 338 switch (kind) { 339 case ANNOTATION_TYPE: 340 case INTERFACE: 341 modifiers.remove(Modifier.ABSTRACT); 342 break; 343 344 case ENUM: 345 modifiers.remove(Modifier.FINAL); 346 modifiers.remove(Modifier.ABSTRACT); 347 break; 348 349 case METHOD: 350 case FIELD: 351 Element enclosingElement = e.getEnclosingElement(); 352 if (enclosingElement != null && 353 enclosingElement.getKind().isInterface()) { 354 modifiers.remove(Modifier.PUBLIC); 355 modifiers.remove(Modifier.ABSTRACT); // only for methods 356 modifiers.remove(Modifier.STATIC); // only for fields 357 modifiers.remove(Modifier.FINAL); // only for fields 358 } 359 break; 360 361 } 362 363 for(Modifier m: modifiers) { 364 writer.print(m.toString() + " "); 365 } 366 } 367 368 private void printFormalTypeParameters(Parameterizable e, 369 boolean pad) { 370 List<? extends TypeParameterElement> typeParams = e.getTypeParameters(); 371 if (typeParams.size() > 0) { 372 writer.print("<"); 373 374 boolean first = true; 375 for(TypeParameterElement tpe: typeParams) { 376 if (!first) 377 writer.print(", "); 378 printAnnotationsInline(tpe); 379 writer.print(tpe.toString()); 380 first = false; 381 } 382 383 writer.print(">"); 384 if (pad) 385 writer.print(" "); 386 } 387 } 388 389 private void printAnnotationsInline(Element e) { 390 List<? extends AnnotationMirror> annots = e.getAnnotationMirrors(); 391 for(AnnotationMirror annotationMirror : annots) { 392 writer.print(annotationMirror); 393 writer.print(" "); 394 } 395 } 396 397 private void printAnnotations(Element e) { 398 List<? extends AnnotationMirror> annots = e.getAnnotationMirrors(); 399 for(AnnotationMirror annotationMirror : annots) { 400 indent(); 401 writer.println(annotationMirror); 402 } 403 } 404 405 // TODO: Refactor 406 private void printParameters(ExecutableElement e) { 407 List<? extends VariableElement> parameters = e.getParameters(); 408 int size = parameters.size(); 409 410 switch (size) { 411 case 0: 412 break; 413 414 case 1: 415 for(VariableElement parameter: parameters) { 416 printModifiers(parameter); 417 418 if (e.isVarArgs() ) { 419 TypeMirror tm = parameter.asType(); 420 if (tm.getKind() != TypeKind.ARRAY) 421 throw new AssertionError("Var-args parameter is not an array type: " + tm); 422 writer.print((ArrayType.class.cast(tm)).getComponentType() ); 423 writer.print("..."); 424 } else 425 writer.print(parameter.asType()); 426 writer.print(" " + parameter.getSimpleName()); 427 } 428 break; 429 430 default: 431 { 432 int i = 1; 433 for(VariableElement parameter: parameters) { 434 if (i == 2) 435 indentation++; 436 437 if (i > 1) 438 indent(); 439 440 printModifiers(parameter); 441 442 if (i == size && e.isVarArgs() ) { 443 TypeMirror tm = parameter.asType(); 444 if (tm.getKind() != TypeKind.ARRAY) 445 throw new AssertionError("Var-args parameter is not an array type: " + tm); 446 writer.print((ArrayType.class.cast(tm)).getComponentType() ); 447 448 writer.print("..."); 449 } else 450 writer.print(parameter.asType()); 451 writer.print(" " + parameter.getSimpleName()); 452 453 if (i < size) 454 writer.println(","); 455 456 i++; 457 } 458 459 if (parameters.size() >= 2) 460 indentation--; 461 } 462 break; 463 } 464 } 465 466 private void printInterfaces(TypeElement e) { 467 ElementKind kind = e.getKind(); 468 469 if(kind != ANNOTATION_TYPE) { 470 List<? extends TypeMirror> interfaces = e.getInterfaces(); 471 if (interfaces.size() > 0) { 472 writer.print((kind.isClass() ? " implements" : " extends")); 473 474 boolean first = true; 475 for(TypeMirror interf: interfaces) { 476 if (!first) 477 writer.print(","); 478 writer.print(" "); 479 writer.print(interf.toString()); 480 first = false; 481 } 482 } 483 } 484 } 485 486 private void printThrows(ExecutableElement e) { 487 List<? extends TypeMirror> thrownTypes = e.getThrownTypes(); 488 final int size = thrownTypes.size(); 489 if (size != 0) { 490 writer.print(" throws"); 491 492 int i = 1; 493 for(TypeMirror thrownType: thrownTypes) { 494 if (i == 1) 495 writer.print(" "); 496 497 if (i == 2) 498 indentation++; 499 500 if (i >= 2) 501 indent(); 502 503 writer.print(thrownType); 504 505 if (i != size) 506 writer.println(", "); 507 508 i++; 509 } 510 511 if (size >= 2) 512 indentation--; 513 } 514 } 515 516 private static final String [] spaces = { 517 "", 518 " ", 519 " ", 520 " ", 521 " ", 522 " ", 523 " ", 524 " ", 525 " ", 526 " ", 527 " " 528 }; 529 530 private void indent() { 531 int indentation = this.indentation; 532 if (indentation < 0) 533 return; 534 final int maxIndex = spaces.length - 1; 535 536 while (indentation > maxIndex) { 537 writer.print(spaces[maxIndex]); 538 indentation -= maxIndex; 539 } 540 writer.print(spaces[indentation]); 541 } 542 543 } 544 }