All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.source.formatter.checkstyle.checks.ChainingCheck Maven / Gradle / Ivy

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.source.formatter.checkstyle.checks;

import com.liferay.petra.string.CharPool;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.source.formatter.checks.util.JavaSourceUtil;
import com.liferay.source.formatter.checkstyle.util.DetailASTUtil;

import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Hugo Huijser
 */
public class ChainingCheck extends BaseCheck {

	@Override
	public int[] getDefaultTokens() {
		return new int[] {TokenTypes.CTOR_DEF, TokenTypes.METHOD_DEF};
	}

	public void setAllowedClassNames(String allowedClassNames) {
		_allowedClassNames = ArrayUtil.append(
			_allowedClassNames, StringUtil.split(allowedClassNames));
	}

	public void setAllowedMethodNames(String allowedMethodNames) {
		_allowedMethodNames = ArrayUtil.append(
			_allowedMethodNames, StringUtil.split(allowedMethodNames));
	}

	public void setAllowedVariableTypeNames(String allowedVariableTypeNames) {
		_allowedVariableTypeNames = ArrayUtil.append(
			_allowedVariableTypeNames,
			StringUtil.split(allowedVariableTypeNames));
	}

	@Override
	protected void doVisitToken(DetailAST detailAST) {
		List methodCallASTList = DetailASTUtil.getAllChildTokens(
			detailAST, true, TokenTypes.METHOD_CALL);

		for (DetailAST methodCallAST : methodCallASTList) {
			DetailAST dotAST = methodCallAST.findFirstToken(TokenTypes.DOT);

			if (dotAST != null) {
				List childMethodCallASTList =
					DetailASTUtil.getAllChildTokens(
						dotAST, false, TokenTypes.METHOD_CALL);

				// Only check the method that is first in the chain

				if (!childMethodCallASTList.isEmpty()) {
					continue;
				}
			}

			if (_isInsideAnonymousClassVariableDefinition(methodCallAST)) {
				continue;
			}

			List chain = _getChain(methodCallAST);

			int chainSize = chain.size();

			if (chainSize == 1) {
				continue;
			}

			if (chainSize == 2) {
				if (dotAST == null) {
					continue;
				}

				_checkMethodName(chain, "getClass", methodCallAST, detailAST);

				String name1 = chain.get(0);

				if ((name1.equals("getParamValue") ||
					 name1.equals("getValue")) &&
					DetailASTUtil.hasParentWithTokenType(
						detailAST, TokenTypes.ENUM_DEF)) {

					continue;
				}

				String name2 = chain.get(1);

				if (name1.equals("concat") || name2.equals("concat")) {
					continue;
				}

				FileContents fileContents = getFileContents();

				String fileName = StringUtil.replace(
					fileContents.getFileName(), CharPool.BACK_SLASH,
					CharPool.SLASH);

				if (fileName.contains("/test/") ||
					fileName.contains("/testIntegration/")) {

					continue;
				}
			}

			if (_isAllowedChainingMethodCall(detailAST, methodCallAST, chain)) {
				if (chainSize > 2) {
					_checkStyling(detailAST, methodCallAST);
				}

				continue;
			}

			int concatsCount = Collections.frequency(chain, "concat");

			if (concatsCount > 2) {
				log(methodCallAST.getLineNo(), _MSG_AVOID_TOO_MANY_CONCAT);

				continue;
			}

			if ((chainSize == 3) && (concatsCount == 2)) {
				continue;
			}

			log(
				methodCallAST.getLineNo(), _MSG_AVOID_CHAINING,
				DetailASTUtil.getMethodName(methodCallAST));
		}
	}

	private void _checkMethodName(
		List chainedMethodNames, String methodName,
		DetailAST methodCallAST, DetailAST detailAST) {

		String firstMethodName = chainedMethodNames.get(0);

		if (firstMethodName.equals(methodName) &&
			!_isInsideConstructorThisCall(methodCallAST, detailAST) &&
			!DetailASTUtil.hasParentWithTokenType(
				methodCallAST, TokenTypes.SUPER_CTOR_CALL)) {

			log(methodCallAST.getLineNo(), _MSG_AVOID_CHAINING, methodName);
		}
	}

	private void _checkStyling(DetailAST detailAST, DetailAST methodCallAST) {
		if (_isInsideConstructorThisCall(methodCallAST, detailAST) ||
			DetailASTUtil.hasParentWithTokenType(
				methodCallAST, TokenTypes.SUPER_CTOR_CALL)) {

			return;
		}

		for (int i = DetailASTUtil.getStartLine(methodCallAST) + 1;
			 i <= DetailASTUtil.getEndLine(methodCallAST); i++) {

			String line = StringUtil.trim(getLine(i - 1));

			if (line.startsWith(").")) {
				return;
			}
		}

		log(
			methodCallAST.getLineNo(), _MSG_INCORRECT_STYLING,
			DetailASTUtil.getMethodName(methodCallAST));
	}

	private List _getChain(DetailAST methodCallAST) {
		List chain = new ArrayList<>();

		chain.add(DetailASTUtil.getMethodName(methodCallAST));

		while (true) {
			DetailAST parentAST = methodCallAST.getParent();

			if (parentAST.getType() != TokenTypes.DOT) {
				return chain;
			}

			DetailAST grandParentAST = parentAST.getParent();

			if (grandParentAST.getType() != TokenTypes.METHOD_CALL) {
				DetailAST siblingAST = methodCallAST.getNextSibling();

				if (siblingAST.getType() == TokenTypes.IDENT) {
					chain.add(siblingAST.getText());
				}

				return chain;
			}

			methodCallAST = grandParentAST;

			chain.add(DetailASTUtil.getMethodName(methodCallAST));
		}
	}

	private DetailAST _getOuterMethodCallAST(DetailAST detailAST) {
		while (true) {
			if ((detailAST.getType() != TokenTypes.DOT) &&
				(detailAST.getType() != TokenTypes.METHOD_CALL)) {

				return null;
			}

			DetailAST parentAST = detailAST.getParent();

			if ((detailAST.getType() == TokenTypes.METHOD_CALL) &&
				(parentAST.getType() != TokenTypes.DOT)) {

				break;
			}

			detailAST = parentAST;
		}

		while (true) {
			DetailAST parentAST = detailAST.getParent();

			if (parentAST == null) {
				return null;
			}

			if (parentAST.getType() == TokenTypes.METHOD_CALL) {
				detailAST = parentAST;

				break;
			}

			detailAST = parentAST;
		}

		while (true) {
			DetailAST childAST = detailAST.getFirstChild();

			if ((detailAST.getType() != TokenTypes.DOT) &&
				(detailAST.getType() != TokenTypes.METHOD_CALL)) {

				return null;
			}

			if ((detailAST.getType() == TokenTypes.DOT) &&
				(childAST.getType() != TokenTypes.METHOD_CALL)) {

				return detailAST.getParent();
			}

			detailAST = childAST;
		}
	}

	private boolean _isAllowedChainingMethodCall(
		DetailAST detailAST, DetailAST methodCallAST,
		List chainedMethodNames) {

		if (_isInsideConstructorThisCall(methodCallAST, detailAST) ||
			DetailASTUtil.hasParentWithTokenType(
				methodCallAST, TokenTypes.SUPER_CTOR_CALL)) {

			return true;
		}

		for (String allowedMethodName : _allowedMethodNames) {
			if (chainedMethodNames.contains(allowedMethodName)) {
				return true;
			}
		}

		DetailAST dotAST = methodCallAST.findFirstToken(TokenTypes.DOT);

		if (dotAST == null) {
			FileContents fileContents = getFileContents();

			String fileName = StringUtil.replace(
				fileContents.getFileName(), CharPool.BACK_SLASH,
				CharPool.SLASH);

			String className = JavaSourceUtil.getClassName(fileName);

			for (String allowedClassName : _allowedClassNames) {
				if (className.matches(allowedClassName)) {
					return true;
				}
			}

			FileText fileText = fileContents.getText();

			String content = (String)fileText.getFullText();

			if (content.contains("extends PowerMockito")) {
				return true;
			}

			return false;
		}

		DetailAST nameAST = null;

		DetailAST firstChild = dotAST.getFirstChild();

		if (firstChild.getType() == TokenTypes.LITERAL_NEW) {
			nameAST = firstChild.findFirstToken(TokenTypes.IDENT);
		}
		else {
			nameAST = dotAST.findFirstToken(TokenTypes.IDENT);
		}

		if (nameAST != null) {
			String classOrVariableName = nameAST.getText();

			if (_isLambdaVariable(methodCallAST, classOrVariableName)) {
				return true;
			}

			for (String allowedClassName : _allowedClassNames) {
				if (classOrVariableName.matches(allowedClassName)) {
					return true;
				}
			}

			String variableTypeName = DetailASTUtil.getVariableTypeName(
				methodCallAST, classOrVariableName, false);

			if (Validator.isNotNull(variableTypeName)) {
				for (String allowedVariableTypeName :
						_allowedVariableTypeNames) {

					if (variableTypeName.matches(allowedVariableTypeName)) {
						return true;
					}
				}
			}
		}

		DetailAST outerMethodCallAST = _getOuterMethodCallAST(methodCallAST);

		if (outerMethodCallAST != null) {
			return _isAllowedChainingMethodCall(
				detailAST, outerMethodCallAST, _getChain(outerMethodCallAST));
		}

		return false;
	}

	private boolean _isInsideAnonymousClassVariableDefinition(
		DetailAST detailAST) {

		DetailAST parentAST = detailAST.getParent();

		while (parentAST != null) {
			if ((parentAST.getType() == TokenTypes.CTOR_DEF) ||
				(parentAST.getType() == TokenTypes.METHOD_DEF)) {

				return false;
			}

			if (parentAST.getType() == TokenTypes.VARIABLE_DEF) {
				parentAST = parentAST.getParent();

				if (parentAST.getType() == TokenTypes.OBJBLOCK) {
					return true;
				}

				return false;
			}

			parentAST = parentAST.getParent();
		}

		return false;
	}

	private boolean _isInsideConstructorThisCall(
		DetailAST methodCallAST, DetailAST detailAST) {

		if (detailAST.getType() != TokenTypes.CTOR_DEF) {
			return false;
		}

		DetailAST parentAST = methodCallAST.getParent();

		while (parentAST != null) {
			String parentASTText = parentAST.getText();

			if ((parentAST.getType() == TokenTypes.CTOR_CALL) &&
				parentASTText.equals("this")) {

				return true;
			}

			parentAST = parentAST.getParent();
		}

		return false;
	}

	private boolean _isLambdaVariable(
		DetailAST methodCallAST, String variableName) {

		DetailAST parentAST = methodCallAST.getParent();

		while (parentAST != null) {
			if (parentAST.getType() != TokenTypes.LAMBDA) {
				parentAST = parentAST.getParent();

				continue;
			}

			DetailAST nameAST = parentAST.findFirstToken(TokenTypes.IDENT);

			if ((nameAST != null) && variableName.equals(nameAST.getText())) {
				return true;
			}

			return false;
		}

		return false;
	}

	private static final String _MSG_AVOID_CHAINING = "chaining.avoid";

	private static final String _MSG_AVOID_TOO_MANY_CONCAT =
		"concat.avoid.too.many";

	private static final String _MSG_INCORRECT_STYLING = "styling.incorrect";

	private String[] _allowedClassNames = new String[0];
	private String[] _allowedMethodNames = new String[0];
	private String[] _allowedVariableTypeNames = new String[0];

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy