View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  import java.util.ArrayList;
11  import java.util.List;
12  
13  /**
14   * Checks for not using concatenation of string literals in any form.
15   *
16   * <p>The following constructs are prohibited:</p>
17   *
18   * <pre>
19   * String a = "done in " + time + " seconds";
20   * System.out.println("File not found: " + file);
21   * x += "done";
22   * </pre>
23   *
24   * <p>You should avoid string concatenation at all cost. Why? There are two
25   * reasons: readability of the code and translateability. First of all it's
26   * difficult to understand how the text will look after concatenation,
27   * especially if the text is long and there are more than a few {@code +}
28   * operators. Second, you won't be able to translate your text to other
29   * languages later, if you don't have solid string literals.</p>
30   *
31   * <p>There are two alternatives to concatenation: {@link StringBuilder}
32   * and {@link String#format(String,Object[])}.</p>
33   *
34   * @since 0.3
35   */
36  public final class StringLiteralsConcatenationCheck extends AbstractCheck {
37  
38      /**
39       * Default constructor.
40       */
41      public StringLiteralsConcatenationCheck() {
42          // nothing to initialize
43      }
44  
45      @Override
46      public int[] getDefaultTokens() {
47          return new int[] {TokenTypes.OBJBLOCK};
48      }
49  
50      @Override
51      public int[] getAcceptableTokens() {
52          return this.getDefaultTokens();
53      }
54  
55      @Override
56      public int[] getRequiredTokens() {
57          return this.getDefaultTokens();
58      }
59  
60      @Override
61      public void visitToken(final DetailAST ast) {
62          final List<DetailAST> pluses = this.findChildAstsOfType(
63              ast,
64              TokenTypes.PLUS,
65              TokenTypes.PLUS_ASSIGN
66          );
67          for (final DetailAST plus : pluses) {
68              if (this.hasStringLiteralOperand(plus)) {
69                  this.log(plus, "Concatenation of string literals prohibited");
70              }
71          }
72      }
73  
74      private List<DetailAST> findChildAstsOfType(final DetailAST tree,
75          final int... types) {
76          final List<DetailAST> children = new ArrayList<>(0);
77          DetailAST child = tree.getFirstChild();
78          while (child != null) {
79              if (StringLiteralsConcatenationCheck.isOfType(child, types)) {
80                  children.add(child);
81              } else {
82                  children.addAll(this.findChildAstsOfType(child, types));
83              }
84              child = child.getNextSibling();
85          }
86          return children;
87      }
88  
89      private boolean hasStringLiteralOperand(final DetailAST node) {
90          boolean found = false;
91          DetailAST child = node.getFirstChild();
92          while (child != null) {
93              final int type = child.getType();
94              if (type == TokenTypes.STRING_LITERAL) {
95                  found = true;
96                  break;
97              }
98              if ((type == TokenTypes.PLUS || type == TokenTypes.PLUS_ASSIGN)
99                  && this.hasStringLiteralOperand(child)) {
100                 found = true;
101                 break;
102             }
103             child = child.getNextSibling();
104         }
105         return found;
106     }
107 
108     private static boolean isOfType(final DetailAST ast, final int... types) {
109         boolean yes = false;
110         for (final int type : types) {
111             if (ast.getType() == type) {
112                 yes = true;
113                 break;
114             }
115         }
116         return yes;
117     }
118 }