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

com.liferay.source.formatter.checkstyle.check.BaseAPICheck 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.portal.json.JSONObjectImpl;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONObject;
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.check.util.JavaSourceUtil;
import com.liferay.source.formatter.util.PortalJSONObjectUtil;

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.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author Hugo Huijser
 */
public abstract class BaseAPICheck extends BaseCheck {

	protected Map> addTypeName(
		Map> typeNamesMap, String typeName,
		int lineNumber) {

		return _addTypeName(typeNamesMap, typeName, lineNumber, null);
	}

	protected List getConstructorCalls(
		DetailAST detailAST, List excludeImportNames,
		boolean skipDeprecated) {

		List constructorCalls = new ArrayList<>();

		List literalNewDetailASTList = getAllChildTokens(
			detailAST, true, TokenTypes.LITERAL_NEW);

		for (DetailAST literalNewDetailAST : literalNewDetailASTList) {
			if (skipDeprecated &&
				(hasDeprecatedParent(literalNewDetailAST) ||
				 hasSuppressDeprecationWarningsAnnotation(
					 literalNewDetailAST))) {

				continue;
			}

			DetailAST lparenDetailAST = literalNewDetailAST.findFirstToken(
				TokenTypes.LPAREN);

			if (lparenDetailAST == null) {
				continue;
			}

			String constructorTypeName = _getConstructorTypeName(
				literalNewDetailAST);

			if ((constructorTypeName != null) &&
				constructorTypeName.startsWith("com.liferay.") &&
				!excludeImportNames.contains(constructorTypeName)) {

				constructorCalls.add(
					new ConstructorCall(
						constructorTypeName,
						_getParameterTypeNames(literalNewDetailAST),
						literalNewDetailAST.getLineNo()));
			}
		}

		return constructorCalls;
	}

	protected List getConstructorJSONObjects(
		ConstructorCall constructorCall, JSONObject javaClassesJSONObject) {

		List constructorJSONObjects = new ArrayList<>();

		JSONObject classJSONObject = javaClassesJSONObject.getJSONObject(
			constructorCall.getTypeName());

		if (classJSONObject == null) {
			return constructorJSONObjects;
		}

		JSONArray constructorsJSONArray = classJSONObject.getJSONArray(
			"constructors");

		if (constructorsJSONArray == null) {
			return constructorJSONObjects;
		}

		List parameterTypeNames =
			constructorCall.getParameterTypeNames();

		Iterator iterator = constructorsJSONArray.iterator();

		outerLoop:
		while (iterator.hasNext()) {
			JSONObject constructorJSONObject = iterator.next();

			JSONArray parametersJSONArray = constructorJSONObject.getJSONArray(
				"parameters");

			if (parametersJSONArray == null) {
				if (parameterTypeNames.isEmpty()) {
					constructorJSONObjects.add(constructorJSONObject);
				}

				continue;
			}

			if (parametersJSONArray.length() != parameterTypeNames.size()) {
				continue;
			}

			for (int i = 0; i < parameterTypeNames.size(); i++) {
				String actualTypeName = parameterTypeNames.get(i);
				String methodTypeName = parametersJSONArray.getString(i);

				if (Validator.isNotNull(actualTypeName) &&
					!StringUtil.equalsIgnoreCase(
						actualTypeName, methodTypeName) &&
					!methodTypeName.equals("Object") &&
					(!_isNumeric(actualTypeName) ||
					 !_isNumeric(methodTypeName))) {

					continue outerLoop;
				}
			}

			constructorJSONObjects.add(constructorJSONObject);
		}

		return constructorJSONObjects;
	}

	protected synchronized JSONObject getJavaClassesJSONObject(String version)
		throws Exception {

		JSONObject javaClassesJSONObject = _javaClassesJSONObjectMap.get(
			version);

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

		JSONObject portalJSONObject = null;

		if (version.equals(getBaseDirName())) {
			portalJSONObject = PortalJSONObjectUtil.getPortalJSONObject(
				version);
		}
		else {
			portalJSONObject =
				PortalJSONObjectUtil.getPortalJSONObjectByVersion(version);
		}

		if (portalJSONObject.has("javaClasses")) {
			javaClassesJSONObject = portalJSONObject.getJSONObject(
				"javaClasses");
		}
		else {
			javaClassesJSONObject = new JSONObjectImpl();
		}

		_javaClassesJSONObjectMap.put(version, javaClassesJSONObject);

		return javaClassesJSONObject;
	}

	protected List getMethodCalls(
		DetailAST detailAST, List excludeImportNames,
		boolean skipDeprecated) {

		List methodCalls = new ArrayList<>();

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

		for (DetailAST methodCallDetailAST : methodCallDetailASTList) {
			if (skipDeprecated &&
				(hasDeprecatedParent(methodCallDetailAST) ||
				 hasSuppressDeprecationWarningsAnnotation(
					 methodCallDetailAST))) {

				continue;
			}

			String methodName = null;
			String variableTypeName = null;

			DetailAST firstChildDetailAST = methodCallDetailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
				methodName = firstChildDetailAST.getText();
				variableTypeName = StringBundler.concat(
					getPackageName(detailAST), ".",
					JavaSourceUtil.getClassName(getAbsolutePath()));
			}
			else if (firstChildDetailAST.getType() == TokenTypes.DOT) {
				DetailAST firstGrandChildDetailAST =
					firstChildDetailAST.getFirstChild();
				DetailAST lastGrandChildDetailAST =
					firstChildDetailAST.getLastChild();

				if ((firstGrandChildDetailAST.getType() != TokenTypes.IDENT) ||
					(lastGrandChildDetailAST.getType() != TokenTypes.IDENT)) {

					continue;
				}

				methodName = lastGrandChildDetailAST.getText();

				variableTypeName = getVariableTypeName(
					firstChildDetailAST, firstGrandChildDetailAST.getText(),
					false, false, true);
			}
			else {
				continue;
			}

			if (variableTypeName.startsWith("com.liferay.") &&
				!excludeImportNames.contains(variableTypeName)) {

				methodCalls.add(
					new MethodCall(
						methodName, variableTypeName,
						_getParameterTypeNames(methodCallDetailAST),
						methodCallDetailAST.getLineNo()));
			}
		}

		return methodCalls;
	}

	protected List getMethodJSONObjects(
		MethodCall methodCall, JSONObject javaClassesJSONObject) {

		List methodJSONObjects = new ArrayList<>();

		JSONObject classJSONObject = javaClassesJSONObject.getJSONObject(
			methodCall.getVariableTypeName());

		if (classJSONObject == null) {
			return methodJSONObjects;
		}

		JSONArray methodsJSONArray = classJSONObject.getJSONArray("methods");

		if (methodsJSONArray != null) {
			String methodName = methodCall.getName();
			List parameterTypeNames =
				methodCall.getParameterTypeNames();

			Iterator iterator = methodsJSONArray.iterator();

			outerLoop:
			while (iterator.hasNext()) {
				JSONObject methodJSONObject = iterator.next();

				if (!methodName.equals(methodJSONObject.getString("name"))) {
					continue;
				}

				JSONArray parametersJSONArray = methodJSONObject.getJSONArray(
					"parameters");

				if (parametersJSONArray == null) {
					if (parameterTypeNames.isEmpty()) {
						methodJSONObjects.add(methodJSONObject);
					}

					continue;
				}

				if (parametersJSONArray.length() != parameterTypeNames.size()) {
					continue;
				}

				for (int i = 0; i < parameterTypeNames.size(); i++) {
					String actualTypeName = parameterTypeNames.get(i);
					String methodTypeName = parametersJSONArray.getString(i);

					if (Validator.isNotNull(actualTypeName) &&
						!StringUtil.equalsIgnoreCase(
							actualTypeName, methodTypeName) &&
						!methodTypeName.equals("Object") &&
						(!_isNumeric(actualTypeName) ||
						 !_isNumeric(methodTypeName))) {

						continue outerLoop;
					}
				}

				methodJSONObjects.add(methodJSONObject);
			}
		}

		JSONArray extendedClassNamesJSONArray = classJSONObject.getJSONArray(
			"extendedClassNames");

		if (extendedClassNamesJSONArray != null) {
			Iterator iterator = extendedClassNamesJSONArray.iterator();

			while (iterator.hasNext()) {
				String extendedClassName = iterator.next();

				if (extendedClassName.startsWith("com.liferay.")) {
					methodJSONObjects.addAll(
						getMethodJSONObjects(
							new MethodCall(
								methodCall.getName(), extendedClassName,
								methodCall.getParameterTypeNames(),
								methodCall.getLineNumber()),
							javaClassesJSONObject));
				}
			}
		}

		return methodJSONObjects;
	}

	protected Map> getTypeNamesMap(
		DetailAST detailAST, List excludeImportNames,
		boolean skipDeprecated) {

		Map> typeNamesMap = new HashMap<>();

		List clauseDetailASTList = getAllChildTokens(
			detailAST, true, TokenTypes.EXTENDS_CLAUSE,
			TokenTypes.IMPLEMENTS_CLAUSE);

		for (DetailAST clauseDetailAST : clauseDetailASTList) {
			if (skipDeprecated &&
				(hasDeprecatedParent(clauseDetailAST) ||
				 hasSuppressDeprecationWarningsAnnotation(clauseDetailAST))) {

				continue;
			}

			List childDetailASTList = getAllChildTokens(
				clauseDetailAST, false, TokenTypes.DOT, TokenTypes.IDENT);

			for (DetailAST childDetailAST : childDetailASTList) {
				if (childDetailAST.getType() == TokenTypes.IDENT) {
					typeNamesMap = _addTypeName(
						typeNamesMap,
						getVariableTypeName(
							childDetailAST, childDetailAST.getText(), false,
							false, true),
						childDetailAST.getLineNo(), excludeImportNames);

					continue;
				}

				DetailAST firstChildDetailAST = childDetailAST.getFirstChild();

				if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
					typeNamesMap = _addTypeName(
						typeNamesMap,
						getVariableTypeName(
							firstChildDetailAST, firstChildDetailAST.getText(),
							false, false, true),
						firstChildDetailAST.getLineNo(), excludeImportNames);
				}
				else {
					FullIdent fullIdent = FullIdent.createFullIdent(
						childDetailAST);

					typeNamesMap = _addTypeName(
						typeNamesMap, fullIdent.getText(),
						firstChildDetailAST.getLineNo(), excludeImportNames);
				}
			}
		}

		List typeDetailASTList = getAllChildTokens(
			detailAST, true, TokenTypes.TYPE);

		for (DetailAST typeDetailAST : typeDetailASTList) {
			if (!skipDeprecated ||
				(!hasDeprecatedParent(typeDetailAST) &&
				 !hasSuppressDeprecationWarningsAnnotation(typeDetailAST))) {

				typeNamesMap = _addTypeName(
					typeNamesMap,
					getTypeName(typeDetailAST, false, false, true),
					typeDetailAST.getLineNo(), excludeImportNames);
			}
		}

		return typeNamesMap;
	}

	protected List getVariableCalls(
		DetailAST detailAST, List excludeImportNames,
		boolean skipDeprecated) {

		List variableCalls = new ArrayList<>();

		List dotDetailASTList = getAllChildTokens(
			detailAST, true, TokenTypes.DOT);

		for (DetailAST dotDetailAST : dotDetailASTList) {
			if (skipDeprecated &&
				(hasDeprecatedParent(dotDetailAST) ||
				 hasSuppressDeprecationWarningsAnnotation(dotDetailAST))) {

				continue;
			}

			DetailAST firstChildDetailAST = dotDetailAST.getFirstChild();
			DetailAST lastChildDetailAST = dotDetailAST.getLastChild();

			if ((firstChildDetailAST.getType() != TokenTypes.IDENT) ||
				(lastChildDetailAST.getType() != TokenTypes.IDENT)) {

				continue;
			}

			String variableTypeName = getVariableTypeName(
				firstChildDetailAST, firstChildDetailAST.getText(), false,
				false, true);

			if (!variableTypeName.startsWith("com.liferay.") ||
				excludeImportNames.contains(variableTypeName)) {

				continue;
			}

			variableCalls.add(
				new VariableCall(
					lastChildDetailAST.getText(), variableTypeName,
					lastChildDetailAST.getLineNo()));
		}

		return variableCalls;
	}

	protected JSONObject getVariableJSONObject(
		VariableCall variableCall, JSONObject javaClassesJSONObject) {

		JSONObject classJSONObject = javaClassesJSONObject.getJSONObject(
			variableCall.getTypeName());

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

		JSONArray variablesJSONArray = classJSONObject.getJSONArray(
			"variables");

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

		Iterator iterator = variablesJSONArray.iterator();

		while (iterator.hasNext()) {
			JSONObject variableJSONObject = iterator.next();

			if (Objects.equals(
					variableCall.getName(),
					variableJSONObject.getString("name"))) {

				return variableJSONObject;
			}
		}

		return null;
	}

	protected boolean hasDeprecatedParent(DetailAST detailAST) {
		DetailAST parentDetailAST = detailAST.getParent();

		while (true) {
			if (parentDetailAST == null) {
				return false;
			}

			if (((parentDetailAST.getType() == TokenTypes.CTOR_DEF) ||
				 (parentDetailAST.getType() == TokenTypes.METHOD_DEF) ||
				 (parentDetailAST.getType() == TokenTypes.VARIABLE_DEF)) &&
				AnnotationUtil.containsAnnotation(
					parentDetailAST, "Deprecated")) {

				return true;
			}

			parentDetailAST = parentDetailAST.getParent();
		}
	}

	protected boolean hasSuppressDeprecationWarningsAnnotation(
		DetailAST detailAST) {

		DetailAST parentDetailAST = detailAST.getParent();

		while (true) {
			if (parentDetailAST == null) {
				return false;
			}

			if (parentDetailAST.findFirstToken(TokenTypes.MODIFIERS) != null) {
				DetailAST annotationDetailAST = AnnotationUtil.getAnnotation(
					parentDetailAST, "SuppressWarnings");

				if (annotationDetailAST != null) {
					List literalStringDetailASTList =
						getAllChildTokens(
							annotationDetailAST, true,
							TokenTypes.STRING_LITERAL);

					for (DetailAST literalStringDetailAST :
							literalStringDetailASTList) {

						String s = literalStringDetailAST.getText();

						if (s.equals("\"deprecation\"")) {
							return true;
						}
					}
				}
			}

			parentDetailAST = parentDetailAST.getParent();
		}
	}

	protected class ConstructorCall {

		public ConstructorCall(
			String typeName, List parameterTypeNames, int lineNumber) {

			_typeName = typeName;
			_parameterTypeNames = parameterTypeNames;
			_lineNumber = lineNumber;
		}

		public int getLineNumber() {
			return _lineNumber;
		}

		public List getParameterTypeNames() {
			return _parameterTypeNames;
		}

		public String getTypeName() {
			return _typeName;
		}

		private int _lineNumber;
		private final List _parameterTypeNames;
		private final String _typeName;

	}

	protected class MethodCall {

		public MethodCall(
			String name, String variableTypeName,
			List parameterTypeNames, int lineNumber) {

			_name = name;
			_variableTypeName = variableTypeName;
			_parameterTypeNames = parameterTypeNames;
			_lineNumber = lineNumber;
		}

		public int getLineNumber() {
			return _lineNumber;
		}

		public String getName() {
			return _name;
		}

		public List getParameterTypeNames() {
			return _parameterTypeNames;
		}

		public String getVariableTypeName() {
			return _variableTypeName;
		}

		private int _lineNumber;
		private final String _name;
		private final List _parameterTypeNames;
		private final String _variableTypeName;

	}

	protected class VariableCall {

		public VariableCall(String name, String typeName, int lineNumber) {
			_name = name;
			_typeName = typeName;
			_lineNumber = lineNumber;
		}

		public int getLineNumber() {
			return _lineNumber;
		}

		public String getName() {
			return _name;
		}

		public String getTypeName() {
			return _typeName;
		}

		private int _lineNumber;
		private final String _name;
		private final String _typeName;

	}

	private Map> _addTypeName(
		Map> typeNamesMap, String typeName, int lineNumber,
		List excludeImportNames) {

		if ((excludeImportNames != null) &&
			(excludeImportNames.contains(typeName) ||
			 !typeName.startsWith("com.liferay."))) {

			return typeNamesMap;
		}

		Set lineNumbers = typeNamesMap.get(typeName);

		if (lineNumbers == null) {
			lineNumbers = new HashSet<>();
		}

		lineNumbers.add(lineNumber);

		typeNamesMap.put(typeName, lineNumbers);

		return typeNamesMap;
	}

	private String _getConstructorTypeName(DetailAST literalNewDetailAST) {
		DetailAST identDetailAST = literalNewDetailAST.findFirstToken(
			TokenTypes.IDENT);

		if (identDetailAST != null) {
			return StringBundler.concat(
				JavaSourceUtil.getPackageName(
					identDetailAST.getText(), getPackageName(identDetailAST),
					getImportNames(identDetailAST)),
				".", identDetailAST.getText());
		}

		DetailAST dotDetailAST = literalNewDetailAST.findFirstToken(
			TokenTypes.DOT);

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

		identDetailAST = dotDetailAST.findFirstToken(TokenTypes.IDENT);

		if (identDetailAST != null) {
			return StringBundler.concat(
				JavaSourceUtil.getPackageName(
					identDetailAST.getText(), getPackageName(identDetailAST),
					getImportNames(identDetailAST)),
				".", identDetailAST.getText());
		}

		return null;
	}

	private String _getParameterTypeName(DetailAST detailAST) {
		if (detailAST.getType() == TokenTypes.IDENT) {
			return getVariableTypeName(
				detailAST, detailAST.getText(), true, true, true);
		}

		if (detailAST.getType() == TokenTypes.STRING_LITERAL) {
			return "java.lang.String";
		}

		if (detailAST.getType() == TokenTypes.CHAR_LITERAL) {
			return "char";
		}

		if ((detailAST.getType() == TokenTypes.LITERAL_FALSE) ||
			(detailAST.getType() == TokenTypes.LITERAL_TRUE) ||
			(detailAST.getType() == TokenTypes.LNOT)) {

			return "boolean";
		}

		if (detailAST.getType() == TokenTypes.PLUS) {
			DetailAST stringLiteralDetailAST = detailAST.findFirstToken(
				TokenTypes.STRING_LITERAL);

			if (stringLiteralDetailAST != null) {
				return "java.lang.String";
			}

			return null;
		}

		if (detailAST.getType() == TokenTypes.LITERAL_NEW) {
			String parameterTypeName = null;

			DetailAST firstChildDetailAST = detailAST.getFirstChild();

			if (firstChildDetailAST.getType() == TokenTypes.IDENT) {
				parameterTypeName = getVariableTypeName(
					firstChildDetailAST, firstChildDetailAST.getText(), true,
					true, true);
			}
			else {
				parameterTypeName = _getParameterTypeName(firstChildDetailAST);
			}

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

			DetailAST curDetailAST = firstChildDetailAST.getNextSibling();

			while (true) {
				if (curDetailAST.getType() != TokenTypes.ARRAY_DECLARATOR) {
					return parameterTypeName;
				}

				parameterTypeName += "[]";

				curDetailAST = curDetailAST.getFirstChild();
			}
		}

		if (detailAST.getType() == TokenTypes.INDEX_OP) {
			String parameterTypeName = _getParameterTypeName(
				detailAST.getFirstChild());

			if (parameterTypeName != null) {
				parameterTypeName = StringUtil.replaceLast(
					parameterTypeName, "[]", "");
			}

			return parameterTypeName;
		}

		if (detailAST.getType() == TokenTypes.TYPECAST) {
			return getTypeName(detailAST, true, true, true);
		}

		if (ArrayUtil.contains(
				UNARY_OPERATOR_TOKEN_TYPES, detailAST.getType())) {

			return _getParameterTypeName(detailAST.getFirstChild());
		}

		if (detailAST.getType() == TokenTypes.LITERAL_THIS) {
			DetailAST parentDetailAST = getParentWithTokenType(
				detailAST, TokenTypes.CLASS_DEF);

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

			DetailAST identDetailAST = parentDetailAST.findFirstToken(
				TokenTypes.IDENT);

			return identDetailAST.getText();
		}

		if (ArrayUtil.contains(
				CONDITIONAL_OPERATOR_TOKEN_TYPES, detailAST.getType()) ||
			ArrayUtil.contains(
				RELATIONAL_OPERATOR_TOKEN_TYPES, detailAST.getType())) {

			return "boolean";
		}

		if (detailAST.getType() == TokenTypes.NUM_DOUBLE) {
			return "double";
		}

		if (detailAST.getType() == TokenTypes.NUM_FLOAT) {
			return "float";
		}

		if (detailAST.getType() == TokenTypes.NUM_INT) {
			return "int";
		}

		if (detailAST.getType() == TokenTypes.NUM_LONG) {
			return "long";
		}

		if ((detailAST.getType() == TokenTypes.LITERAL_BOOLEAN) ||
			(detailAST.getType() == TokenTypes.LITERAL_BYTE) ||
			(detailAST.getType() == TokenTypes.LITERAL_DOUBLE) ||
			(detailAST.getType() == TokenTypes.LITERAL_FLOAT) ||
			(detailAST.getType() == TokenTypes.LITERAL_INT) ||
			(detailAST.getType() == TokenTypes.LITERAL_LONG) ||
			(detailAST.getType() == TokenTypes.LITERAL_SHORT)) {

			return detailAST.getText();
		}

		if (detailAST.getType() == TokenTypes.QUESTION) {
			return _getParameterTypeName(detailAST.getLastChild());
		}

		return null;
	}

	private List _getParameterTypeNames(DetailAST detailAST) {
		List parameterTypeNames = new ArrayList<>();

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

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

		for (DetailAST exprDetailAST : exprDetailASTList) {
			parameterTypeNames.add(
				_getParameterTypeName(exprDetailAST.getFirstChild()));
		}

		return parameterTypeNames;
	}

	private boolean _isNumeric(String typeName) {
		if (typeName.equals("double") || typeName.equals("float") ||
			typeName.equals("int") || typeName.equals("long")) {

			return true;
		}

		return false;
	}

	private final Map _javaClassesJSONObjectMap =
		new HashMap<>();

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy