1 /*
   2  * Copyright (c) 2006, 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 
  32 import java.util.Set;
  33 import java.util.EnumSet;
  34 
  35 import javax.annotation.processing.*;
  36 import javax.lang.model.SourceVersion;
  37 import javax.lang.model.element.*;
  38 import javax.lang.model.type.*;
  39 import javax.lang.model.util.*;
  40 import static javax.lang.model.SourceVersion.*;
  41 import static javax.lang.model.element.Modifier.*;
  42 import static javax.lang.model.element.ElementKind.*;
  43 import static javax.lang.model.type.TypeKind.*;
  44 import static javax.lang.model.util.ElementFilter.*;
  45 import 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  * For some notes on how to run an annotation processor inside
  79  * NetBeans, see http://wiki.java.net/bin/view/Netbeans/FaqApt.
  80  *
  81  * <h3>Possible Enhancements</h3>
  82  * <ul>
  83  *
  84  * <li> Support an annotation processor option to control checking
  85  * exported API elements ({@code public} and {@code protected} ones)
  86  * or all elements
  87  *
  88  * <li> Print out warnings that are more informative
  89  *
  90  * <li> Return a true/false status if any warnings were printed or
  91  * compute and return name warning count
  92  *
  93  * <li> Implement checks of package names
  94  *
  95  * <li> Use the Tree API, com.sun.source, to examine names within method bodies
  96  *
  97  * <li> Define an annotation type whose presence can indicate a
  98  * different naming convention is being followed
  99  *
 100  * <li> Implement customized checks on elements in chosen packages
 101  *
 102  * </ul>
 103  *
 104  * @author Joseph D. Darcy
 105  */
 106 @SupportedAnnotationTypes("*")     // Process (check) everything
 107 public class CheckNamesProcessor extends AbstractProcessor {
 108     private NameChecker nameChecker;
 109 
 110     /**
 111      * Check that the names of the root elements (and their enclosed
 112      * elements) follow the appropriate naming conventions.  This
 113      * processor examines all files regardless of whether or not
 114      * annotations are present; no new source or class files are
 115      * generated.
 116      *
 117      * <p>Processors that actually process specific annotations should
 118      * <em>not</em> report supporting {@code *}; this could cause
 119      * performance degradations and other undesirable outcomes.
 120      */
 121     @Override
 122     public boolean process(Set<? extends TypeElement> annotations,
 123             RoundEnvironment roundEnv) {
 124         if (!roundEnv.processingOver()) {
 125             for (Element element : roundEnv.getRootElements() )
 126                 nameChecker.checkNames(element);
 127         }
 128         return false; // Allow other processors to examine files too.
 129     }
 130 
 131     @Override
 132     public void init(ProcessingEnvironment processingEnv) {
 133         super.init(processingEnv);
 134         nameChecker = new NameChecker(processingEnv);
 135     }
 136 
 137     @Override
 138     public SourceVersion getSupportedSourceVersion() {
 139         /*
 140          * Return latest source version instead of a fixed version
 141          * like RELEASE_7.  To return a fixed version, this class
 142          * could be annotated with a SupportedSourceVersion
 143          * annotation.
 144          *
 145          * Warnings will be issued if any unknown language constructs
 146          * are encountered.
 147          */
 148         return SourceVersion.latest();
 149     }
 150 
 151     /**
 152      * Provide checks that an element and its enclosed elements follow
 153      * the usual naming conventions.
 154      *
 155      * <p> Conventions from JLSv3 section 6.8:
 156      *
 157      * <ul>
 158      * <li> Classes and interfaces: camel case, first letter is uppercase
 159      * <li> Methods: camel case, first letter is lowercase
 160      * <li> Type variables: one uppercase letter
 161      * <li> Fields
 162      * <ul>
 163      * <li> non-final: camel case, initial lowercase
 164      * <li> constant: uppercase separated by underscores
 165      * </ul>
 166      * <li> Packages: checks left as exercise for the reader, see JLSv3 section 7.7
 167      * </ul>
 168      */
 169     private static class NameChecker {
 170         private final Messager messager;
 171         private final Types typeUtils;
 172 
 173         NameCheckScanner nameCheckScanner = new NameCheckScanner();
 174 
 175         NameChecker(ProcessingEnvironment processsingEnv) {
 176             this.messager  = processsingEnv.getMessager();
 177             this.typeUtils = processsingEnv.getTypeUtils();
 178         }
 179 
 180         /**
 181          * If the name of the argument or its enclosed elements
 182          * violates the naming conventions, report a warning.
 183          */
 184         public void checkNames(Element element) {
 185             // Implement name checks with a visitor, but expose that
 186             // functionality through this method instead.
 187             nameCheckScanner.scan(element);
 188         }
 189 
 190         /**
 191          * Visitor to implement name checks.
 192          */
 193         private class NameCheckScanner extends ElementScanner7<Void, Void> {
 194             // The visitor could be enhanced to return true/false if
 195             // there were warnings reported or a count of the number
 196             // of warnings.  This could be facilitated by using
 197             // Boolean or Integer instead of Void for the actual type
 198             // arguments.  In more detail, one way to tally the number
 199             // of warnings would be for each method to return the sum
 200             // of the warnings it and the methods it called issued, a
 201             // bottom-up computation.  In that case, the first type
 202             // argument would be Integer and the second type argument
 203             // would still be Void.  Alternatively, the current count
 204             // could be passed along in Integer parameter p and each
 205             // method could return the Integer sum of p and the
 206             // warnings the method issued.  Some computations are more
 207             // naturally expressed in one form instead of the other.
 208             // If greater control is needed over traversal order, a
 209             // SimpleElementVisitor can be extended instead of an
 210             // ElementScanner.
 211 
 212             /**
 213              * Check the name of a type and its enclosed elements and
 214              * type parameters.
 215              */
 216             @Override
 217             public Void visitType(TypeElement e, Void p) {
 218                 scan(e.getTypeParameters(), p); // Check the names of any type parameters
 219                 checkCamelCase(e, true);        // Check the name of the class or interface
 220                 super.visitType(e, p);          // Check the names of any enclosed elements
 221                 return null;
 222             }
 223 
 224             /**
 225              * Check the name of an executable (method, constructor,
 226              * etc.) and its type parameters.
 227              */
 228             @Override
 229             public Void visitExecutable(ExecutableElement e, Void p) {
 230                 scan(e.getTypeParameters(), p); // Check the names of any type parameters
 231 
 232                 // Check the name of the executable
 233                 if (e.getKind() == METHOD) {
 234                     // Make sure that a method does not have the same
 235                     // name as its class or interface.
 236                     Name name = e.getSimpleName();
 237                     if (name.contentEquals(e.getEnclosingElement().getSimpleName()))
 238                         messager.printMessage(WARNING,
 239                                               "A method should not have the same name as its enclosing type, ``" +
 240                                               name + "''." , e);
 241                     checkCamelCase(e, false);
 242                 }
 243                 // else constructors and initializers don't have user-defined names
 244 
 245                 // At this point, could use the Tree API,
 246                 // com.sun.source, to examine the names of entities
 247                 // inside a method.
 248                 super.visitExecutable(e, p);
 249                 return null;
 250             }
 251 
 252             /**
 253              * Check the name of a field, parameter, etc.
 254              */
 255             @Override
 256             public Void visitVariable(VariableElement e, Void p) {
 257                 if (!checkForSerial(e)) { // serialVersionUID checks
 258                     // Is the variable a constant?
 259                     if (e.getKind() == ENUM_CONSTANT ||
 260                         e.getConstantValue() != null ||
 261                         heuristicallyConstant(e) )
 262                         checkAllCaps(e); // includes enum constants
 263                     else
 264                         checkCamelCase(e, false);
 265                 }
 266                 // A call to super can be elided with the current language definition.
 267                 // super.visitVariable(e, p);
 268                 return null;
 269             }
 270 
 271             /**
 272              * Check the name of a type parameter.
 273              */
 274             @Override
 275             public Void visitTypeParameter(TypeParameterElement e, Void p) {
 276                 checkAllCaps(e);
 277                 // A call to super can be elided with the current language definition.
 278                 // super.visitTypeParameter(e, p);
 279                 return null;
 280             }
 281 
 282             /**
 283              * Check the name of a package.
 284              */
 285             @Override
 286             public Void visitPackage(PackageElement e, Void p) {
 287                 /*
 288                  * Implementing the checks of package names is left
 289                  * as an exercise for the reader, see JLSv3 section
 290                  * 7.7 for conventions.
 291                  */
 292 
 293                 // Whether or not this method should call
 294                 // super.visitPackage, to visit the packages enclosed
 295                 // elements, is a design decision based on what a
 296                 // PackageElemement is used to mean in this context.
 297                 // A PackageElement can represent a whole package, so
 298                 // it can provide a concise way to indicate many
 299                 // user-defined types should be visited.  However, a
 300                 // PackageElement can also represent a
 301                 // package-info.java file, as would be in the case if
 302                 // the PackageElement came from
 303                 // RoundEnvironment.getRootElements.  In that case,
 304                 // the package-info file and other files in that
 305                 // package could be passed in.  Therefore, without
 306                 // further checks, types in a package could be visited
 307                 // more than once if a package's elements were visited
 308                 // too.
 309                 return null;
 310             }
 311 
 312             @Override
 313             public Void visitUnknown(Element e, Void p) {
 314                 // This method will be called if a kind of element
 315                 // added after JDK 7 is visited.  Since as of this
 316                 // writing the conventions for such constructs aren't
 317                 // known, issue a warning.
 318                 messager.printMessage(WARNING,
 319                                       "Unknown kind of element, " + e.getKind() +
 320                                       ", no name checking performed.", e);
 321                 return null;
 322             }
 323 
 324             // All the name checking methods assume the examined names
 325             // are syntactically well-formed identifiers.
 326 
 327             /**
 328              * Return {@code true} if this variable is a field named
 329              * "serialVersionUID"; false otherwise.  A true
 330              * serialVersionUID of a class has type {@code long} and
 331              * is static and final.
 332              *
 333              * <p>To check that a Serializable class defines a proper
 334              * serialVersionUID, run javac with -Xlint:serial.
 335              *
 336              * @return true if this variable is a serialVersionUID field and false otherwise
 337              */
 338             private boolean checkForSerial(VariableElement e) {
 339                 // If a field is named "serialVersionUID" ...
 340                 if (e.getKind() == FIELD &&
 341                     e.getSimpleName().contentEquals("serialVersionUID")) {
 342                     // ... issue a warning if it does not act as a serialVersionUID
 343                     if (!(e.getModifiers().containsAll(EnumSet.of(STATIC, FINAL)) &&
 344                             typeUtils.isSameType(e.asType(), typeUtils.getPrimitiveType(LONG)) &&
 345                             e.getEnclosingElement().getKind() == CLASS )) // could check that class implements Serializable
 346                         messager.printMessage(WARNING,
 347                                               "Field named ``serialVersionUID'' is not acting as such.", e);
 348                     return true;
 349                 }
 350                 return false;
 351             }
 352 
 353             /**
 354              * Using heuristics, return {@code true} is the variable
 355              * should follow the naming conventions for constants and
 356              * {@code false} otherwise.  For example, the public
 357              * static final fields ZERO, ONE, and TEN in
 358              * java.math.BigDecimal are logically constants (and named
 359              * as constants) even though BigDecimal values are not
 360              * regarded as constants by the language specification.
 361              * However, some final fields may not act as constants
 362              * since the field may be a reference to a mutable object.
 363              *
 364              * <p> These heuristics could be tweaked to provide better
 365              * fidelity.
 366              *
 367              * @return true if the current heuristics regard the
 368              * variable as a constant and false otherwise.
 369              */
 370             private boolean heuristicallyConstant(VariableElement e) {
 371                 // Fields declared in interfaces are logically
 372                 // constants, JLSv3 section 9.3.
 373                 if (e.getEnclosingElement().getKind() == INTERFACE)
 374                     return true;
 375                 else if (e.getKind() == FIELD &&
 376                          e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL)))
 377                     return true;
 378                 else {
 379                     // A parameter declared final should not be named like
 380                     // a constant, neither should exception parameters.
 381                     return false;
 382                 }
 383             }
 384 
 385             /**
 386              * Print a warning if an element's simple name is not in
 387              * camel case.  If there are two adjacent uppercase
 388              * characters, the name is considered to violate the
 389              * camel case naming convention.
 390              *
 391              * @param e the element whose name will be checked
 392              * @param initialCaps whether or not the first character should be uppercase
 393              */
 394             private void checkCamelCase(Element e, boolean initialCaps) {
 395                 String name = e.getSimpleName().toString();
 396                 boolean previousUpper = false;
 397                 boolean conventional = true;
 398                 int firstCodePoint = name.codePointAt(0);
 399 
 400                 if (Character.isUpperCase(firstCodePoint)) {
 401                     previousUpper = true;
 402                     if (!initialCaps) {
 403                         messager.printMessage(WARNING,
 404                                               "Name, ``" + name + "'', should start in lowercase.", e);
 405                         return;
 406                     }
 407                 } else if (Character.isLowerCase(firstCodePoint)) {
 408                     if (initialCaps) {
 409                         messager.printMessage(WARNING,
 410                                               "Name, ``" + name + "'', should start in uppercase.", e);
 411                         return;
 412                     }
 413                 } else // underscore, etc.
 414                     conventional = false;
 415 
 416                 if (conventional) {
 417                     int cp = firstCodePoint;
 418                     for (int i = Character.charCount(cp);
 419                          i < name.length();
 420                          i += Character.charCount(cp)) {
 421                         cp = name.codePointAt(i);
 422                         if (Character.isUpperCase(cp)){
 423                             if (previousUpper) {
 424                                 conventional = false;
 425                                 break;
 426                             }
 427                             previousUpper = true;
 428                         } else
 429                             previousUpper = false;
 430                     }
 431                 }
 432 
 433                 if (!conventional)
 434                     messager.printMessage(WARNING,
 435                                           "Name, ``" + name + "'', should be in camel case.", e);
 436             }
 437 
 438             /**
 439              * Print a warning if the element's name is not a sequence
 440              * of uppercase letters separated by underscores ("_").
 441              *
 442              * @param e the element whose name will be checked
 443              */
 444             private void checkAllCaps(Element e) {
 445                 String name = e.getSimpleName().toString();
 446                 if (e.getKind() == TYPE_PARAMETER) { // Should be one character
 447                     if (name.codePointCount(0, name.length()) > 1 ||
 448                         // Assume names are non-empty
 449                         !Character.isUpperCase(name.codePointAt(0)))
 450                         messager.printMessage(WARNING,
 451                                               "A type variable's name,``" + name +
 452                                               "'', should be a single uppercace character.",
 453                                               e);
 454                 } else {
 455                     boolean conventional = true;
 456                     int firstCodePoint = name.codePointAt(0);
 457 
 458                     // Starting with an underscore is not conventional
 459                     if (!Character.isUpperCase(firstCodePoint))
 460                         conventional = false;
 461                     else {
 462                         // Was the previous character an underscore?
 463                         boolean previousUnderscore = false;
 464                         int cp = firstCodePoint;
 465                         for (int i = Character.charCount(cp);
 466                              i < name.length();
 467                              i += Character.charCount(cp)) {
 468                             cp = name.codePointAt(i);
 469                             if (cp == (int) '_') {
 470                                 if (previousUnderscore) {
 471                                     conventional = false;
 472                                     break;
 473                                 }
 474                                 previousUnderscore = true;
 475                             } else {
 476                                 previousUnderscore = false;
 477                                 if (!Character.isUpperCase(cp) && !Character.isDigit(cp) ) {
 478                                     conventional = false;
 479                                     break;
 480                                 }
 481                             }
 482                         }
 483                     }
 484 
 485                     if (!conventional)
 486                         messager.printMessage(WARNING,
 487                                               "A constant's name, ``" + name + "'', should be ALL_CAPS.",
 488                                               e);
 489                 }
 490             }
 491 
 492         }
 493     }
 494 }
 495 
 496 /**
 497  * Lots of bad names.  Don't write code like this!
 498  */
 499 class BADLY_NAMED_CODE {
 500     enum colors {
 501         red,
 502         blue,
 503         green;
 504     }
 505 
 506     // Don't start the name of a constant with an underscore
 507     static final int _FORTY_TWO = 42;
 508 
 509     // Non-constants shouldn't use ALL_CAPS
 510     public static int NOT_A_CONSTANT = _FORTY_TWO;
 511 
 512     // *Not* a serialVersionUID
 513     private static final int serialVersionUID = _FORTY_TWO;
 514 
 515     // Not a constructor
 516     protected void BADLY_NAMED_CODE() {
 517         return;
 518     }
 519 
 520     public void NOTcamelCASEmethodNAME() {
 521         return;
 522     }
 523 }