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&trade; 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&trade; 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