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;
021
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Locale;
025import java.util.Set;
026
027import com.puppycrawl.tools.checkstyle.StatelessCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031
032/**
033 * <div> Checks that hexadecimal literals are defined using uppercase letters {@code (A-F)}
034 * rather than lowercase {@code (a-f)}.
035 * This improves readability and avoids confusion with suffixes like {@code f}(float)
036 * and {@code d}(double). For example, use {@code 0xFF} instead of {@code 0xff}.
037 * All other numerical prefixes, infixes, and suffixes (such as {@code 0x}, {@code 0b},
038 * {@code f}, {@code d}) should remain lowercase.
039 * This convention follows the
040 * <a href="https://cr.openjdk.org/~alundblad/styleguide/index-v6.html#toc-literals">
041 * OpenJDK Style Guide</a>.
042 * </div>
043 *
044 * <p>
045 * For example, {@code 0xAF} is valid, but {@code 0xaf} is not.
046 * </p>
047 *
048 * <p>
049 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
050 * </p>
051 *
052 * <p>
053 * Violation Message Keys:
054 * </p>
055 *
056 * <ul>
057 * <li>
058 * {@code hex.Literal}
059 * </li>
060 * </ul>
061 *
062 * @since 11.1.0
063 */
064@StatelessCheck
065public class HexLiteralCaseCheck extends AbstractCheck {
066
067    /**
068     * A key is pointing to the warning message text in "messages.properties"
069     * file.
070     */
071    public static final String MSG_KEY = "hex.Literal";
072    /**
073     * Lowercase hexadecimal characters that are considered violations
074     * when used in hexadecimal literals (e.g., 0x1a). Hex digits should be uppercase.
075     */
076    private final Set<Character> hexChars = Set.of('a', 'b', 'c', 'd', 'e', 'f');
077
078    @Override
079    public int[] getDefaultTokens() {
080        return getRequiredTokens();
081    }
082
083    @Override
084    public int[] getAcceptableTokens() {
085        return getRequiredTokens();
086    }
087
088    @Override
089    public int[] getRequiredTokens() {
090        return new int[] {TokenTypes.NUM_LONG, TokenTypes.NUM_INT};
091    }
092
093    @Override
094    public void visitToken(DetailAST ast) {
095        if (ast.getText().toLowerCase(Locale.ROOT).startsWith("0x")) {
096            final char[] charArray = ast.getText().toCharArray();
097            final List<Character> characterList = new ArrayList<>(charArray.length);
098            for (char letter : charArray) {
099                characterList.add(letter);
100            }
101            final boolean containLowerLetter = characterList.stream()
102                    .anyMatch(hexChars::contains);
103            if (containLowerLetter) {
104                log(ast, MSG_KEY);
105            }
106        }
107    }
108}