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.indentation; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 025 026/** 027 * Handler for operator new. 028 */ 029public class NewHandler extends AbstractExpressionHandler { 030 031 /** The AST which is handled by this handler. */ 032 private final DetailAST mainAst; 033 034 /** 035 * Construct an instance of this handler with the given indentation check, 036 * abstract syntax tree, and parent handler. 037 * 038 * @param indentCheck the indentation check 039 * @param ast the abstract syntax tree 040 * @param parent the parent handler 041 */ 042 public NewHandler(IndentationCheck indentCheck, 043 DetailAST ast, 044 AbstractExpressionHandler parent) { 045 super(indentCheck, "new", ast, parent); 046 mainAst = ast; 047 } 048 049 @Override 050 public void checkIndentation() { 051 // if new is on the line start and it is not the part of assignment. 052 if (isOnStartOfLine(mainAst)) { 053 final int columnNo = expandedTabsColumnNo(mainAst); 054 final IndentLevel level = getIndentImpl(); 055 056 final boolean forceStrictCondition = getIndentCheck().isForceStrictCondition(); 057 if (forceStrictCondition && !level.isAcceptable(columnNo) 058 || !forceStrictCondition && level.isGreaterThan(columnNo)) { 059 logError(mainAst, "", columnNo, level); 060 } 061 } 062 063 final DetailAST firstChild = mainAst.getFirstChild(); 064 if (firstChild != null) { 065 checkExpressionSubtree(firstChild, getIndent(), false, false); 066 } 067 068 final DetailAST expression = mainAst.findFirstToken(TokenTypes.ELIST); 069 if (checkNestedNew(expression) && isOnStartOfLine(expression)) { 070 final IndentLevel indentLevel = new IndentLevel(getIndent(), 071 getLineWrappingIndent()); 072 checkExpressionSubtree(expression, indentLevel, false, false); 073 } 074 075 final DetailAST lparen = mainAst.findFirstToken(TokenTypes.LPAREN); 076 checkLeftParen(lparen); 077 } 078 079 /** 080 * Check if nested {@code new} present. 081 * 082 * @param expression expression 083 * 084 * @return true if nested new is present. 085 */ 086 public boolean checkNestedNew(DetailAST expression) { 087 boolean result = false; 088 if (expression != null && expression.getFirstChild() != null) { 089 final boolean isNestedNewPresent = expression.getFirstChild() 090 .findFirstToken(TokenTypes.LITERAL_NEW) != null; 091 if (!isNestedNewPresent) { 092 result = true; 093 } 094 } 095 return result; 096 } 097 098 @Override 099 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}