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

com.liferay.source.formatter.checkstyle.check.MissingEmptyLineCheck Maven / Gradle / Ivy

The newest version!
/**
 * 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.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.util.Validator;

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

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

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

	@Override
	public int[] getDefaultTokens() {
		return new int[] {
			TokenTypes.ASSIGN, TokenTypes.INSTANCE_INIT, TokenTypes.METHOD_CALL,
			TokenTypes.VARIABLE_DEF
		};
	}

	@Override
	protected void doVisitToken(DetailAST detailAST) {
		if (detailAST.getType() == TokenTypes.INSTANCE_INIT) {
			_checkMissingEmptyLineInInstanceInit(detailAST);

			return;
		}

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

			return;
		}

		if (detailAST.getType() == TokenTypes.VARIABLE_DEF) {
			DetailAST parentDetailAST = detailAST.getParent();

			if (parentDetailAST.getType() == TokenTypes.SLIST) {
				_checkMissingEmptyLineAfterVariableDef(
					detailAST, "ThemeDisplay");
				_checkMissingEmptyLineBeforeVariableDef(detailAST);
			}

			return;
		}

		_checkMissingEmptyLineBeforeAssign(detailAST);

		DetailAST firstChildDetailAST = detailAST.getFirstChild();

		if ((firstChildDetailAST == null) ||
			(firstChildDetailAST.getType() == TokenTypes.DOT)) {

			return;
		}

		String variableName = _getVariableName(detailAST);

		if (variableName == null) {
			return;
		}

		DetailAST parentDetailAST = detailAST.getParent();

		_checkMissingEmptyLineAfterReferencingVariable(
			parentDetailAST, variableName, detailAST,
			getEndLineNumber(detailAST));
		_checkMissingEmptyLineBetweenAssigningAndUsingVariable(
			parentDetailAST, variableName, getEndLineNumber(detailAST));
	}

	private void _checkMissingEmptyLineAfterMethodCall(
		DetailAST detailAST, String variableName,
		DetailAST nextSiblingDetailAST) {

		if (nextSiblingDetailAST == null) {
			return;
		}

		int endLineNumber = getEndLineNumber(detailAST);

		int nextExpressionStartLineNumber = getStartLineNumber(
			nextSiblingDetailAST);

		if ((endLineNumber + 1) != nextExpressionStartLineNumber) {
			return;
		}

		if (nextSiblingDetailAST.getType() == TokenTypes.EXPR) {
			List enforceEmptyLineAfterMethodNames = getAttributeValues(
				_ENFORCE_EMPTY_LINE_AFTER_METHOD_NAMES);

			String methodName = getMethodName(detailAST);

			if (enforceEmptyLineAfterMethodNames.contains(
					StringBundler.concat(
						getVariableTypeName(detailAST, variableName, false),
						StringPool.PERIOD, methodName))) {

				log(
					endLineNumber, _MSG_MISSING_EMPTY_LINE_AFTER_METHOD_NAME,
					StringBundler.concat(
						variableName, StringPool.PERIOD, methodName));
			}

			DetailAST firstChildDetailAST =
				nextSiblingDetailAST.getFirstChild();

			if ((firstChildDetailAST.getType() == TokenTypes.METHOD_CALL) &&
				variableName.equals(getVariableName(firstChildDetailAST))) {

				return;
			}
		}

		if (containsVariableName(nextSiblingDetailAST, variableName, null)) {
			log(
				endLineNumber, _MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "after",
				endLineNumber);
		}
	}

	private void _checkMissingEmptyLineAfterReferencingVariable(
		DetailAST detailAST, String variableName, DetailAST assignDetailAST,
		int endLineNumber) {

		String lastAssignedVariableName = null;
		DetailAST previousDetailAST = null;
		boolean referenced = false;

		DetailAST nextSiblingDetailAST = detailAST.getNextSibling();

		while (true) {
			if ((nextSiblingDetailAST == null) ||
				(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {

				return;
			}

			nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();

			if ((nextSiblingDetailAST == null) ||
				hasPrecedingPlaceholder(nextSiblingDetailAST) ||
				((nextSiblingDetailAST.getType() != TokenTypes.EXPR) &&
				 (nextSiblingDetailAST.getType() != TokenTypes.VARIABLE_DEF))) {

				return;
			}

			if (!containsVariableName(
					nextSiblingDetailAST, variableName, assignDetailAST)) {

				if (!referenced) {
					return;
				}

				int nextExpressionStartLineNumber = getStartLineNumber(
					nextSiblingDetailAST);

				if ((endLineNumber + 1) != nextExpressionStartLineNumber) {
					return;
				}

				if (!containsVariableName(
						previousDetailAST, lastAssignedVariableName,
						assignDetailAST) ||
					!containsVariableName(
						nextSiblingDetailAST, lastAssignedVariableName,
						assignDetailAST)) {

					log(
						nextExpressionStartLineNumber,
						_MSG_MISSING_EMPTY_LINE_AFTER_VARIABLE_REFERENCE,
						nextExpressionStartLineNumber, variableName);
				}

				return;
			}

			List assignDetailASTList = getAllChildTokens(
				nextSiblingDetailAST, false, TokenTypes.ASSIGN);

			if (assignDetailASTList.size() == 1) {
				lastAssignedVariableName = _getVariableName(
					assignDetailASTList.get(0));
			}

			referenced = true;

			endLineNumber = getEndLineNumber(nextSiblingDetailAST);

			previousDetailAST = nextSiblingDetailAST;

			nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();
		}
	}

	private void _checkMissingEmptyLineAfterVariableDef(
		DetailAST detailAST, String variableTypeName) {

		if (detailAST.findFirstToken(TokenTypes.ASSIGN) == null) {
			return;
		}

		int endLineNumber = getEndLineNumber(detailAST);

		String nextLine = getLine(endLineNumber);

		if (Validator.isNull(nextLine)) {
			return;
		}

		DetailAST nextSiblingDetailAST = detailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(getHiddenBefore(nextSiblingDetailAST) != null) ||
			(nextSiblingDetailAST.getType() == TokenTypes.RCURLY)) {

			return;
		}

		if (variableTypeName.equals(
				getVariableTypeName(detailAST, getName(detailAST), false))) {

			log(
				detailAST, _MSG_MISSING_EMPTY_LINE_AFTER_VARIABLE_DEFINITION,
				variableTypeName);

			return;
		}

		if (nextSiblingDetailAST.getType() == TokenTypes.VARIABLE_DEF) {
			return;
		}

		if (nextSiblingDetailAST.getType() == TokenTypes.EXPR) {
			DetailAST firstChildDetailAST =
				nextSiblingDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.ASSIGN) {
				return;
			}
		}

		log(
			endLineNumber, _MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "after",
			endLineNumber);
	}

	private void _checkMissingEmptyLineBeforeAssign(DetailAST assignDetailAST) {
		DetailAST parentDetailAST = assignDetailAST.getParent();

		DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.SEMI) ||
			_hasPrecedingVariableDef(parentDetailAST)) {

			return;
		}

		List adjacentAssignDetailASTList =
			_getAdjacentAssignDetailASTList(assignDetailAST);

		if (adjacentAssignDetailASTList.size() <= 1) {
			return;
		}

		DetailAST lastAssignDetailAST = adjacentAssignDetailASTList.get(
			adjacentAssignDetailASTList.size() - 1);

		List identDetailASTList =
			_getFollowingStatementsIdentDetailASTList(
				lastAssignDetailAST.getParent());

		if (_containsVariableName(identDetailASTList, assignDetailAST)) {
			return;
		}

		DetailAST firstReferencedAssignDetailAST = null;

		for (int i = 1; i < adjacentAssignDetailASTList.size(); i++) {
			DetailAST curAssignDetailAST = adjacentAssignDetailASTList.get(i);

			if (_containsVariableName(identDetailASTList, curAssignDetailAST)) {
				if (firstReferencedAssignDetailAST == null) {
					firstReferencedAssignDetailAST = curAssignDetailAST;
				}

				continue;
			}

			if (firstReferencedAssignDetailAST != null) {
				return;
			}
		}

		if (firstReferencedAssignDetailAST != null) {
			String name = getName(firstReferencedAssignDetailAST);

			if (name == null) {
				name = getName(firstReferencedAssignDetailAST.getParent());
			}

			log(
				firstReferencedAssignDetailAST,
				_MSG_MISSING_EMPTY_LINE_BEFORE_VARIABLE_ASSIGN, name);
		}
	}

	private void _checkMissingEmptyLineBeforeMethodCall(
		DetailAST detailAST, String variableName,
		DetailAST previousSiblingDetailAST) {

		if (previousSiblingDetailAST == null) {
			return;
		}

		int startLineNumber = getStartLineNumber(detailAST);

		int previousExpressionEndLineNumber = getEndLineNumber(
			previousSiblingDetailAST);

		if ((previousExpressionEndLineNumber + 1) != startLineNumber) {
			return;
		}

		if (previousSiblingDetailAST.getType() == TokenTypes.EXPR) {
			DetailAST firstChildDetailAST =
				previousSiblingDetailAST.getFirstChild();

			if ((firstChildDetailAST.getType() == TokenTypes.METHOD_CALL) &&
				variableName.equals(getVariableName(firstChildDetailAST))) {

				List enforceEmptyLineBeforeMethodNames =
					getAttributeValues(_ENFORCE_EMPTY_LINE_BEFORE_METHOD_NAMES);

				String methodName = getMethodName(detailAST);

				if (enforceEmptyLineBeforeMethodNames.contains(methodName) &&
					Validator.isNull(getParameterDetailAST(detailAST))) {

					log(
						startLineNumber,
						_MSG_MISSING_EMPTY_LINE_BEFORE_METHOD_NAME,
						StringBundler.concat(
							variableName, StringPool.PERIOD, methodName));
				}

				return;
			}
		}

		if (containsVariableName(
				previousSiblingDetailAST, variableName, null)) {

			log(
				startLineNumber, _MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "before",
				startLineNumber);
		}
	}

	private void _checkMissingEmptyLineBeforeVariableDef(DetailAST detailAST) {
		if (getHiddenBefore(detailAST) != null) {
			return;
		}

		DetailAST parentDetailAST = detailAST.getParent();

		if (parentDetailAST.getType() != TokenTypes.SLIST) {
			return;
		}

		int startLineNumber = getStartLineNumber(detailAST);

		String previousLine = getLine(startLineNumber - 2);

		if (Validator.isNull(previousLine)) {
			return;
		}

		DetailAST previousSiblingDetailAST = detailAST.getPreviousSibling();

		if ((previousSiblingDetailAST == null) ||
			(previousSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		previousSiblingDetailAST =
			previousSiblingDetailAST.getPreviousSibling();

		if (previousSiblingDetailAST.getType() == TokenTypes.VARIABLE_DEF) {
			return;
		}

		if (previousSiblingDetailAST.getType() == TokenTypes.EXPR) {
			DetailAST firstChildDetailAST =
				previousSiblingDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.ASSIGN) {
				return;
			}
		}

		log(
			startLineNumber, _MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "before",
			startLineNumber);
	}

	private void _checkMissingEmptyLineBetweenAssigningAndUsingVariable(
		DetailAST detailAST, String name, int endLineNumber) {

		DetailAST nextSiblingDetailAST = detailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();

		if (nextSiblingDetailAST == null) {
			return;
		}

		int nextExpressionStartLineNumber = getStartLineNumber(
			nextSiblingDetailAST);

		if ((endLineNumber + 1) != nextExpressionStartLineNumber) {
			return;
		}

		List identDetailASTList = getAllChildTokens(
			nextSiblingDetailAST, true, TokenTypes.IDENT);

		boolean nextVariableUsesVariable = false;

		for (DetailAST identDetailAST : identDetailASTList) {
			String identName = identDetailAST.getText();

			if (!identName.equals(name)) {
				continue;
			}

			DetailAST parentDetailAST = identDetailAST.getParent();

			if ((parentDetailAST.getType() == TokenTypes.ASSIGN) &&
				(identDetailAST.getPreviousSibling() == null)) {

				return;
			}

			nextVariableUsesVariable = true;
		}

		if (nextVariableUsesVariable) {
			log(
				nextExpressionStartLineNumber,
				_MSG_MISSING_EMPTY_LINE_BEFORE_VARIABLE_USE, name);
		}
	}

	private void _checkMissingEmptyLineInInstanceInit(DetailAST detailAST) {
		DetailAST firstChildDetailAST = detailAST.getFirstChild();

		if ((firstChildDetailAST == null) ||
			(firstChildDetailAST.getType() != TokenTypes.SLIST)) {

			return;
		}

		List exprDetailASTList = getAllChildTokens(
			firstChildDetailAST, false, TokenTypes.EXPR);

		if (exprDetailASTList.size() < 2) {
			return;
		}

		DetailAST previousExprDetailAST = null;

		for (DetailAST exprDetailAST : exprDetailASTList) {
			if (previousExprDetailAST == null) {
				previousExprDetailAST = exprDetailAST;

				continue;
			}

			firstChildDetailAST = exprDetailAST.getFirstChild();
			DetailAST previousExprFirstChildDetailAST =
				previousExprDetailAST.getFirstChild();

			if ((firstChildDetailAST.getType() != TokenTypes.METHOD_CALL) ||
				(previousExprFirstChildDetailAST.getType() !=
					TokenTypes.ASSIGN)) {

				previousExprDetailAST = exprDetailAST;

				continue;
			}

			int previousExprEndLineNo = getEndLineNumber(previousExprDetailAST);

			if ((previousExprEndLineNo + 1) == exprDetailAST.getLineNo()) {
				log(
					previousExprFirstChildDetailAST,
					_MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "after",
					previousExprEndLineNo);
			}

			previousExprDetailAST = exprDetailAST;
		}
	}

	private void _checkMissingEmptyLinesAfterChaining(DetailAST detailAST) {
		DetailAST dotDetailAST = detailAST.findFirstToken(TokenTypes.DOT);

		if (dotDetailAST != null) {
			List childMethodCallDetailASTList = getAllChildTokens(
				dotDetailAST, false, TokenTypes.METHOD_CALL);

			if (!childMethodCallDetailASTList.isEmpty()) {
				return;
			}
		}

		ChainInformation chainInformation = getChainInformation(detailAST);

		List chainedMethodNames = chainInformation.getMethodNames();

		int chainSize = chainedMethodNames.size();

		if (chainSize == 1) {
			return;
		}

		DetailAST topLevelMethodCallDetailAST = getTopLevelMethodCallDetailAST(
			detailAST);

		DetailAST parentDetailAST = topLevelMethodCallDetailAST.getParent();

		if (parentDetailAST.getType() != TokenTypes.EXPR) {
			return;
		}

		DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.EXPR)) {

			return;
		}

		DetailAST nextMethodCallDetailAST =
			nextSiblingDetailAST.getFirstChild();

		if (nextMethodCallDetailAST.getType() != TokenTypes.METHOD_CALL) {
			return;
		}

		int endLineNumber = getEndLineNumber(detailAST);

		int nextExpressionStartLineNumber = getStartLineNumber(
			nextSiblingDetailAST);

		if ((endLineNumber + 1) != nextExpressionStartLineNumber) {
			return;
		}

		log(
			endLineNumber, _MSG_MISSING_EMPTY_LINE_LINE_NUMBER, "after",
			endLineNumber);
	}

	private void _checkMissingEmptyLinesAroundMethodCall(DetailAST detailAST) {
		_checkMissingEmptyLinesAfterChaining(detailAST);

		String variableName = getVariableName(detailAST);

		if ((variableName == null) ||
			Character.isUpperCase(variableName.charAt(0))) {

			return;
		}

		DetailAST parentDetailAST = detailAST.getParent();

		if (parentDetailAST.getType() != TokenTypes.EXPR) {
			return;
		}

		DetailAST nextSiblingDetailAST = parentDetailAST.getNextSibling();

		if ((nextSiblingDetailAST == null) ||
			(nextSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		_checkMissingEmptyLineAfterMethodCall(
			detailAST, variableName, nextSiblingDetailAST.getNextSibling());

		DetailAST previousSiblingDetailAST =
			parentDetailAST.getPreviousSibling();

		if ((previousSiblingDetailAST == null) ||
			(previousSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return;
		}

		_checkMissingEmptyLineBeforeMethodCall(
			detailAST, variableName,
			previousSiblingDetailAST.getPreviousSibling());
	}

	private boolean _containsVariableName(
		List identDetailASTList, DetailAST assignDetailAST) {

		String variableName = _getVariableName(assignDetailAST);

		if (variableName == null) {
			return false;
		}

		return containsVariableName(
			identDetailASTList, variableName, assignDetailAST);
	}

	private List _getAdjacentAssignDetailASTList(
		DetailAST assignDetailAST) {

		List assignDetailASTList = new ArrayList<>();

		assignDetailASTList.add(assignDetailAST);

		DetailAST followingStatementDetailAST = _getFollowingStatementDetailAST(
			assignDetailAST.getParent(), false);

		while (true) {
			if (followingStatementDetailAST == null) {
				return assignDetailASTList;
			}

			DetailAST followingAssignDetailAST =
				followingStatementDetailAST.findFirstToken(TokenTypes.ASSIGN);

			if (followingAssignDetailAST == null) {
				return assignDetailASTList;
			}

			assignDetailASTList.add(followingAssignDetailAST);

			followingStatementDetailAST = _getFollowingStatementDetailAST(
				followingStatementDetailAST, false);
		}
	}

	private DetailAST _getFollowingStatementDetailAST(
		DetailAST detailAST, boolean allowDividingEmptyLine) {

		int endLineNumber = getEndLineNumber(detailAST);

		DetailAST nextSiblingDetailAST = detailAST.getNextSibling();

		while (true) {
			if (nextSiblingDetailAST == null) {
				return null;
			}

			int nextStartLineNumber = getStartLineNumber(nextSiblingDetailAST);

			if (nextStartLineNumber <= endLineNumber) {
				nextSiblingDetailAST = nextSiblingDetailAST.getNextSibling();

				continue;
			}

			if (nextStartLineNumber == (endLineNumber + 1)) {
				return nextSiblingDetailAST;
			}

			if (hasPrecedingPlaceholder(nextSiblingDetailAST)) {
				return null;
			}

			if (allowDividingEmptyLine) {
				return nextSiblingDetailAST;
			}

			return null;
		}
	}

	private List _getFollowingStatementsIdentDetailASTList(
		DetailAST detailAST) {

		List identDetailASTList = new ArrayList<>();

		DetailAST followingStatementDetailAST = _getFollowingStatementDetailAST(
			detailAST, true);

		while (followingStatementDetailAST != null) {
			identDetailASTList.addAll(
				getAllChildTokens(
					followingStatementDetailAST, true, TokenTypes.IDENT));

			followingStatementDetailAST = _getFollowingStatementDetailAST(
				followingStatementDetailAST, false);
		}

		return identDetailASTList;
	}

	private String _getVariableName(DetailAST assignDetailAST) {
		DetailAST parentDetailAST = assignDetailAST.getParent();

		if (parentDetailAST.getType() == TokenTypes.EXPR) {
			DetailAST firstChildDetailAST = assignDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
				return firstChildDetailAST.getText();
			}
		}
		else if (parentDetailAST.getType() == TokenTypes.VARIABLE_DEF) {
			DetailAST nameDetailAST = parentDetailAST.findFirstToken(
				TokenTypes.IDENT);

			return nameDetailAST.getText();
		}

		return null;
	}

	private boolean _hasPrecedingVariableDef(
		DetailAST variableDefinitionDetailAST) {

		DetailAST previousSiblingDetailAST =
			variableDefinitionDetailAST.getPreviousSibling();

		if ((previousSiblingDetailAST == null) ||
			(previousSiblingDetailAST.getType() != TokenTypes.SEMI)) {

			return false;
		}

		previousSiblingDetailAST =
			previousSiblingDetailAST.getPreviousSibling();

		if (previousSiblingDetailAST.getType() != TokenTypes.VARIABLE_DEF) {
			return false;
		}

		if ((getEndLineNumber(previousSiblingDetailAST) + 1) ==
				getStartLineNumber(variableDefinitionDetailAST)) {

			return true;
		}

		return false;
	}

	private static final String _ENFORCE_EMPTY_LINE_AFTER_METHOD_NAMES =
		"enforceEmptyLineAfterMethodNames";

	private static final String _ENFORCE_EMPTY_LINE_BEFORE_METHOD_NAMES =
		"enforceEmptyLineBeforeMethodNames";

	private static final String _MSG_MISSING_EMPTY_LINE_AFTER_METHOD_NAME =
		"empty.line.missing.after.method.name";

	private static final String
		_MSG_MISSING_EMPTY_LINE_AFTER_VARIABLE_DEFINITION =
			"empty.line.missing.after.variable.definition";

	private static final String
		_MSG_MISSING_EMPTY_LINE_AFTER_VARIABLE_REFERENCE =
			"empty.line.missing.after.variable.reference";

	private static final String _MSG_MISSING_EMPTY_LINE_BEFORE_METHOD_NAME =
		"empty.line.missing.before.method.name";

	private static final String _MSG_MISSING_EMPTY_LINE_BEFORE_VARIABLE_ASSIGN =
		"empty.line.missing.before.variable.assign";

	private static final String _MSG_MISSING_EMPTY_LINE_BEFORE_VARIABLE_USE =
		"empty.line.missing.before.variable.use";

	private static final String _MSG_MISSING_EMPTY_LINE_LINE_NUMBER =
		"empty.line.missing.line.number";

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy