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" 045 * file. 046 */ 047 public static final String MSG_KEY = "one.top.level.class"; 048 049 /** 050 * Creates a new {@code OneTopLevelClassCheck} instance. 051 */ 052 public OneTopLevelClassCheck() { 053 // no code by default 054 } 055 056 @Override 057 public int[] getDefaultTokens() { 058 return getRequiredTokens(); 059 } 060 061 @Override 062 public int[] getAcceptableTokens() { 063 return getRequiredTokens(); 064 } 065 066 @Override 067 public int[] getRequiredTokens() { 068 return new int[] { 069 TokenTypes.COMPILATION_UNIT, 070 }; 071 } 072 073 @Override 074 public void visitToken(DetailAST compilationUnit) { 075 DetailAST currentNode = compilationUnit.getFirstChild(); 076 077 boolean publicTypeFound = false; 078 DetailAST firstType = null; 079 080 while (currentNode != null) { 081 if (isTypeDef(currentNode)) { 082 if (isPublic(currentNode)) { 083 // log the first type later 084 publicTypeFound = true; 085 } 086 if (firstType == null) { 087 // first type is set aside 088 firstType = currentNode; 089 } 090 else if (!isPublic(currentNode)) { 091 // extra non-public type, log immediately 092 final String typeName = currentNode 093 .findFirstToken(TokenTypes.IDENT).getText(); 094 log(currentNode, MSG_KEY, typeName); 095 } 096 } 097 currentNode = currentNode.getNextSibling(); 098 } 099 100 // if there was a public type and first type is non-public, log it 101 if (publicTypeFound && !isPublic(firstType)) { 102 final String typeName = firstType 103 .findFirstToken(TokenTypes.IDENT).getText(); 104 log(firstType, MSG_KEY, typeName); 105 } 106 } 107 108 /** 109 * Checks if an AST node is a type definition. 110 * 111 * @param node AST node to check. 112 * @return true if the node is a type (class, enum, interface, annotation) definition. 113 */ 114 private static boolean isTypeDef(DetailAST node) { 115 return TokenUtil.isTypeDeclaration(node.getType()); 116 } 117 118 /** 119 * Checks if a type is public. 120 * 121 * @param typeDef type definition node. 122 * @return true if a type has a public access level modifier. 123 */ 124 private static boolean isPublic(DetailAST typeDef) { 125 final DetailAST modifiers = 126 typeDef.findFirstToken(TokenTypes.MODIFIERS); 127 return modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null; 128 } 129 130}