001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.naming; 021 022import java.util.regex.Pattern; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.FullIdent; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030/** 031 * <p> 032 * Checks that package names conform to a specified pattern. 033 * </p> 034 * <p> 035 * The default value of {@code format} for module {@code PackageName} has been chosen to match 036 * the requirements in the 037 * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5.3"> 038 * Java Language specification</a> 039 * and the Sun coding conventions. However both underscores and uppercase letters are rather 040 * uncommon, so most configurations should probably assign value 041 * {@code ^[a-z]+(\.[a-z][a-z0-9]*)*$} to {@code format} for module {@code PackageName}. 042 * </p> 043 * <ul> 044 * <li> 045 * Property {@code format} - Specifies valid identifiers. 046 * Type is {@code java.util.regex.Pattern}. 047 * Default value is {@code "^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$"}. 048 * </li> 049 * </ul> 050 * <p> 051 * An example of how to configure the check is: 052 * </p> 053 * <pre> 054 * <module name="PackageName"/> 055 * </pre> 056 * <p>Code Example:</p> 057 * <pre> 058 * package com; // OK 059 * package COM; // violation, name 'COM' must match pattern '^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$' 060 * package com.checkstyle.checks; // OK 061 * package com.A.checkstyle.checks; // OK 062 * package com.checkstyle1.checks; // OK 063 * package com.checkSTYLE.checks; // OK 064 * package com._checkstyle.checks_; // OK 065 * </pre> 066 * <p> 067 * An example of how to configure the check to ensure with packages start with a lowercase letter 068 * and only contains lowercase letters or numbers is: 069 * </p> 070 * <pre> 071 * <module name="PackageName"> 072 * <property name="format" 073 * value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> 074 * </module> 075 * </pre> 076 * <p>Code Example:</p> 077 * <pre> 078 * package com; // OK 079 * package COM; // violation, name 'COM' must match pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$' 080 * package com.checkstyle.checks; // OK 081 * package com.A.checkstyle.checks; // violation, name 'com.A.checkstyle' must match 082 * // pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$' 083 * package com.checkstyle1.checks; // OK 084 * package com.checkSTYLE.checks; // violation, name 'com.checkSTYLE.checks' must 085 * // match pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$' 086 * package com._checkstyle.checks_; // violation, name 'com._checkstyle.checks_' must match 087 * // pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$' 088 * </pre> 089 * <p> 090 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 091 * </p> 092 * <p> 093 * Violation Message Keys: 094 * </p> 095 * <ul> 096 * <li> 097 * {@code name.invalidPattern} 098 * </li> 099 * </ul> 100 * 101 * @since 3.0 102 */ 103@StatelessCheck 104public class PackageNameCheck 105 extends AbstractCheck { 106 107 /** 108 * A key is pointing to the warning message text in "messages.properties" 109 * file. 110 */ 111 public static final String MSG_KEY = "name.invalidPattern"; 112 113 /** Specifies valid identifiers. */ 114 // Uppercase letters seem rather uncommon, but they're allowed in 115 // https://docs.oracle.com/javase/specs/ 116 // second_edition/html/packages.doc.html#40169 117 private Pattern format = Pattern.compile("^[a-z]+(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$"); 118 119 /** 120 * Setter to specifies valid identifiers. 121 * 122 * @param pattern the new pattern 123 */ 124 public void setFormat(Pattern pattern) { 125 format = pattern; 126 } 127 128 @Override 129 public int[] getDefaultTokens() { 130 return getRequiredTokens(); 131 } 132 133 @Override 134 public int[] getAcceptableTokens() { 135 return getRequiredTokens(); 136 } 137 138 @Override 139 public int[] getRequiredTokens() { 140 return new int[] {TokenTypes.PACKAGE_DEF}; 141 } 142 143 @Override 144 public void visitToken(DetailAST ast) { 145 final DetailAST nameAST = ast.getLastChild().getPreviousSibling(); 146 final FullIdent full = FullIdent.createFullIdent(nameAST); 147 if (!format.matcher(full.getText()).find()) { 148 log(full.getDetailAst(), 149 MSG_KEY, 150 full.getText(), 151 format.pattern()); 152 } 153 } 154 155}