001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.design; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 027 028/** 029 * <div> 030 * Checks that each top-level class, interface, enum 031 * or annotation resides in a source file of its own. 032 * Official description of a 'top-level' term: 033 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-7.html#jls-7.6"> 034 * 7.6. Top Level Type Declarations</a>. If file doesn't contain 035 * public class, interface, enum or annotation, top-level type is the first type in file. 036 * </div> 037 * 038 * @since 5.8 039 */ 040@StatelessCheck 041public class OneTopLevelClassCheck extends AbstractCheck { 042 043 /** 044 * A key is pointing to the warning message text in "messages.properties" file. 045 */ 046 public static final String MSG_KEY = "one.top.level.class"; 047 048 /** 049 * Creates a new {@code OneTopLevelClassCheck} instance. 050 */ 051 public OneTopLevelClassCheck() { 052 // no code by default 053 } 054 055 @Override 056 public int[] getDefaultTokens() { 057 return getRequiredTokens(); 058 } 059 060 @Override 061 public int[] getAcceptableTokens() { 062 return getRequiredTokens(); 063 } 064 065 @Override 066 public int[] getRequiredTokens() { 067 return new int[] { 068 TokenTypes.COMPILATION_UNIT, 069 }; 070 } 071 072 @Override 073 public void visitToken(DetailAST compilationUnit) { 074 DetailAST currentNode = compilationUnit.getFirstChild(); 075 076 boolean publicTypeFound = false; 077 DetailAST firstType = null; 078 079 while (currentNode != null) { 080 if (isTypeDef(currentNode)) { 081 if (isPublic(currentNode)) { 082 // log the first type later 083 publicTypeFound = true; 084 } 085 if (firstType == null) { 086 // first type is set aside 087 firstType = currentNode; 088 } 089 else if (!isPublic(currentNode)) { 090 // extra non-public type, log immediately 091 final String typeName = currentNode 092 .findFirstToken(TokenTypes.IDENT).getText(); 093 log(currentNode, MSG_KEY, typeName); 094 } 095 } 096 currentNode = currentNode.getNextSibling(); 097 } 098 099 // if there was a public type and first type is non-public, log it 100 if (publicTypeFound && !isPublic(firstType)) { 101 final String typeName = firstType 102 .findFirstToken(TokenTypes.IDENT).getText(); 103 log(firstType, MSG_KEY, typeName); 104 } 105 } 106 107 /** 108 * Checks if an AST node is a type definition. 109 * 110 * @param node AST node to check. 111 * @return true if the node is a type (class, enum, interface, annotation) definition. 112 */ 113 private static boolean isTypeDef(DetailAST node) { 114 return TokenUtil.isTypeDeclaration(node.getType()); 115 } 116 117 /** 118 * Checks if a type is public. 119 * 120 * @param typeDef type definition node. 121 * @return true if a type has a public access level modifier. 122 */ 123 private static boolean isPublic(DetailAST typeDef) { 124 final DetailAST modifiers = 125 typeDef.findFirstToken(TokenTypes.MODIFIERS); 126 return modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null; 127 } 128 129}