View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.indentation;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
25  
26  /**
27   * Handler for operator new.
28   */
29  public class NewHandler extends AbstractExpressionHandler {
30  
31      /** The AST which is handled by this handler. */
32      private final DetailAST mainAst;
33  
34      /**
35       * Construct an instance of this handler with the given indentation check,
36       * abstract syntax tree, and parent handler.
37       *
38       * @param indentCheck   the indentation check
39       * @param ast           the abstract syntax tree
40       * @param parent        the parent handler
41       */
42      public NewHandler(IndentationCheck indentCheck,
43                        DetailAST ast,
44                        AbstractExpressionHandler parent) {
45          super(indentCheck, "new", ast, parent);
46          mainAst = ast;
47      }
48  
49      @Override
50      public void checkIndentation() {
51          // if new is on the line start and it is not the part of assignment.
52          if (isOnStartOfLine(mainAst)) {
53              final int columnNo = expandedTabsColumnNo(mainAst);
54              final IndentLevel level = getIndentImpl();
55  
56              final boolean forceStrictCondition = getIndentCheck().isForceStrictCondition();
57              if (forceStrictCondition && !level.isAcceptable(columnNo)
58                  || !forceStrictCondition && level.isGreaterThan(columnNo)) {
59                  logError(mainAst, "", columnNo, level);
60              }
61          }
62  
63          final DetailAST firstChild = mainAst.getFirstChild();
64          if (firstChild != null) {
65              checkExpressionSubtree(firstChild, getIndent(), false, false);
66          }
67  
68          final DetailAST expression = mainAst.findFirstToken(TokenTypes.ELIST);
69          if (checkNestedNew(expression) && isOnStartOfLine(expression)) {
70              final IndentLevel indentLevel = new IndentLevel(getIndent(),
71                      getLineWrappingIndent());
72              checkExpressionSubtree(expression, indentLevel, false, false);
73          }
74  
75          final DetailAST lparen = mainAst.findFirstToken(TokenTypes.LPAREN);
76          checkLeftParen(lparen);
77      }
78  
79      /**
80       * Check if nested {@code new} present.
81       *
82       * @param expression expression
83       *
84       * @return true if nested new is present.
85       */
86      public boolean checkNestedNew(DetailAST expression) {
87          boolean result = false;
88          if (expression != null && expression.getFirstChild() != null) {
89              final boolean isNestedNewPresent = expression.getFirstChild()
90                  .findFirstToken(TokenTypes.LITERAL_NEW) != null;
91              if (!isNestedNewPresent) {
92                  result = true;
93              }
94          }
95          return result;
96      }
97  
98      @Override
99      public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
100         final int offset;
101         if (TokenUtil.isOfType(child.getMainAst(), TokenTypes.OBJBLOCK)) {
102             offset = getBasicOffset();
103         }
104         else {
105             offset = getLineWrappingIndent();
106         }
107         return new IndentLevel(getIndent(), offset);
108     }
109 
110     @Override
111     protected IndentLevel getIndentImpl() {
112         IndentLevel result;
113         // if our expression isn't first on the line, just use the start
114         // of the line
115         if (getLineStart(mainAst) == mainAst.getColumnNo()) {
116             result = super.getIndentImpl();
117 
118             final boolean isLineWrappedNew = TokenUtil.isOfType(mainAst.getParent().getParent(),
119                                         TokenTypes.ASSIGN, TokenTypes.LITERAL_RETURN);
120 
121             if (isLineWrappedNew || doesChainedMethodNeedsLineWrapping()) {
122                 result = new IndentLevel(result, getLineWrappingIndent());
123             }
124         }
125         else {
126             result = new IndentLevel(getLineStart(mainAst));
127         }
128 
129         return result;
130     }
131 
132     /**
133      * A shortcut for {@code IndentationCheck} property.
134      *
135      * @return value of lineWrappingIndentation property
136      *         of {@code IndentationCheck}
137      */
138     private int getLineWrappingIndent() {
139         return getIndentCheck().getLineWrappingIndentation();
140     }
141 
142     @Override
143     protected boolean shouldIncreaseIndent() {
144         return false;
145     }
146 
147     /**
148      * The function checks if the new keyword is a child of chained method calls,
149      * it checks if the new is directly followed by equal operator or return operator.
150      *
151      * @return true if the new it is chained method calls and new keyword is directly followed
152      *         by assign or return
153      */
154     private boolean doesChainedMethodNeedsLineWrapping() {
155         DetailAST ast = mainAst.getParent();
156 
157         while (TokenUtil.isOfType(ast, TokenTypes.DOT, TokenTypes.METHOD_CALL, TokenTypes.EXPR)) {
158             ast = ast.getParent();
159         }
160 
161         return TokenUtil.isOfType(ast, TokenTypes.ASSIGN, TokenTypes.LITERAL_RETURN);
162     }
163 
164 }