/* * @(#)CheckNamesProcessor.java 1.2 06/09/28 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ import java.util.Set; import java.util.EnumSet; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.*; import javax.lang.model.util.*; import static javax.lang.model.SourceVersion.*; import static javax.lang.model.element.Modifier.*; import static javax.lang.model.element.ElementKind.*; import static javax.lang.model.type.TypeKind.*; import static javax.lang.model.util.ElementFilter.*; import static javax.tools.Diagnostic.Kind.*; /** * A sample processor to check naming conventions are being followed. * *
Processors that actually process specific annotations should * not report supporting {@code *}; this could cause * performance degradations and other undesirable outcomes. */ @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { for (Element element : roundEnv.getRootElements() ) nameChecker.checkNames(element); } return false; // Allow other processors to examine files too. } @Override public void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); nameChecker = new NameChecker(processingEnv); } @Override public SourceVersion getSupportedSourceVersion() { /* * Return latest source version instead of a fixed version * like RELEASE_6. To return a fixed version, this class * could be annotated with a SupportedSourceVersion * annotation. * * Warnings will be issued if any unknown language constructs * are encountered. */ return SourceVersion.latest(); } /** * Provide checks that an element and its enclosed elements follow * the usual naming conventions. * *
Conventions from JLSv3 section 6.8: * *
To check that a Serializable class defines a proper * serialVersionUID, run javac with -Xlint:serial. * * @return true if this variable is a serialVersionUID field and false otherwise */ private boolean checkForSerial(VariableElement e) { // If a field is named "serialVersionUID" ... if (e.getKind() == FIELD && e.getSimpleName().contentEquals("serialVersionUID")) { // ... issue a warning if it does not act as a serialVersionUID if (!(e.getModifiers().containsAll(EnumSet.of(STATIC, FINAL)) && typeUtils.isSameType(e.asType(), typeUtils.getPrimitiveType(LONG)) && e.getEnclosingElement().getKind() == CLASS )) // could check that class implements Serializable messager.printMessage(WARNING, "Field named ``serialVersionUID'' is not acting as such.", e); return true; } return false; } /** * Using heuristics, return {@code true} is the variable * should follow the naming conventions for constants and * {@code false} otherwise. For example, the public * static final fields ZERO, ONE, and TEN in * java.math.BigDecimal are logically constants (and named * as constants) even though BigDecimal values are not * regarded as constants by the language specification. * However, some final fields may not act as constants * since the field may be a reference to a mutable object. * *
These heuristics could be tweaked to provide better * fidelity. * * @return true if the current heuristics regard the * variable as a constant and false otherwise. */ private boolean heuristicallyConstant(VariableElement e) { // Fields declared in interfaces are logically // constants, JLSv3 section 9.3. if (e.getEnclosingElement().getKind() == INTERFACE) return true; else if (e.getKind() == FIELD && e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL))) return true; else { // A parameter declared final should not be named like // a constant, neither should exception parameters. return false; } } /** * Print a warning if an element's simple name is not in * camel case. If there are two adjacent uppercase * characters, the name is considered to violate the * camel case naming convention. * * @param e the element whose name will be checked * @param initialCaps whether or not the first character should be uppercase */ private void checkCamelCase(Element e, boolean initialCaps) { String name = e.getSimpleName().toString(); boolean previousUpper = false; boolean conventional = true; int firstCodePoint = name.codePointAt(0); if (Character.isUpperCase(firstCodePoint)) { previousUpper = true; if (!initialCaps) { messager.printMessage(WARNING, "Name, ``" + name + "'', should start in lowercase.", e); return; } } else if (Character.isLowerCase(firstCodePoint)) { if (initialCaps) { messager.printMessage(WARNING, "Name, ``" + name + "'', should start in uppercase.", e); return; } } else // underscore, etc. conventional = false; if (conventional) { int cp = firstCodePoint; for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { cp = name.codePointAt(i); if (Character.isUpperCase(cp)){ if (previousUpper) { conventional = false; break; } previousUpper = true; } else previousUpper = false; } } if (!conventional) messager.printMessage(WARNING, "Name, ``" + name + "'', should be in camel case.", e); } /** * Print a warning if the element's name is not a sequence * of uppercase letters separated by underscores ("_"). * * @param e the element whose name will be checked */ private void checkAllCaps(Element e) { String name = e.getSimpleName().toString(); if (e.getKind() == TYPE_PARAMETER) { // Should be one character if (name.codePointCount(0, name.length()) > 1 || // Assume names are non-empty !Character.isUpperCase(name.codePointAt(0))) messager.printMessage(WARNING, "A type variable's name,``" + name + "'', should be a single uppercace character.", e); } else { boolean conventional = true; int firstCodePoint = name.codePointAt(0); // Starting with an underscore is not conventional if (!Character.isUpperCase(firstCodePoint)) conventional = false; else { // Was the previous character an underscore? boolean previousUnderscore = false; int cp = firstCodePoint; for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { cp = name.codePointAt(i); if (cp == (int) '_') { if (previousUnderscore) { conventional = false; break; } previousUnderscore = true; } else { previousUnderscore = false; if (!Character.isUpperCase(cp) && !Character.isDigit(cp) ) { conventional = false; break; } } } } if (!conventional) messager.printMessage(WARNING, "A constant's name, ``" + name + "'', should be ALL_CAPS.", e); } } } } } /** * Lots of bad names. Don't write code like this! */ class BADLY_NAMED_CODE { enum colors { red, blue, green; } // Don't start the name of a constant with an underscore static final int _FORTY_TWO = 42; // Non-constants shouldn't use ALL_CAPS public static int NOT_A_CONSTANT = _FORTY_TWO; // *Not* a serialVersionUID private static final int serialVersionUID = _FORTY_TWO; // Not a constructor protected void BADLY_NAMED_CODE() { return; } public void NOTcamelCASEmethodNAME() { return; } }