View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
30  
31  /**
32   * <div>
33   * Checks that overloaded methods are grouped together. Overloaded methods have the same
34   * name but different signatures where the signature can differ by the number of
35   * input parameters or type of input parameters or both.
36   * </div>
37   *
38   * @since 5.8
39   */
40  @StatelessCheck
41  public class OverloadMethodsDeclarationOrderCheck extends AbstractCheck {
42  
43      /**
44       * A key is pointing to the warning message text in "messages.properties"
45       * file.
46       */
47      public static final String MSG_KEY = "overload.methods.declaration";
48  
49      /**
50       * A key is pointing to the warning message text in "messages.properties"
51       * file.
52       */
53      public static final String MSG_ORDER = "overload.methods.declaration.order";
54  
55      /**
56       * Control whether to order overloaded methods by increasing parameter count.
57       */
58      private boolean orderByIncreasingParameterCount;
59  
60      /**
61       * Creates a new {@code OverloadMethodsDeclarationOrderCheck} instance.
62       */
63      public OverloadMethodsDeclarationOrderCheck() {
64          // no code by default
65      }
66  
67      /**
68       * Setter to control whether to enforce order by increasing parameter count (arity) or not.
69       *
70       * @param orderByIncreasingParameterCount true if order by increasing parameter
71       *               count is required.
72       * @since 13.7.0
73       */
74      public void setOrderByIncreasingParameterCount(boolean orderByIncreasingParameterCount) {
75          this.orderByIncreasingParameterCount = orderByIncreasingParameterCount;
76      }
77  
78      @Override
79      public int[] getDefaultTokens() {
80          return getRequiredTokens();
81      }
82  
83      @Override
84      public int[] getAcceptableTokens() {
85          return getRequiredTokens();
86      }
87  
88      @Override
89      public int[] getRequiredTokens() {
90          return new int[] {
91              TokenTypes.OBJBLOCK,
92              TokenTypes.COMPACT_COMPILATION_UNIT,
93          };
94      }
95  
96      @Override
97      public void visitToken(DetailAST ast) {
98          final int[] objectBlockParentTypes = {
99              TokenTypes.CLASS_DEF,
100             TokenTypes.ENUM_DEF,
101             TokenTypes.INTERFACE_DEF,
102             TokenTypes.LITERAL_NEW,
103             TokenTypes.RECORD_DEF,
104         };
105 
106         if (ast.getType() == TokenTypes.COMPACT_COMPILATION_UNIT
107             || TokenUtil.isOfType(ast.getParent().getType(), objectBlockParentTypes)) {
108             checkOverloadMethodsGrouping(ast);
109         }
110     }
111 
112     /**
113      * Checks that if overload methods are grouped together they should not be
114      * separated from each other.
115      *
116      * @param objectBlock
117      *        is a class, interface, enum object block, or a compact
118      *        compilation unit for a JEP 512 compact source file.
119      */
120     private void checkOverloadMethodsGrouping(DetailAST objectBlock) {
121         final int allowedDistance = 1;
122         DetailAST currentToken = objectBlock.getFirstChild();
123         final Map<String, Integer> methodIndexMap = new HashMap<>();
124         final Map<String, Integer> methodLineNumberMap = new HashMap<>();
125 
126         final Map<String, Integer> methodParameterCountMap = new HashMap<>();
127         final Map<String, Boolean> methodIsOrderedMap = new HashMap<>();
128 
129         int currentIndex = 0;
130         while (currentToken != null) {
131             if (currentToken.getType() == TokenTypes.METHOD_DEF) {
132                 currentIndex++;
133                 final String methodName =
134                         currentToken.findFirstToken(TokenTypes.IDENT).getText();
135                 final Integer previousIndex = methodIndexMap.get(methodName);
136 
137                 if (previousIndex != null) {
138                     final DetailAST previousSibling = currentToken.getPreviousSibling();
139                     final boolean isMethod = previousSibling.getType() == TokenTypes.METHOD_DEF;
140 
141                     if (!isMethod || currentIndex - previousIndex > allowedDistance) {
142                         final int previousLineWithOverloadMethod =
143                                 methodLineNumberMap.get(methodName);
144                         log(currentToken, MSG_KEY,
145                                 previousLineWithOverloadMethod);
146                     }
147 
148                     if (orderByIncreasingParameterCount) {
149                         checkMethodOrdering(currentToken, methodName,
150                                 methodIsOrderedMap, methodParameterCountMap);
151                     }
152                 }
153                 methodIsOrderedMap.putIfAbsent(methodName, Boolean.TRUE);
154                 methodIndexMap.put(methodName, currentIndex);
155                 methodLineNumberMap.put(methodName, currentToken.getLineNo());
156                 methodParameterCountMap.put(methodName, getParameterCount(currentToken));
157             }
158             currentToken = currentToken.getNextSibling();
159         }
160     }
161 
162     /**
163      * Checks that overloaded methods are ordered with increasing parameter count
164      * or not.
165      *
166      * @param currentMethod ast of the method.
167      * @param methodName method name.
168      * @param methodIsOrderedMap
169      *        is a map of method names to booleans indicating whether the method is ordered.
170      * @param methodParameterCountMap
171      *        is a map of method names to integers indicating the parameter count of the previous
172      *        similar method.
173      */
174     private void checkMethodOrdering(DetailAST currentMethod, String methodName,
175         Map<String, Boolean> methodIsOrderedMap, Map<String, Integer> methodParameterCountMap) {
176 
177         final int currentParamCount = getParameterCount(currentMethod);
178         final boolean isOrdered = methodIsOrderedMap.get(methodName)
179                             && currentParamCount >= methodParameterCountMap.get(methodName);
180         if (!isOrdered) {
181             methodIsOrderedMap.put(methodName, Boolean.FALSE);
182             log(currentMethod, MSG_ORDER);
183         }
184     }
185 
186     /**
187      * Get the parameter count of a method.
188      *
189      * @param method the method AST
190      * @return the parameter count of the method
191      */
192     private static int getParameterCount(DetailAST method) {
193         final DetailAST params = method.findFirstToken(TokenTypes.PARAMETERS);
194         return params.getChildCount(TokenTypes.PARAMETER_DEF);
195     }
196 
197 }