com.liferay.source.formatter.checkstyle.check.BaseBuilderCheck Maven / Gradle / Ivy
/**
* SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
*/
package com.liferay.source.formatter.checkstyle.check;
import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FullIdent;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author Hugo Huijser
*/
public abstract class BaseBuilderCheck extends BaseChainedMethodCheck {
@Override
public int[] getDefaultTokens() {
return new int[] {
TokenTypes.ASSIGN, TokenTypes.INSTANCE_INIT, TokenTypes.METHOD_CALL
};
}
protected abstract boolean allowNullValues();
protected abstract List doGetBuilderInformationList();
@Override
protected void doVisitToken(DetailAST detailAST) {
if (isExcludedPath(RUN_OUTSIDE_PORTAL_EXCLUDES) ||
ListUtil.isEmpty(getAttributeValues(_ENFORCE_BUILDER_NAMES_KEY))) {
return;
}
if (detailAST.getType() == TokenTypes.INSTANCE_INIT) {
_checkAnonymousClass(detailAST);
return;
}
if (detailAST.getType() == TokenTypes.METHOD_CALL) {
_checkBuilder(detailAST);
return;
}
DetailAST parentDetailAST = detailAST.getParent();
if ((parentDetailAST.getType() != TokenTypes.EXPR) &&
(parentDetailAST.getType() != TokenTypes.VARIABLE_DEF)) {
return;
}
DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();
if ((nextSiblingDetailAST == null) ||
(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {
return;
}
String variableName = getVariableName(detailAST, parentDetailAST);
if (variableName != null) {
_checkAssignVariableStatement(
detailAST, variableName, nextSiblingDetailAST);
}
}
protected abstract String getAssignClassName(DetailAST assignDetailAST);
protected List getAvoidCastStringMethodNames() {
return Collections.emptyList();
}
protected List getBuilderInformationList() {
List builderInformationList =
doGetBuilderInformationList();
List enforceBuilderNames = getAttributeValues(
_ENFORCE_BUILDER_NAMES_KEY);
return ListUtil.filter(
builderInformationList,
builderInformation -> enforceBuilderNames.contains(
builderInformation.getBuilderClassName()));
}
protected String getNewInstanceTypeName(DetailAST assignDetailAST) {
DetailAST firstChildDetailAST = assignDetailAST.getFirstChild();
DetailAST assignValueDetailAST = null;
DetailAST parentDetailAST = assignDetailAST.getParent();
if (parentDetailAST.getType() == TokenTypes.EXPR) {
assignValueDetailAST = firstChildDetailAST.getNextSibling();
}
else {
assignValueDetailAST = firstChildDetailAST.getFirstChild();
}
if ((assignValueDetailAST == null) ||
(assignValueDetailAST.getType() != TokenTypes.LITERAL_NEW)) {
return null;
}
return getName(assignValueDetailAST);
}
protected Map getReservedKeywordsMap() {
return Collections.emptyMap();
}
protected abstract List getSupportsFunctionMethodNames();
protected abstract boolean isSupportsNestedMethodCalls();
protected static class BuilderInformation {
public BuilderInformation(
String className, String builderClassName, String... methodNames) {
_className = className;
_builderClassName = builderClassName;
_methodNames = methodNames;
}
public String getBuilderClassName() {
return _builderClassName;
}
public String getClassName() {
return _className;
}
public String[] getMethodNames() {
return _methodNames;
}
private final String _builderClassName;
private final String _className;
private final String[] _methodNames;
}
private List _addNonfinalVariableRangeList(
List nonfinalVariableRangeList, DetailAST detailAST) {
if (nonfinalVariableRangeList == null) {
nonfinalVariableRangeList = new ArrayList<>();
}
List variableNames = _getVariableNames(detailAST);
for (String variableName : variableNames) {
DetailAST variableTypeDetailAST = getVariableTypeDetailAST(
detailAST, variableName);
if (variableTypeDetailAST == null) {
String[] lines = getLines();
nonfinalVariableRangeList.add(new int[] {0, lines.length});
continue;
}
DetailAST variableDefinitionDetailAST =
variableTypeDetailAST.getParent();
DetailAST parentDetailAST = variableDefinitionDetailAST.getParent();
if (parentDetailAST.getType() == TokenTypes.OBJBLOCK) {
if (AnnotationUtil.containsAnnotation(
variableDefinitionDetailAST, "Reference")) {
continue;
}
DetailAST modifiersDetailAST =
variableDefinitionDetailAST.findFirstToken(
TokenTypes.MODIFIERS);
if ((modifiersDetailAST == null) ||
!modifiersDetailAST.branchContains(TokenTypes.FINAL)) {
nonfinalVariableRangeList.add(
new int[] {
getStartLineNumber(parentDetailAST),
getEndLineNumber(parentDetailAST)
});
}
continue;
}
boolean isFinal = true;
int start = getStartLineNumber(variableDefinitionDetailAST);
int end = getEndLineNumber(variableDefinitionDetailAST);
List variableCallerDetailASTList =
getVariableCallerDetailASTList(
variableDefinitionDetailAST, variableName);
for (DetailAST variableCallerDetailAST :
variableCallerDetailASTList) {
parentDetailAST = variableCallerDetailAST.getParent();
if ((parentDetailAST.getType() == TokenTypes.ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.BAND_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.BOR_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.BXOR_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.DEC) ||
(parentDetailAST.getType() == TokenTypes.DIV_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.INC) ||
(parentDetailAST.getType() == TokenTypes.MINUS_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.MOD_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.PLUS_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.POST_DEC) ||
(parentDetailAST.getType() == TokenTypes.POST_INC) ||
(parentDetailAST.getType() == TokenTypes.SL_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.SR_ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.STAR_ASSIGN)) {
isFinal = false;
}
end = Math.max(end, getEndLineNumber(variableCallerDetailAST));
}
if (!isFinal) {
nonfinalVariableRangeList.add(new int[] {start, end});
}
}
return nonfinalVariableRangeList;
}
private void _checkAnonymousClass(DetailAST detailAST) {
DetailAST parentDetailAST = detailAST.getParent();
if (parentDetailAST.getType() != TokenTypes.OBJBLOCK) {
return;
}
parentDetailAST = parentDetailAST.getParent();
if (parentDetailAST.getType() != TokenTypes.LITERAL_NEW) {
return;
}
String className = getName(parentDetailAST);
if (className == null) {
return;
}
BuilderInformation builderInformation =
_findBuilderInformationByClassName(className);
if (builderInformation == null) {
return;
}
List childDetailASTList = getAllChildTokens(
detailAST, true, ALL_TYPES);
for (DetailAST childDetailAST : childDetailASTList) {
if (getHiddenBefore(childDetailAST) != null) {
return;
}
}
List methodCallDetailASTList = getAllChildTokens(
detailAST, true, TokenTypes.METHOD_CALL);
for (DetailAST methodCallDetailAST : methodCallDetailASTList) {
parentDetailAST = methodCallDetailAST.getParent();
if (parentDetailAST.getType() != TokenTypes.EXPR) {
continue;
}
parentDetailAST = parentDetailAST.getParent();
if (parentDetailAST.getType() != TokenTypes.SLIST) {
continue;
}
DetailAST firstChildDetailAST = methodCallDetailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.IDENT) {
continue;
}
if (!ArrayUtil.contains(
builderInformation.getMethodNames(),
firstChildDetailAST.getText())) {
return;
}
if (isSupportsNestedMethodCalls()) {
parentDetailAST = getParentWithTokenType(
methodCallDetailAST, TokenTypes.DO_WHILE, TokenTypes.LAMBDA,
TokenTypes.LITERAL_FOR, TokenTypes.LITERAL_TRY,
TokenTypes.LITERAL_WHILE);
if ((parentDetailAST != null) &&
(detailAST.getLineNo() <= parentDetailAST.getLineNo())) {
return;
}
parentDetailAST = getParentWithTokenType(
methodCallDetailAST, TokenTypes.LITERAL_ELSE);
if ((parentDetailAST != null) &&
(detailAST.getLineNo() <= parentDetailAST.getLineNo())) {
firstChildDetailAST = parentDetailAST.getFirstChild();
if (firstChildDetailAST.getType() ==
TokenTypes.LITERAL_IF) {
return;
}
}
}
else if (!equals(parentDetailAST.getParent(), detailAST)) {
return;
}
if (!allowNullValues()) {
DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
TokenTypes.ELIST);
DetailAST childDetailAST = elistDetailAST.getFirstChild();
while (true) {
if (childDetailAST == null) {
break;
}
if (_isNullValueExpression(childDetailAST)) {
return;
}
childDetailAST = childDetailAST.getNextSibling();
}
}
}
log(
detailAST, _MSG_USE_BUILDER_INSTEAD,
builderInformation.getBuilderClassName(), className);
}
private void _checkAssignVariableStatement(
DetailAST assignDetailAST, String variableName,
DetailAST nextSiblingDetailAST) {
BuilderInformation builderInformation =
_findBuilderInformationByClassName(
getAssignClassName(assignDetailAST));
if (builderInformation == null) {
return;
}
while (true) {
nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();
if ((nextSiblingDetailAST == null) ||
hasPrecedingPlaceholder(nextSiblingDetailAST)) {
return;
}
FullIdent fullIdent = getMethodCallFullIdent(
nextSiblingDetailAST, variableName,
builderInformation.getMethodNames());
if (fullIdent != null) {
DetailAST methodCallDetailAST =
nextSiblingDetailAST.findFirstToken(TokenTypes.METHOD_CALL);
DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
TokenTypes.ELIST);
DetailAST childDetailAST = elistDetailAST.getFirstChild();
while (true) {
if (childDetailAST == null) {
log(
assignDetailAST, _MSG_USE_BUILDER,
builderInformation.getBuilderClassName(),
assignDetailAST.getLineNo(), fullIdent.getLineNo());
return;
}
if ((!allowNullValues() &&
_isNullValueExpression(childDetailAST)) ||
containsVariableName(
childDetailAST, variableName, true)) {
return;
}
childDetailAST = childDetailAST.getNextSibling();
}
}
if (containsVariableName(
nextSiblingDetailAST, variableName, true)) {
return;
}
}
}
private void _checkBuilder(DetailAST methodCallDetailAST) {
DetailAST firstChildDetailAST = methodCallDetailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.DOT) {
return;
}
firstChildDetailAST = firstChildDetailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.IDENT) {
return;
}
String builderClassName = firstChildDetailAST.getText();
BuilderInformation builderInformation =
_findBuilderInformationByBuilderClassName(builderClassName);
if (builderInformation == null) {
return;
}
_checkUnneededCastString(methodCallDetailAST);
Map> expressionDetailASTMap =
_getExpressionDetailASTMap(methodCallDetailAST, false);
if (!allowNullValues()) {
_checkNullValues(expressionDetailASTMap, builderClassName);
}
_checkReservedKeywords(expressionDetailASTMap);
DetailAST parentDetailAST = methodCallDetailAST.getParent();
int endLineNumber = getEndLineNumber(parentDetailAST);
int startLineNumber = getStartLineNumber(parentDetailAST);
while ((parentDetailAST.getType() == TokenTypes.DOT) ||
(parentDetailAST.getType() == TokenTypes.EXPR) ||
(parentDetailAST.getType() == TokenTypes.METHOD_CALL)) {
endLineNumber = getEndLineNumber(parentDetailAST);
startLineNumber = getStartLineNumber(parentDetailAST);
parentDetailAST = parentDetailAST.getParent();
}
if (parentDetailAST.getType() == TokenTypes.ELIST) {
while (true) {
DetailAST grandParentDetailAST = parentDetailAST.getParent();
if (grandParentDetailAST == null) {
return;
}
if (grandParentDetailAST.getType() == TokenTypes.SLIST) {
_checkInline(
parentDetailAST, expressionDetailASTMap,
builderClassName, startLineNumber, endLineNumber);
return;
}
parentDetailAST = grandParentDetailAST;
}
}
if (parentDetailAST.getType() == TokenTypes.LITERAL_RETURN) {
_checkInline(
parentDetailAST, expressionDetailASTMap, builderClassName,
startLineNumber, endLineNumber);
return;
}
if (parentDetailAST.getType() != TokenTypes.ASSIGN) {
return;
}
DetailAST assignDetailAST = parentDetailAST;
parentDetailAST = assignDetailAST.getParent();
DetailAST grandParentDetailAST = parentDetailAST.getParent();
if (grandParentDetailAST.getType() == TokenTypes.OBJBLOCK) {
DetailAST greatGrandParentDetailAST =
grandParentDetailAST.getParent();
if (greatGrandParentDetailAST.getType() == TokenTypes.CLASS_DEF) {
return;
}
}
_checkInline(
parentDetailAST, expressionDetailASTMap, builderClassName,
startLineNumber, endLineNumber);
if (isJSPFile()) {
return;
}
String variableName = getVariableName(assignDetailAST, parentDetailAST);
_checkBuildString(
methodCallDetailAST, assignDetailAST, variableName, startLineNumber,
endLineNumber);
_checkInlineIfStatement(
parentDetailAST, builderClassName, variableName,
firstChildDetailAST.getLineNo());
firstChildDetailAST = assignDetailAST.getFirstChild();
DetailAST assignValueDetailAST = null;
if (parentDetailAST.getType() == TokenTypes.EXPR) {
assignValueDetailAST = firstChildDetailAST.getNextSibling();
}
else {
assignValueDetailAST = firstChildDetailAST.getFirstChild();
}
if ((assignValueDetailAST == null) ||
(assignValueDetailAST.getType() != TokenTypes.METHOD_CALL)) {
return;
}
List variableNames = _getVariableNames(
parentDetailAST, "get.*");
String[] builderMethodNames = builderInformation.getMethodNames();
DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();
while (true) {
if (nextSiblingDetailAST == null) {
return;
}
FullIdent fullIdent = getMethodCallFullIdent(
nextSiblingDetailAST, variableName, builderMethodNames);
if (fullIdent != null) {
if (_hasVariableNameInMethodCall(
nextSiblingDetailAST.findFirstToken(
TokenTypes.METHOD_CALL),
variableName)) {
return;
}
log(
assignDetailAST, _MSG_INCLUDE_BUILDER, fullIdent.getText(),
fullIdent.getLineNo(), builderClassName,
assignDetailAST.getLineNo());
return;
}
if (containsVariableName(
nextSiblingDetailAST, variableName, false)) {
return;
}
for (String curVariableName : variableNames) {
if (containsVariableName(
nextSiblingDetailAST, curVariableName, true)) {
return;
}
}
nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();
}
}
private void _checkBuildString(
DetailAST methodCallDetailAST, DetailAST assignDetailAST,
String variableName, int startLineNumber, int endLineNumber) {
Class> clazz = getClass();
String className = clazz.getName();
if (!className.endsWith("URLBuilderCheck")) {
return;
}
List chainedMethodNames = getChainedMethodNames(
methodCallDetailAST);
String methodName = chainedMethodNames.get(
chainedMethodNames.size() - 1);
if (methodName.equals("buildString")) {
return;
}
DetailAST variableDefinitionDetailAST = getVariableDefinitionDetailAST(
assignDetailAST, variableName, false);
if (variableDefinitionDetailAST == null) {
return;
}
List variableCallerDetailASTList =
getVariableCallerDetailASTList(
variableDefinitionDetailAST, variableName);
DetailAST lastDetailAST = variableCallerDetailASTList.get(
variableCallerDetailASTList.size() - 1);
if (lastDetailAST.getLineNo() <= endLineNumber) {
return;
}
FullIdent fullIdent = FullIdent.createFullIdent(
lastDetailAST.getParent());
if (!Objects.equals(fullIdent.getText(), variableName + ".toString")) {
return;
}
if (variableCallerDetailASTList.size() > 1) {
DetailAST secondToLastDetailAST = variableCallerDetailASTList.get(
variableCallerDetailASTList.size() - 2);
if (secondToLastDetailAST.getLineNo() > startLineNumber) {
return;
}
}
if (equals(variableDefinitionDetailAST, assignDetailAST.getParent()) ||
equals(
getParentWithTokenType(assignDetailAST, TokenTypes.SLIST),
getParentWithTokenType(lastDetailAST, TokenTypes.SLIST))) {
log(
fullIdent.getLineNo(), _MSG_USE_BUILD_STRING, variableName,
methodName);
}
}
private void _checkInline(
DetailAST variableDefinitionDetailAST, DetailAST parentDetailAST,
String builderClassName, List supportsFunctionMethodNames,
Map> expressionDetailASTMap,
int startLineNumber, int endlineNumber) {
DetailAST identDetailAST = variableDefinitionDetailAST.findFirstToken(
TokenTypes.IDENT);
String matchingMethodName = _getInlineExpressionMethodName(
expressionDetailASTMap, ListUtil.fromArray(identDetailAST));
if (!supportsFunctionMethodNames.contains(matchingMethodName)) {
return;
}
List dependentIdentDetailASTList =
getDependentIdentDetailASTList(
variableDefinitionDetailAST, startLineNumber);
if (dependentIdentDetailASTList.isEmpty()) {
return;
}
List nonfinalVariableRangeList = _addNonfinalVariableRangeList(
null, variableDefinitionDetailAST);
String variableName = identDetailAST.getText();
for (int i = dependentIdentDetailASTList.size() - 1; i >= 0; i--) {
DetailAST dependentIdentDetailAST = dependentIdentDetailASTList.get(
i);
if (variableName.equals(dependentIdentDetailAST.getText()) &&
(dependentIdentDetailAST.getLineNo() > endlineNumber)) {
return;
}
}
matchingMethodName = _getInlineExpressionMethodName(
expressionDetailASTMap, dependentIdentDetailASTList);
if (matchingMethodName == null) {
return;
}
DetailAST literalTryDetailAST = getParentWithTokenType(
variableDefinitionDetailAST, TokenTypes.LITERAL_TRY);
if (literalTryDetailAST != null) {
DetailAST literalCatchDetailAST = getParentWithTokenType(
variableDefinitionDetailAST, TokenTypes.LITERAL_CATCH);
if ((literalCatchDetailAST == null) ||
(literalCatchDetailAST.getLineNo() <
literalTryDetailAST.getLineNo())) {
return;
}
}
List additionalDependentDetailASTList =
_getAdditionalDependentDetailASTList(
dependentIdentDetailASTList,
variableDefinitionDetailAST.getLineNo(), startLineNumber);
if (additionalDependentDetailASTList.isEmpty()) {
if (!_hasNonfinalVariableReference(
nonfinalVariableRangeList,
getStartLineNumber(variableDefinitionDetailAST),
getStartLineNumber(variableDefinitionDetailAST))) {
log(
identDetailAST, _MSG_INLINE_BUILDER_1,
identDetailAST.getText(), identDetailAST.getLineNo(),
builderClassName, startLineNumber);
}
return;
}
DetailAST lastAdditionalDependentDetailAST =
additionalDependentDetailASTList.get(
additionalDependentDetailASTList.size() - 1);
if (lastAdditionalDependentDetailAST.getLineNo() >=
parentDetailAST.getLineNo()) {
return;
}
for (DetailAST additionalDependentDetailAST :
additionalDependentDetailASTList) {
List assignDetailASTList = getAllChildTokens(
additionalDependentDetailAST, true, TokenTypes.ASSIGN);
for (DetailAST assignDetailAST : assignDetailASTList) {
String assignVariableName = null;
DetailAST firstChildDetailAST = assignDetailAST.getFirstChild();
if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
assignVariableName = firstChildDetailAST.getText();
}
else {
DetailAST previousSiblingDetailAST =
assignDetailAST.getPreviousSibling();
if ((previousSiblingDetailAST != null) &&
(previousSiblingDetailAST.getType() ==
TokenTypes.IDENT)) {
assignVariableName = previousSiblingDetailAST.getText();
}
}
if (assignVariableName == null) {
return;
}
if (!variableName.equals(assignVariableName)) {
for (int i = dependentIdentDetailASTList.size() - 1; i >= 0;
i--) {
DetailAST dependentIdentDetailAST =
dependentIdentDetailASTList.get(i);
if (assignVariableName.equals(
dependentIdentDetailAST.getText()) &&
(dependentIdentDetailAST.getLineNo() >
endlineNumber)) {
return;
}
}
}
nonfinalVariableRangeList = _addNonfinalVariableRangeList(
nonfinalVariableRangeList, additionalDependentDetailAST);
}
if (_hasNonfinalVariableReference(
nonfinalVariableRangeList,
getStartLineNumber(variableDefinitionDetailAST),
getEndLineNumber(lastAdditionalDependentDetailAST))) {
return;
}
List methodCallDetailASTList = getAllChildTokens(
additionalDependentDetailAST, true, TokenTypes.METHOD_CALL);
for (DetailAST methodCallDetailAST : methodCallDetailASTList) {
DetailAST firstChildDetailAST =
methodCallDetailAST.getFirstChild();
if (firstChildDetailAST.getType() == TokenTypes.DOT) {
FullIdent fullIdent = FullIdent.createFullIdent(
firstChildDetailAST);
String methodCall = fullIdent.getText();
if (!methodCall.startsWith(variableName + ".") &&
!methodCall.contains(".get")) {
return;
}
}
else if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
String methodName = firstChildDetailAST.getText();
if (!methodName.matches("_?get.*")) {
return;
}
}
else {
return;
}
}
}
log(
identDetailAST, _MSG_INLINE_BUILDER_2, variableName,
identDetailAST.getLineNo(),
_getLineNumbers(additionalDependentDetailASTList), builderClassName,
startLineNumber);
}
private void _checkInline(
DetailAST parentDetailAST,
Map> expressionDetailASTMap,
String builderClassName, int startLineNumber, int endLineNumber) {
if (!isAttributeValue(_CHECK_INLINE)) {
return;
}
List supportsFunctionMethodNames =
getSupportsFunctionMethodNames();
if (supportsFunctionMethodNames.isEmpty()) {
return;
}
int branchStatementLineNumber = -1;
List branchingStatementDetailASTList = getAllChildTokens(
parentDetailAST.getParent(), true, TokenTypes.LITERAL_BREAK,
TokenTypes.LITERAL_CONTINUE, TokenTypes.LITERAL_RETURN);
for (DetailAST branchingStatementDetailAST :
branchingStatementDetailASTList) {
int lineNumber = branchingStatementDetailAST.getLineNo();
if (lineNumber >= startLineNumber) {
break;
}
branchStatementLineNumber = lineNumber;
}
List variableDefinitionDetailASTList = getAllChildTokens(
parentDetailAST.getParent(), false, TokenTypes.VARIABLE_DEF);
for (DetailAST variableDefinitionDetailAST :
variableDefinitionDetailASTList) {
int lineNumber = variableDefinitionDetailAST.getLineNo();
if (lineNumber >= startLineNumber) {
return;
}
if (branchStatementLineNumber < lineNumber) {
_checkInline(
variableDefinitionDetailAST, parentDetailAST,
builderClassName, supportsFunctionMethodNames,
expressionDetailASTMap, startLineNumber, endLineNumber);
}
}
}
private void _checkInlineIfStatement(
DetailAST parentDetailAST, String builderClassName, String variableName,
int lineNumber) {
List supportsFunctionMethodNames =
getSupportsFunctionMethodNames();
if (supportsFunctionMethodNames.isEmpty()) {
return;
}
DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();
if (nextSiblingDetailAST.getType() != TokenTypes.SEMI) {
return;
}
DetailAST detailAST = nextSiblingDetailAST.getNextSibling();
int rangeLineNumber = -1;
int startLineNumber = -1;
List nonfinalVariableRangeList = new ArrayList<>();
while (true) {
if (detailAST.getType() == TokenTypes.SEMI) {
detailAST = detailAST.getNextSibling();
continue;
}
DetailAST variableDefinitionDetailAST = null;
if (detailAST.getType() == TokenTypes.EXPR) {
DetailAST firstChildDetailAST = detailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.ASSIGN) {
return;
}
String name = getName(firstChildDetailAST);
if (name == null) {
return;
}
variableDefinitionDetailAST = getVariableDefinitionDetailAST(
detailAST, name, false);
}
else if (detailAST.getType() == TokenTypes.VARIABLE_DEF) {
variableDefinitionDetailAST = detailAST;
}
if (variableDefinitionDetailAST == null) {
break;
}
List variableCallerDetailASTList =
getVariableCallerDetailASTList(variableDefinitionDetailAST);
if (variableCallerDetailASTList.isEmpty()) {
return;
}
nonfinalVariableRangeList = _addNonfinalVariableRangeList(
nonfinalVariableRangeList, variableDefinitionDetailAST);
DetailAST lastDetailAST = variableCallerDetailASTList.get(
variableCallerDetailASTList.size() - 1);
if (startLineNumber == -1) {
startLineNumber = getStartLineNumber(detailAST);
}
rangeLineNumber = Math.max(
rangeLineNumber, lastDetailAST.getLineNo());
detailAST = detailAST.getNextSibling();
}
List slistDetailASTList = new ArrayList<>();
int endLineNumber = -1;
while (true) {
if ((detailAST == null) ||
((detailAST.getType() != TokenTypes.LITERAL_ELSE) &&
(detailAST.getType() != TokenTypes.LITERAL_IF))) {
break;
}
if (startLineNumber == -1) {
startLineNumber = getStartLineNumber(detailAST);
}
if (endLineNumber == -1) {
nonfinalVariableRangeList = _addNonfinalVariableRangeList(
nonfinalVariableRangeList, detailAST);
endLineNumber = getEndLineNumber(detailAST);
if (rangeLineNumber > endLineNumber) {
return;
}
}
if (detailAST.getType() == TokenTypes.LITERAL_ELSE) {
DetailAST firstChildDetailAST = detailAST.getFirstChild();
if (firstChildDetailAST.getType() == TokenTypes.SLIST) {
slistDetailASTList.add(firstChildDetailAST);
break;
}
if (firstChildDetailAST.getType() != TokenTypes.LITERAL_IF) {
return;
}
detailAST = firstChildDetailAST;
}
DetailAST slistDetailAST = detailAST.findFirstToken(
TokenTypes.SLIST);
if (slistDetailAST == null) {
return;
}
slistDetailASTList.add(slistDetailAST);
detailAST = slistDetailAST.getNextSibling();
}
if (slistDetailASTList.isEmpty()) {
return;
}
String methodKey = null;
for (DetailAST slistDetailAST : slistDetailASTList) {
String curMethodKey = _getMethodKey(
slistDetailAST, variableName, supportsFunctionMethodNames);
if (curMethodKey == null) {
return;
}
if (methodKey == null) {
methodKey = curMethodKey;
continue;
}
if (!methodKey.equals(curMethodKey)) {
return;
}
}
if (!_hasNonfinalVariableReference(
nonfinalVariableRangeList, startLineNumber, endLineNumber)) {
log(
startLineNumber, _MSG_INLINE_IF_STATEMENT, builderClassName,
lineNumber);
}
}
private void _checkNullValues(
Map> expressionDetailASTMap,
String builderClassName) {
for (Map.Entry> entry :
expressionDetailASTMap.entrySet()) {
for (DetailAST expressionDetailAST : entry.getValue()) {
if (_isNullValueExpression(expressionDetailAST)) {
log(
expressionDetailAST, _MSG_INCORRECT_NULL_VALUE,
builderClassName);
}
}
}
}
private void _checkReservedKeywords(
Map> expressionDetailASTMap) {
Map reservedKeywordsMap = getReservedKeywordsMap();
for (Map.Entry entry :
reservedKeywordsMap.entrySet()) {
String methodName = entry.getKey();
List expressionDetailASTList =
expressionDetailASTMap.get(methodName);
if (expressionDetailASTList == null) {
continue;
}
for (DetailAST expressionDetailAST : expressionDetailASTList) {
DetailAST previousDetailAST =
expressionDetailAST.getPreviousSibling();
if (previousDetailAST != null) {
continue;
}
DetailAST firstChildDetailAST =
expressionDetailAST.getFirstChild();
String value = null;
if (firstChildDetailAST.getType() ==
TokenTypes.STRING_LITERAL) {
value = StringUtil.removeChar(
firstChildDetailAST.getText(), CharPool.QUOTE);
}
else if (firstChildDetailAST.getType() == TokenTypes.DOT) {
FullIdent fullIdent = FullIdent.createFullIdent(
firstChildDetailAST);
value = fullIdent.getText();
}
else {
continue;
}
String[][] reservedKeywordsArray = entry.getValue();
for (String[] reservedKeywordArray : reservedKeywordsArray) {
String reservedKey = reservedKeywordArray[0];
if (value.equals(reservedKey)) {
log(
expressionDetailAST, _MSG_RESERVED_KEYWORD,
methodName, value, reservedKeywordArray[1]);
break;
}
}
}
}
}
private void _checkUnneededCastString(DetailAST methodCallDetailAST) {
List avoidCastStringMethodNames =
getAvoidCastStringMethodNames();
if (avoidCastStringMethodNames.isEmpty()) {
return;
}
Map> expressionDetailASTMap =
_getExpressionDetailASTMap(methodCallDetailAST, true);
for (String avoidCastStringMethodName :
getAvoidCastStringMethodNames()) {
List expressionDetailASTList =
expressionDetailASTMap.get(avoidCastStringMethodName);
if (expressionDetailASTList == null) {
continue;
}
for (DetailAST expressionDetailAST : expressionDetailASTList) {
if (expressionDetailAST.getType() != TokenTypes.EXPR) {
continue;
}
DetailAST childDetailAST = expressionDetailAST.getFirstChild();
if (childDetailAST.getType() == TokenTypes.METHOD_CALL) {
FullIdent fullIdent = FullIdent.createFullIdentBelow(
childDetailAST);
String methodCall = fullIdent.getText();
if (methodCall.equals("String.valueOf") ||
methodCall.endsWith(".toString")) {
log(
expressionDetailAST, _MSG_UNNEEDED_STRING_CAST,
avoidCastStringMethodName);
}
}
}
}
}
private BuilderInformation _findBuilderInformationByBuilderClassName(
String builderClassName) {
for (BuilderInformation builderInformation :
getBuilderInformationList()) {
if (builderClassName.equals(
builderInformation.getBuilderClassName())) {
return builderInformation;
}
}
return null;
}
private BuilderInformation _findBuilderInformationByClassName(
String className) {
if (className == null) {
return null;
}
for (BuilderInformation builderInformation :
getBuilderInformationList()) {
if (className.equals(builderInformation.getClassName())) {
return builderInformation;
}
}
return null;
}
private List _getAdditionalDependentDetailASTList(
List dependentIdentDetailASTList, int startLineNumber,
int endLineNumber) {
List dependentDetailASTList = new ArrayList<>();
for (DetailAST dependentIdentDetailAST : dependentIdentDetailASTList) {
if (dependentIdentDetailAST.getLineNo() >= endLineNumber) {
return dependentDetailASTList;
}
DetailAST detailAST = dependentIdentDetailAST;
while (true) {
DetailAST parentDetailAST = detailAST.getParent();
if (parentDetailAST.getLineNo() < startLineNumber) {
if (!dependentDetailASTList.contains(detailAST)) {
dependentDetailASTList.add(detailAST);
}
break;
}
detailAST = parentDetailAST;
}
}
return dependentDetailASTList;
}
private Map> _getExpressionDetailASTMap(
DetailAST methodCallDetailAST, boolean lastExpressionOnly) {
Map> expressionDetailASTMap = new HashMap<>();
while (true) {
String methodName = getMethodName(methodCallDetailAST);
List expressionDetailASTList =
expressionDetailASTMap.get(methodName);
if (expressionDetailASTList == null) {
expressionDetailASTList = new ArrayList<>();
}
DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
TokenTypes.ELIST);
if (lastExpressionOnly) {
DetailAST childDetailAST = elistDetailAST.getLastChild();
if (childDetailAST != null) {
expressionDetailASTList.add(childDetailAST);
}
}
else {
DetailAST childDetailAST = elistDetailAST.getFirstChild();
while (true) {
if (childDetailAST == null) {
break;
}
if (childDetailAST.getType() != TokenTypes.COMMA) {
expressionDetailASTList.add(childDetailAST);
}
childDetailAST = childDetailAST.getNextSibling();
}
}
if (!expressionDetailASTList.isEmpty()) {
expressionDetailASTMap.put(methodName, expressionDetailASTList);
}
DetailAST parentDetailAST = methodCallDetailAST.getParent();
if (parentDetailAST.getType() != TokenTypes.DOT) {
return expressionDetailASTMap;
}
methodCallDetailAST = parentDetailAST.getParent();
}
}
private String _getInlineExpressionMethodName(
Map> expressionDetailASTMap,
List dependentIdentDetailASTList) {
String methodName = null;
for (Map.Entry> entry :
expressionDetailASTMap.entrySet()) {
for (DetailAST expressionDetailAST : entry.getValue()) {
List variableNames = _getVariableNames(
expressionDetailAST);
for (DetailAST dependentIdentDetailAST :
dependentIdentDetailASTList) {
if (variableNames.contains(
dependentIdentDetailAST.getText())) {
List nonfinalVariableRangeList =
_addNonfinalVariableRangeList(
null, expressionDetailAST);
for (int[] array : nonfinalVariableRangeList) {
if (expressionDetailAST.getLineNo() > array[0]) {
return null;
}
}
if (methodName != null) {
return null;
}
methodName = entry.getKey();
break;
}
}
}
}
return methodName;
}
private String _getLineNumbers(List detailASTList) {
StringBundler sb = new StringBundler(detailASTList.size() * 2);
for (DetailAST detailAST : detailASTList) {
sb.append(detailAST.getLineNo());
sb.append(StringPool.COMMA_AND_SPACE);
}
sb.setIndex(sb.index() - 1);
return sb.toString();
}
private String _getMethodKey(
DetailAST slistDetailAST, String variableName,
List supportsFunctionMethodNames) {
DetailAST lastChildDetailAST = slistDetailAST.getLastChild();
if (lastChildDetailAST.getType() != TokenTypes.RCURLY) {
return null;
}
DetailAST previousSiblingDetailAST =
lastChildDetailAST.getPreviousSibling();
if ((previousSiblingDetailAST == null) ||
(previousSiblingDetailAST.getType() != TokenTypes.SEMI)) {
return null;
}
previousSiblingDetailAST =
previousSiblingDetailAST.getPreviousSibling();
if ((previousSiblingDetailAST == null) ||
(previousSiblingDetailAST.getType() != TokenTypes.EXPR)) {
return null;
}
DetailAST methodCallDetailAST =
previousSiblingDetailAST.getFirstChild();
if (methodCallDetailAST.getType() != TokenTypes.METHOD_CALL) {
return null;
}
DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
TokenTypes.ELIST);
if (elistDetailAST == null) {
return null;
}
List exprDetailASTList = getAllChildTokens(
elistDetailAST, false, TokenTypes.EXPR);
for (DetailAST exprDetailAST : exprDetailASTList) {
DetailAST exprChildDetailAST = exprDetailAST.getFirstChild();
if (exprChildDetailAST.getType() == TokenTypes.LITERAL_NULL) {
return null;
}
}
DetailAST firstChildDetailAST = methodCallDetailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.DOT) {
return null;
}
firstChildDetailAST = firstChildDetailAST.getFirstChild();
if ((firstChildDetailAST.getType() != TokenTypes.IDENT) ||
!variableName.equals(firstChildDetailAST.getText())) {
return null;
}
DetailAST nextSiblingDetailAST = firstChildDetailAST.getNextSibling();
if (nextSiblingDetailAST.getType() != TokenTypes.IDENT) {
return null;
}
String methodName = nextSiblingDetailAST.getText();
if (!supportsFunctionMethodNames.contains(methodName)) {
return null;
}
String methodKey = methodName;
if (exprDetailASTList.size() > 1) {
FullIdent fullIdent = FullIdent.createFullIdentBelow(
exprDetailASTList.get(0));
methodKey += ":" + fullIdent.getText();
}
List methodCallDetailASTList = getAllChildTokens(
slistDetailAST, true, TokenTypes.METHOD_CALL);
for (DetailAST curMethodCallDetailAST : methodCallDetailASTList) {
if (equals(curMethodCallDetailAST, methodCallDetailAST)) {
continue;
}
firstChildDetailAST = curMethodCallDetailAST.getFirstChild();
if (firstChildDetailAST.getType() != TokenTypes.DOT) {
continue;
}
firstChildDetailAST = firstChildDetailAST.getFirstChild();
if (variableName.equals(firstChildDetailAST.getText())) {
return null;
}
}
return methodKey;
}
private List _getVariableNames(DetailAST detailAST) {
return _getVariableNames(detailAST, null);
}
private List _getVariableNames(
DetailAST detailAST, String excludeRegex) {
List variableNames = new ArrayList<>();
List identDetailASTList = getAllChildTokens(
detailAST, true, TokenTypes.IDENT);
for (DetailAST identDetailAST : identDetailASTList) {
String variableName = identDetailAST.getText();
if (!variableName.matches("[a-z_].*")) {
continue;
}
DetailAST parentDetailAST = identDetailAST.getParent();
if ((parentDetailAST.getType() == TokenTypes.ASSIGN) ||
(parentDetailAST.getType() == TokenTypes.EXPR) ||
(parentDetailAST.getType() == TokenTypes.TYPECAST) ||
ArrayUtil.contains(
ARITHMETIC_OPERATOR_TOKEN_TYPES,
parentDetailAST.getType()) ||
ArrayUtil.contains(
CONDITIONAL_OPERATOR_TOKEN_TYPES,
parentDetailAST.getType()) ||
ArrayUtil.contains(
RELATIONAL_OPERATOR_TOKEN_TYPES,
parentDetailAST.getType()) ||
ArrayUtil.contains(
UNARY_OPERATOR_TOKEN_TYPES, parentDetailAST.getType())) {
variableNames.add(variableName);
}
else if (parentDetailAST.getType() == TokenTypes.DOT) {
DetailAST nextSiblingDetailAST =
identDetailAST.getNextSibling();
if (nextSiblingDetailAST != null) {
if ((nextSiblingDetailAST.getType() != TokenTypes.IDENT) ||
(excludeRegex == null)) {
variableNames.add(variableName);
}
else {
String s = nextSiblingDetailAST.getText();
if (!s.matches(excludeRegex)) {
variableNames.add(variableName);
}
}
}
}
}
return variableNames;
}
private boolean _hasNonfinalVariableReference(
List nonfinalVariableRangeList, int start, int end) {
for (int[] range : nonfinalVariableRangeList) {
int rangeStart = range[0];
int rangeEnd = range[1];
if ((rangeStart < start) || (rangeEnd > end)) {
return true;
}
}
return false;
}
private boolean _hasVariableNameInMethodCall(
DetailAST methodCallDetailAST, String variableName) {
DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
TokenTypes.ELIST);
for (String name : getNames(elistDetailAST, true)) {
if (variableName.equals(name)) {
return true;
}
}
return false;
}
private boolean _isNullValueExpression(DetailAST detailAST) {
if (detailAST.getType() != TokenTypes.EXPR) {
return false;
}
DetailAST firstChildDetailAST = detailAST.getFirstChild();
if (firstChildDetailAST.getType() == TokenTypes.LITERAL_NULL) {
return true;
}
if (firstChildDetailAST.getType() == TokenTypes.TYPECAST) {
DetailAST lastChildDetailAST = firstChildDetailAST.getLastChild();
if (lastChildDetailAST.getType() == TokenTypes.LITERAL_NULL) {
return true;
}
}
return false;
}
private static final String _CHECK_INLINE = "checkInline";
private static final String _ENFORCE_BUILDER_NAMES_KEY =
"enforceBuilderNames";
private static final String _MSG_INCLUDE_BUILDER = "builder.include";
private static final String _MSG_INCORRECT_NULL_VALUE =
"null.value.incorrect";
private static final String _MSG_INLINE_BUILDER_1 = "builder.inline.1";
private static final String _MSG_INLINE_BUILDER_2 = "builder.inline.2";
private static final String _MSG_INLINE_IF_STATEMENT =
"if.statement.inline";
private static final String _MSG_RESERVED_KEYWORD = "keyword.reserved";
private static final String _MSG_UNNEEDED_STRING_CAST =
"string.cast.unneeded";
private static final String _MSG_USE_BUILD_STRING = "build.string.use";
private static final String _MSG_USE_BUILDER = "builder.use";
private static final String _MSG_USE_BUILDER_INSTEAD =
"builder.use.instead";
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy