001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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.coding;
021
022import java.util.HashMap;
023import java.util.Map;
024
025import com.puppycrawl.tools.checkstyle.StatelessCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
030
031/**
032 * <div>
033 * Checks that overloaded methods are grouped together. Overloaded methods have the same
034 * name but different signatures where the signature can differ by the number of
035 * input parameters or type of input parameters or both.
036 * </div>
037 *
038 * @since 5.8
039 */
040@StatelessCheck
041public class OverloadMethodsDeclarationOrderCheck 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 = "overload.methods.declaration";
048
049    @Override
050    public int[] getDefaultTokens() {
051        return getRequiredTokens();
052    }
053
054    @Override
055    public int[] getAcceptableTokens() {
056        return getRequiredTokens();
057    }
058
059    @Override
060    public int[] getRequiredTokens() {
061        return new int[] {
062            TokenTypes.OBJBLOCK,
063        };
064    }
065
066    @Override
067    public void visitToken(DetailAST ast) {
068        final int parentType = ast.getParent().getType();
069
070        final int[] tokenTypes = {
071            TokenTypes.CLASS_DEF,
072            TokenTypes.ENUM_DEF,
073            TokenTypes.INTERFACE_DEF,
074            TokenTypes.LITERAL_NEW,
075            TokenTypes.RECORD_DEF,
076        };
077
078        if (TokenUtil.isOfType(parentType, tokenTypes)) {
079            checkOverloadMethodsGrouping(ast);
080        }
081    }
082
083    /**
084     * Checks that if overload methods are grouped together they should not be
085     * separated from each other.
086     *
087     * @param objectBlock
088     *        is a class, interface or enum object block.
089     */
090    private void checkOverloadMethodsGrouping(DetailAST objectBlock) {
091        final int allowedDistance = 1;
092        DetailAST currentToken = objectBlock.getFirstChild();
093        final Map<String, Integer> methodIndexMap = new HashMap<>();
094        final Map<String, Integer> methodLineNumberMap = new HashMap<>();
095        int currentIndex = 0;
096        while (currentToken != null) {
097            if (currentToken.getType() == TokenTypes.METHOD_DEF) {
098                currentIndex++;
099                final String methodName =
100                        currentToken.findFirstToken(TokenTypes.IDENT).getText();
101                final Integer previousIndex = methodIndexMap.get(methodName);
102                final DetailAST previousSibling = currentToken.getPreviousSibling();
103                final boolean isMethod = previousSibling.getType() == TokenTypes.METHOD_DEF;
104
105                if (previousIndex != null
106                        && (!isMethod || currentIndex - previousIndex > allowedDistance)) {
107                    final int previousLineWithOverloadMethod =
108                            methodLineNumberMap.get(methodName);
109                    log(currentToken, MSG_KEY,
110                            previousLineWithOverloadMethod);
111                }
112                methodIndexMap.put(methodName, currentIndex);
113                methodLineNumberMap.put(methodName, currentToken.getLineNo());
114            }
115            currentToken = currentToken.getNextSibling();
116        }
117    }
118
119}