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

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

There is a newer version: 1.0.1457
Show 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.portal.json.JSONArrayImpl;
import com.liferay.portal.json.JSONObjectImpl;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.json.JSONUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.HashMapBuilder;
import com.liferay.portal.kernel.util.Tuple;
import com.liferay.source.formatter.util.FileUtil;

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

import java.io.File;
import java.io.IOException;

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 class TryWithResourcesCheck extends BaseCheck {

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

	@Override
	protected void doVisitToken(DetailAST detailAST) {
		DetailAST literalFinallyDetailAST = detailAST.findFirstToken(
			TokenTypes.LITERAL_FINALLY);

		if (literalFinallyDetailAST != null) {
			_checkFinallyStatement(detailAST, literalFinallyDetailAST);
		}

		if (!isAttributeValue(_POPULATE_TYPE_NAMES_KEY)) {
			return;
		}

		DetailAST firstChildDetailAST = detailAST.getFirstChild();

		if (firstChildDetailAST.getType() ==
				TokenTypes.RESOURCE_SPECIFICATION) {

			_populateCloseableTypeNames(firstChildDetailAST);
		}
	}

	private void _checkFinallyStatement(
		DetailAST literalTryDetailAST, DetailAST literalFinallyDetailAST) {

		Map cleanUpVariableNamesMap = new HashMap<>();
		Map closeVariableNamesMap = new HashMap<>();

		DetailAST slistDetailAST = literalFinallyDetailAST.findFirstToken(
			TokenTypes.SLIST);

		List methodCallDetailASTList = getAllChildTokens(
			slistDetailAST, true, TokenTypes.METHOD_CALL);

		for (DetailAST methodCallDetailAST : methodCallDetailASTList) {
			List cleanUpVariableNames = _getCleanUpVariableNames(
				methodCallDetailAST);

			for (String cleanUpVariableName : cleanUpVariableNames) {
				DetailAST typeDetailAST = getVariableTypeDetailAST(
					literalTryDetailAST, cleanUpVariableName, false);

				if (!_useTryWithResources(
						cleanUpVariableName, typeDetailAST,
						literalTryDetailAST)) {

					return;
				}

				cleanUpVariableNamesMap.put(
					methodCallDetailAST.getLineNo(), cleanUpVariableName);
			}

			String closeVariableName = _getCloseVariableName(
				methodCallDetailAST, literalFinallyDetailAST);

			if (closeVariableName == null) {
				continue;
			}

			DetailAST typeDetailAST = getVariableTypeDetailAST(
				literalTryDetailAST, closeVariableName, false);

			if (!_useTryWithResources(
					closeVariableName, typeDetailAST, literalTryDetailAST)) {

				return;
			}

			List closeableTypeNames = _getCloseableTypeNames();

			if (!closeableTypeNames.contains(
					getFullyQualifiedTypeName(typeDetailAST, true))) {

				return;
			}

			closeVariableNamesMap.put(
				methodCallDetailAST.getLineNo(), closeVariableName);
		}

		for (Map.Entry entry :
				cleanUpVariableNamesMap.entrySet()) {

			log(
				entry.getKey(), _MSG_USE_TRY_WITH_RESOURCES, entry.getValue(),
				"DataAccess.cleanUp");
		}

		for (Map.Entry entry :
				closeVariableNamesMap.entrySet()) {

			String closeVariableName = entry.getValue();

			log(
				entry.getKey(), _MSG_USE_TRY_WITH_RESOURCES, closeVariableName,
				closeVariableName + ".close");
		}
	}

	private List _getCleanUpVariableNames(
		DetailAST methodCallDetailAST) {

		List variableNames = new ArrayList<>();

		DetailAST dotDetailAST = methodCallDetailAST.getFirstChild();

		if (dotDetailAST.getType() != TokenTypes.DOT) {
			return variableNames;
		}

		DetailAST lastChildDetailAST = dotDetailAST.getLastChild();

		if (!Objects.equals(lastChildDetailAST.getText(), "cleanUp")) {
			return variableNames;
		}

		DetailAST firstChildDetailAST = dotDetailAST.getFirstChild();

		if (!Objects.equals(firstChildDetailAST.getText(), "DataAccess")) {
			return variableNames;
		}

		DetailAST elistDetailAST = methodCallDetailAST.findFirstToken(
			TokenTypes.ELIST);

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

		for (DetailAST exprDetailAST : exprDetailASTList) {
			firstChildDetailAST = exprDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
				variableNames.add(firstChildDetailAST.getText());
			}
		}

		return variableNames;
	}

	private List _getCloseableTypeNames() {
		Tuple closeableTypeNamesTuple = _getCloseableTypeNamesTuple();

		JSONObject jsonObject = (JSONObject)closeableTypeNamesTuple.getObject(
			0);

		JSONArray jsonArray = (JSONArray)jsonObject.get(
			_CLOSEABLE_TYPE_NAMES_CATEGORY);

		return JSONUtil.toStringList(jsonArray, "name");
	}

	private synchronized Tuple _getCloseableTypeNamesTuple() {
		if (_closeableTypeNamesTuple != null) {
			return _closeableTypeNamesTuple;
		}

		_closeableTypeNamesTuple = getTypeNamesTuple(
			_CLOSEABLE_TYPE_NAMES_FILE_NAME, _CLOSEABLE_TYPE_NAMES_CATEGORY);

		return _closeableTypeNamesTuple;
	}

	private String _getCloseVariableName(
		DetailAST methodCallDetailAST, DetailAST literalFinallyDetailAST) {

		DetailAST dotDetailAST = methodCallDetailAST.getFirstChild();

		if (dotDetailAST.getType() != TokenTypes.DOT) {
			return null;
		}

		DetailAST lastChildDetailAST = dotDetailAST.getLastChild();

		if (!Objects.equals(lastChildDetailAST.getText(), "close")) {
			return null;
		}

		DetailAST firstChildDetailAST = dotDetailAST.getFirstChild();

		if (firstChildDetailAST.getType() != TokenTypes.IDENT) {
			return null;
		}

		DetailAST parentDetailAST = methodCallDetailAST.getParent();

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

		parentDetailAST = parentDetailAST.getParent();

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

		String variableName = firstChildDetailAST.getText();

		parentDetailAST = parentDetailAST.getParent();

		if (equals(parentDetailAST, literalFinallyDetailAST)) {
			return variableName;
		}

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

		DetailAST exprDetailAST = parentDetailAST.findFirstToken(
			TokenTypes.EXPR);

		firstChildDetailAST = exprDetailAST.getFirstChild();

		if (firstChildDetailAST.getType() != TokenTypes.NOT_EQUAL) {
			return null;
		}

		lastChildDetailAST = firstChildDetailAST.getLastChild();

		if (lastChildDetailAST.getType() != TokenTypes.LITERAL_NULL) {
			return null;
		}

		firstChildDetailAST = firstChildDetailAST.getFirstChild();

		if (!variableName.equals(firstChildDetailAST.getText())) {
			return null;
		}

		parentDetailAST = parentDetailAST.getParent();

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

		parentDetailAST = parentDetailAST.getParent();

		if (equals(parentDetailAST, literalFinallyDetailAST)) {
			return variableName;
		}

		return null;
	}

	private void _populateCloseableTypeNames(
		DetailAST resourceSpecificationDetailAST) {

		Tuple closeableTypeNamesTuple = _getCloseableTypeNamesTuple();

		File closeableTypeNamesFile = (File)closeableTypeNamesTuple.getObject(
			1);

		if (closeableTypeNamesFile == null) {
			return;
		}

		DetailAST resourcesDetailAST =
			resourceSpecificationDetailAST.findFirstToken(TokenTypes.RESOURCES);

		if (resourcesDetailAST == null) {
			return;
		}

		List closeableTypeNames = _getCloseableTypeNames();

		List resourceDetailASTList = getAllChildTokens(
			resourcesDetailAST, false, TokenTypes.RESOURCE);

		for (DetailAST resourceDetailAST : resourceDetailASTList) {
			DetailAST typeDetailAST = resourceDetailAST.findFirstToken(
				TokenTypes.TYPE);

			if (typeDetailAST == null) {
				continue;
			}

			String typeName = getFullyQualifiedTypeName(typeDetailAST, false);

			if ((typeName == null) || closeableTypeNames.contains(typeName)) {
				continue;
			}

			closeableTypeNames.add(typeName);

			Collections.sort(closeableTypeNames);

			try {
				JSONObject jsonObject = new JSONObjectImpl();

				JSONArray jsonArray = new JSONArrayImpl();

				for (String closeableTypeName : closeableTypeNames) {
					jsonArray.put(
						new JSONObjectImpl(
							HashMapBuilder.put(
								"name", closeableTypeName
							).build()));
				}

				jsonObject.put(_CLOSEABLE_TYPE_NAMES_CATEGORY, jsonArray);

				FileUtil.write(
					closeableTypeNamesFile, JSONUtil.toString(jsonObject));

				System.out.println(
					StringBundler.concat(
						"Added '", typeName, "' to '",
						_CLOSEABLE_TYPE_NAMES_FILE_NAME, "'"));

				_closeableTypeNamesTuple = null;
			}
			catch (IOException ioException) {
				if (_log.isDebugEnabled()) {
					_log.debug(ioException);
				}
			}
		}
	}

	private boolean _useTryWithResources(
		String variableName, DetailAST typeDetailAST,
		DetailAST literalTryDetailAST) {

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

		DetailAST parentDetailAST = typeDetailAST.getParent();

		if ((parentDetailAST.getType() != TokenTypes.VARIABLE_DEF) ||
			hasParentWithTokenType(typeDetailAST, TokenTypes.FOR_EACH_CLAUSE)) {

			return false;
		}

		int endLineNumber = getEndLineNumber(literalTryDetailAST);

		int assignCount = 0;

		DetailAST assignDetailAST = parentDetailAST.findFirstToken(
			TokenTypes.ASSIGN);

		if (assignDetailAST != null) {
			DetailAST firstChildDetailAST = assignDetailAST.getFirstChild();

			firstChildDetailAST = firstChildDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() != TokenTypes.LITERAL_NULL) {
				assignCount++;
			}
		}

		List variableCallerDetailASTList =
			getVariableCallerDetailASTList(parentDetailAST, variableName);

		for (DetailAST variableCallerDetailAST : variableCallerDetailASTList) {
			if (hasParentWithTokenType(
					variableCallerDetailAST, TokenTypes.LAMBDA)) {

				return false;
			}

			parentDetailAST = variableCallerDetailAST.getParent();

			if (parentDetailAST.getType() == TokenTypes.ASSIGN) {
				if (assignCount > 0) {
					return false;
				}

				DetailAST literalIfDetailAST = getParentWithTokenType(
					variableCallerDetailAST, TokenTypes.LITERAL_IF);

				if ((literalIfDetailAST != null) &&
					(literalIfDetailAST.getLineNo() >
						typeDetailAST.getLineNo())) {

					return false;
				}

				assignCount++;
			}

			if (variableCallerDetailAST.getLineNo() > endLineNumber) {
				return false;
			}

			DetailAST callerLiteralTryDetailAST = getParentWithTokenType(
				variableCallerDetailAST, TokenTypes.LITERAL_TRY);

			if (callerLiteralTryDetailAST == null) {
				continue;
			}

			if ((callerLiteralTryDetailAST.getLineNo() <
					literalTryDetailAST.getLineNo()) ||
				((callerLiteralTryDetailAST.getLineNo() ==
					literalTryDetailAST.getLineNo()) &&
				 hasParentWithTokenType(
					 variableCallerDetailAST, TokenTypes.LITERAL_CATCH))) {

				return false;
			}
		}

		return true;
	}

	private static final String _CLOSEABLE_TYPE_NAMES_CATEGORY =
		"closeableTypeNames";

	private static final String _CLOSEABLE_TYPE_NAMES_FILE_NAME =
		"closeable-type-names.json";

	private static final String _MSG_USE_TRY_WITH_RESOURCES =
		"try.with.resources.use";

	private static final String _POPULATE_TYPE_NAMES_KEY = "populateTypeNames";

	private static final Log _log = LogFactoryUtil.getLog(
		TryWithResourcesCheck.class);

	private Tuple _closeableTypeNamesTuple;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy