1/* 2 * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32import java.util.Set; 33import java.util.EnumSet; 34 35import javax.annotation.processing.*; 36import javax.lang.model.SourceVersion; 37import javax.lang.model.element.*; 38import javax.lang.model.type.*; 39import javax.lang.model.util.*; 40import static javax.lang.model.SourceVersion.*; 41import static javax.lang.model.element.Modifier.*; 42import static javax.lang.model.element.ElementKind.*; 43import static javax.lang.model.type.TypeKind.*; 44import static javax.lang.model.util.ElementFilter.*; 45import static javax.tools.Diagnostic.Kind.*; 46 47/** 48 * A sample processor to check naming conventions are being followed. 49 * 50 * <h3>How to run this processor from the command line</h3> 51 * <ol> 52 * <li> Compile this file; for example<br> 53 * {@code javac -d procdir CheckNamesProcessor.java} 54 * <li> Use {@code javac} to run the annotation processor on itself:<br> 55 * {@code javac -processorpath procdir -processor CheckNamesProcessor -proc:only CheckNamesProcessor.java} 56 * </ol> 57 * 58 * <h3>Another way to run this processor from the command line</h3> 59 * <ol> 60 * <li> Compile the processor as before 61 * 62 * <li> Create a UTF-8 encoded text file named {@code 63 * javax.annotation.processing.Processor} in the {@code 64 * META-INF/services} directory. The contents of the file are a list 65 * of the binary names of the concrete processor classes, one per 66 * line. This provider-configuration file is used by {@linkplain 67 * java.util.ServiceLoader service-loader} style lookup. 68 * 69 * <li> Create a {@code jar} file with the processor classes and 70 * {@code META-INF} information. 71 * 72 * <li> Such a {@code jar} file can now be used with the <i>discovery 73 * process</i> without explicitly naming the processor to run:<br> 74 * {@code javac -processorpath procdir -proc:only CheckNamesProcessor.java} 75 * 76 * </ol> 77 * 78 * <h3>Possible Enhancements</h3> 79 * <ul> 80 * 81 * <li> Support an annotation processor option to control checking 82 * exported API elements ({@code public} and {@code protected} ones) 83 * or all elements 84 * 85 * <li> Print out warnings that are more informative 86 * 87 * <li> Return a true/false status if any warnings were printed or 88 * compute and return name warning count 89 * 90 * <li> Implement checks of package names 91 * 92 * <li> Use the Tree API, com.sun.source, to examine names within method bodies 93 * 94 * <li> Define an annotation type whose presence can indicate a 95 * different naming convention is being followed 96 * 97 * <li> Implement customized checks on elements in chosen packages 98 * 99 * </ul> 100 * 101 * @author Joseph D. Darcy 102 */ 103@SupportedAnnotationTypes("*") // Process (check) everything 104public class CheckNamesProcessor extends AbstractProcessor { 105 private NameChecker nameChecker; 106 107 /** 108 * Check that the names of the root elements (and their enclosed 109 * elements) follow the appropriate naming conventions. This 110 * processor examines all files regardless of whether or not 111 * annotations are present; no new source or class files are 112 * generated. 113 * 114 * <p>Processors that actually process specific annotations should 115 * <em>not</em> report supporting {@code *}; this could cause 116 * performance degradations and other undesirable outcomes. 117 */ 118 @Override 119 public boolean process(Set<? extends TypeElement> annotations, 120 RoundEnvironment roundEnv) { 121 if (!roundEnv.processingOver()) { 122 for (Element element : roundEnv.getRootElements() ) 123 nameChecker.checkNames(element); 124 } 125 return false; // Allow other processors to examine files too. 126 } 127 128 @Override 129 public void init(ProcessingEnvironment processingEnv) { 130 super.init(processingEnv); 131 nameChecker = new NameChecker(processingEnv); 132 } 133 134 @Override 135 public SourceVersion getSupportedSourceVersion() { 136 /* 137 * Return latest source version instead of a fixed version 138 * like RELEASE_9. To return a fixed version, this class could 139 * be annotated with a SupportedSourceVersion annotation. 140 * 141 * Warnings will be issued if any unknown language constructs 142 * are encountered. 143 */ 144 return SourceVersion.latest(); 145 } 146 147 /** 148 * Provide checks that an element and its enclosed elements follow 149 * the usual naming conventions. 150 * 151 * <p> Conventions from section 6.8 of 152 * <cite>The Java™ Language Specification</cite> 153 * 154 * <ul> 155 * <li> Classes and interfaces: camel case, first letter is uppercase 156 * <li> Methods: camel case, first letter is lowercase 157 * <li> Type variables: one uppercase letter 158 * <li> Fields 159 * <ul> 160 * <li> non-final: camel case, initial lowercase 161 * <li> constant: uppercase separated by underscores 162 * </ul> 163 * <li> Packages: checks left as exercise for the reader, see section 7.7 of 164 * <cite>The Java™ Language Specification</cite>. 165 * </ul> 166 */ 167 private static class NameChecker { 168 private final Messager messager; 169 private final Types typeUtils; 170 171 NameCheckScanner nameCheckScanner = new NameCheckScanner(); 172 173 NameChecker(ProcessingEnvironment processsingEnv) { 174 this.messager = processsingEnv.getMessager(); 175 this.typeUtils = processsingEnv.getTypeUtils(); 176 } 177 178 /** 179 * If the name of the argument or its enclosed elements 180 * violates the naming conventions, report a warning. 181 */ 182 public void checkNames(Element element) { 183 // Implement name checks with a visitor, but expose that 184 // functionality through this method instead. 185 nameCheckScanner.scan(element); 186 } 187 188 /** 189 * Visitor to implement name checks. 190 */ 191 private class NameCheckScanner extends ElementScanner9<Void, Void> { 192 // The visitor could be enhanced to return true/false if 193 // there were warnings reported or a count of the number 194 // of warnings. This could be facilitated by using 195 // Boolean or Integer instead of Void for the actual type 196 // arguments. In more detail, one way to tally the number 197 // of warnings would be for each method to return the sum 198 // of the warnings it and the methods it called issued, a 199 // bottom-up computation. In that case, the first type 200 // argument would be Integer and the second type argument 201 // would still be Void. Alternatively, the current count 202 // could be passed along in Integer parameter p and each 203 // method could return the Integer sum of p and the 204 // warnings the method issued. Some computations are more 205 // naturally expressed in one form instead of the other. 206 // If greater control is needed over traversal order, a 207 // SimpleElementVisitor can be extended instead of an 208 // ElementScanner. 209 210 /** 211 * Check the name of a type and its enclosed elements and 212 * type parameters. 213 */ 214 @Override 215 public Void visitType(TypeElement e, Void p) { 216 scan(e.getTypeParameters(), p); // Check the names of any type parameters 217 checkCamelCase(e, true); // Check the name of the class or interface 218 super.visitType(e, p); // Check the names of any enclosed elements 219 return null; 220 } 221 222 /** 223 * Check the name of an executable (method, constructor, 224 * etc.) and its type parameters. 225 */ 226 @Override 227 public Void visitExecutable(ExecutableElement e, Void p) { 228 scan(e.getTypeParameters(), p); // Check the names of any type parameters 229 230 // Check the name of the executable 231 if (e.getKind() == METHOD) { 232 // Make sure that a method does not have the same 233 // name as its class or interface. 234 Name name = e.getSimpleName(); 235 if (name.contentEquals(e.getEnclosingElement().getSimpleName())) 236 messager.printMessage(WARNING, 237 "A method should not have the same name as its enclosing type, ``" + 238 name + "''." , e); 239 checkCamelCase(e, false); 240 } 241 // else constructors and initializers don't have user-defined names 242 243 // At this point, could use the Tree API, 244 // com.sun.source, to examine the names of entities 245 // inside a method. 246 super.visitExecutable(e, p); 247 return null; 248 } 249 250 /** 251 * Check the name of a field, parameter, etc. 252 */ 253 @Override 254 public Void visitVariable(VariableElement e, Void p) { 255 if (!checkForSerial(e)) { // serialVersionUID checks 256 // Is the variable a constant? 257 if (e.getKind() == ENUM_CONSTANT || 258 e.getConstantValue() != null || 259 heuristicallyConstant(e) ) 260 checkAllCaps(e); // includes enum constants 261 else 262 checkCamelCase(e, false); 263 } 264 // A call to super can be elided with the current language definition. 265 // super.visitVariable(e, p); 266 return null; 267 } 268 269 /** 270 * Check the name of a type parameter. 271 */ 272 @Override 273 public Void visitTypeParameter(TypeParameterElement e, Void p) { 274 checkAllCaps(e); 275 // A call to super can be elided with the current language definition. 276 // super.visitTypeParameter(e, p); 277 return null; 278 } 279 280 /** 281 * Check the name of a package. 282 */ 283 @Override 284 public Void visitPackage(PackageElement e, Void p) { 285 /* 286 * Implementing the checks of package names is left 287 * as an exercise for the reader, see JLS section 288 * 7.7 for conventions. 289 */ 290 291 // Whether or not this method should call 292 // super.visitPackage, to visit the packages enclosed 293 // elements, is a design decision based on what a 294 // PackageElemement is used to mean in this context. 295 // A PackageElement can represent a whole package, so 296 // it can provide a concise way to indicate many 297 // user-defined types should be visited. However, a 298 // PackageElement can also represent a 299 // package-info.java file, as would be in the case if 300 // the PackageElement came from 301 // RoundEnvironment.getRootElements. In that case, 302 // the package-info file and other files in that 303 // package could be passed in. Therefore, without 304 // further checks, types in a package could be visited 305 // more than once if a package's elements were visited 306 // too. 307 return null; 308 } 309 310 /** 311 * Check the name of a module. 312 */ 313 @Override 314 public Void visitModule(ModuleElement e, Void p) { 315 /* 316 * Implementing the checks of package names is left as 317 * an exercise for the reader. 318 */ 319 320 // Similar to the options of how visiting a package 321 // could be handled, whether or not this method should 322 // call super and scan, etc. is a design choice on 323 // whether it is desired for a ModuleElement to 324 // represent a module-info file or for the 325 // ModuleElement to represent the entire contents of a 326 // module, including its packages. 327 return null; 328 } 329 330 @Override 331 public Void visitUnknown(Element e, Void p) { 332 // This method will be called if a kind of element 333 // added after JDK 9 is visited. Since as of this 334 // writing the conventions for such constructs aren't 335 // known, issue a warning. 336 messager.printMessage(WARNING, 337 "Unknown kind of element, " + e.getKind() + 338 ", no name checking performed.", e); 339 return null; 340 } 341 342 // All the name checking methods assume the examined names 343 // are syntactically well-formed identifiers. 344 345 /** 346 * Return {@code true} if this variable is a field named 347 * "serialVersionUID"; false otherwise. A true 348 * serialVersionUID of a class has type {@code long} and 349 * is static and final. 350 * 351 * <p>To check that a Serializable class defines a proper 352 * serialVersionUID, run javac with -Xlint:serial. 353 * 354 * @return true if this variable is a serialVersionUID field and false otherwise 355 */ 356 private boolean checkForSerial(VariableElement e) { 357 // If a field is named "serialVersionUID" ... 358 if (e.getKind() == FIELD && 359 e.getSimpleName().contentEquals("serialVersionUID")) { 360 // ... issue a warning if it does not act as a serialVersionUID 361 if (!(e.getModifiers().containsAll(EnumSet.of(STATIC, FINAL)) && 362 typeUtils.isSameType(e.asType(), typeUtils.getPrimitiveType(LONG)) && 363 e.getEnclosingElement().getKind() == CLASS )) // could check that class implements Serializable 364 messager.printMessage(WARNING, 365 "Field named ``serialVersionUID'' is not acting as such.", e); 366 return true; 367 } 368 return false; 369 } 370 371 /** 372 * Using heuristics, return {@code true} is the variable 373 * should follow the naming conventions for constants and 374 * {@code false} otherwise. For example, the public 375 * static final fields ZERO, ONE, and TEN in 376 * java.math.BigDecimal are logically constants (and named 377 * as constants) even though BigDecimal values are not 378 * regarded as constants by the language specification. 379 * However, some final fields may not act as constants 380 * since the field may be a reference to a mutable object. 381 * 382 * <p> These heuristics could be tweaked to provide better 383 * fidelity. 384 * 385 * @return true if the current heuristics regard the 386 * variable as a constant and false otherwise. 387 */ 388 private boolean heuristicallyConstant(VariableElement e) { 389 // Fields declared in interfaces are logically 390 // constants, JLSv3 section 9.3. 391 if (e.getEnclosingElement().getKind() == INTERFACE) 392 return true; 393 else if (e.getKind() == FIELD && 394 e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL))) 395 return true; 396 else { 397 // A parameter declared final should not be named like 398 // a constant, neither should exception parameters. 399 return false; 400 } 401 } 402 403 /** 404 * Print a warning if an element's simple name is not in 405 * camel case. If there are two adjacent uppercase 406 * characters, the name is considered to violate the 407 * camel case naming convention. 408 * 409 * @param e the element whose name will be checked 410 * @param initialCaps whether or not the first character should be uppercase 411 */ 412 private void checkCamelCase(Element e, boolean initialCaps) { 413 String name = e.getSimpleName().toString(); 414 boolean previousUpper = false; 415 boolean conventional = true; 416 int firstCodePoint = name.codePointAt(0); 417 418 if (Character.isUpperCase(firstCodePoint)) { 419 previousUpper = true; 420 if (!initialCaps) { 421 messager.printMessage(WARNING, 422 "Name ``" + name + "'' should start in lowercase.", e); 423 return; 424 } 425 } else if (Character.isLowerCase(firstCodePoint)) { 426 if (initialCaps) { 427 messager.printMessage(WARNING, 428 "Name ``" + name + "'' should start in uppercase.", e); 429 return; 430 } 431 } else // underscore, etc. 432 conventional = false; 433 434 if (conventional) { 435 int cp = firstCodePoint; 436 for (int i = Character.charCount(cp); 437 i < name.length(); 438 i += Character.charCount(cp)) { 439 cp = name.codePointAt(i); 440 if (Character.isUpperCase(cp)){ 441 if (previousUpper) { 442 conventional = false; 443 break; 444 } 445 previousUpper = true; 446 } else 447 previousUpper = false; 448 } 449 } 450 451 if (!conventional) 452 messager.printMessage(WARNING, 453 "Name ``" + name + "'', should be in camel case.", e); 454 } 455 456 /** 457 * Print a warning if the element's name is not a sequence 458 * of uppercase letters separated by underscores ("_"). 459 * 460 * @param e the element whose name will be checked 461 */ 462 private void checkAllCaps(Element e) { 463 String name = e.getSimpleName().toString(); 464 /* 465 * Traditionally type variables are recommended to 466 * have one-character names. As an exercise for the 467 * reader, a more nuanced policy can be implemented. 468 */ 469 if (e.getKind() == TYPE_PARAMETER) { 470 if (name.codePointCount(0, name.length()) > 1 || 471 // Assume names are non-empty 472 !Character.isUpperCase(name.codePointAt(0))) 473 messager.printMessage(WARNING, 474 "A type variable's name,``" + name + 475 "'', should be a single uppercace character.", 476 e); 477 } else { 478 boolean conventional = true; 479 int firstCodePoint = name.codePointAt(0); 480 481 // Starting with an underscore is not conventional 482 if (!Character.isUpperCase(firstCodePoint)) 483 conventional = false; 484 else { 485 // Was the previous character an underscore? 486 boolean previousUnderscore = false; 487 int cp = firstCodePoint; 488 for (int i = Character.charCount(cp); 489 i < name.length(); 490 i += Character.charCount(cp)) { 491 cp = name.codePointAt(i); 492 if (cp == (int) '_') { 493 if (previousUnderscore) { 494 conventional = false; 495 break; 496 } 497 previousUnderscore = true; 498 } else { 499 previousUnderscore = false; 500 if (!Character.isUpperCase(cp) && !Character.isDigit(cp) ) { 501 conventional = false; 502 break; 503 } 504 } 505 } 506 } 507 508 if (!conventional) 509 messager.printMessage(WARNING, 510 "A constant's name, ``" + name + "'', should be ALL_CAPS.", 511 e); 512 } 513 } 514 515 } 516 } 517} 518 519/** 520 * Lots of bad names. Don't write code like this! 521 * 522 * The unmodified name checks will print 11 warnings for this class. 523 */ 524class BADLY_NAMED_CODE { 525 enum colors { 526 red, 527 blue, 528 green; 529 } 530 531 // Don't start the name of a constant with an underscore 532 static final int _FORTY_TWO = 42; 533 534 // Non-constants shouldn't use ALL_CAPS 535 public static int NOT_A_CONSTANT = _FORTY_TWO; 536 537 // *Not* a serialVersionUID 538 private static final int serialVersionUID = _FORTY_TWO; 539 540 // Not a constructor 541 protected void BADLY_NAMED_CODE() { 542 return; 543 } 544 545 public void NOTcamelCASEmethodNAME() { 546 return; 547 } 548} 549