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

com.liferay.source.formatter.check.UpgradeCatchAllCheck Maven / Gradle / Ivy

There is a newer version: 1.0.1457
Show newest version
/**
 * SPDX-FileCopyrightText: (c) 2023 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.check;

import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.json.JSONArrayImpl;
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.util.ArrayUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.source.formatter.check.util.JavaSourceUtil;
import com.liferay.source.formatter.exception.UpgradeCatchAllException;
import com.liferay.source.formatter.parser.JavaClass;
import com.liferay.source.formatter.parser.JavaClassParser;
import com.liferay.source.formatter.parser.JavaMethod;
import com.liferay.source.formatter.parser.JavaTerm;
import com.liferay.source.formatter.parser.JavaVariable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Nícolas Moura
 */
public class UpgradeCatchAllCheck extends BaseFileCheck {

	public static String[] getExpectedMessages() throws Exception {
		List expectedMessages = new ArrayList<>();

		JSONArray jsonArray = _getReplacementsJSONArray("replacements.json");

		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObject = jsonArray.getJSONObject(i);

			String[] validExtensions = JSONUtil.toStringArray(
				jsonObject.getJSONArray("validExtensions"));

			if ((validExtensions.length > 0) &&
				!ArrayUtil.contains(validExtensions, "java")) {

				continue;
			}

			String from = jsonObject.getString("from");

			Set keys = jsonObject.keySet();

			boolean skipValidation = false;

			if (jsonObject.getBoolean("skipParametersValidation") ||
				from.startsWith("regex:")) {

				skipValidation = true;
			}

			if ((from.contains(StringPool.OPEN_PARENTHESIS) &&
				 !skipValidation) ||
				!keys.contains("to")) {

				expectedMessages.add(_getMessage(jsonObject));
			}
		}

		return ArrayUtil.toStringArray(expectedMessages);
	}

	public static void setTestMode(boolean testMode) {
		_testMode = testMode;
	}

	@Override
	protected String doProcess(
			String fileName, String absolutePath, String content)
		throws Exception {

		if (_testMode && fileName.endsWith(".java")) {
			UpgradeCatchAllJavaTermOrderCheck termOrderCheck =
				new UpgradeCatchAllJavaTermOrderCheck();

			JavaClass javaClass = JavaClassParser.parseJavaClass(
				fileName, content);

			if (!StringUtil.equals(
					javaClass.getContent(),
					termOrderCheck.doProcess(
						fileName, absolutePath, javaClass, content))) {

				throw new UpgradeCatchAllException(
					fileName + " missing javaTerms sorting");
			}
		}

		JSONArray jsonArray = _getReplacementsJSONArray("replacements.json");

		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObject = jsonArray.getJSONObject(i);

			if (!_hasValidExtension(fileName, jsonObject)) {
				continue;
			}

			String oldContent = content;

			_newMessage = false;

			if (fileName.endsWith(".java")) {
				content = _formatJava(content, fileName, jsonObject);
			}
			else {
				content = _formatGeneral(content, fileName, jsonObject);
			}

			if (_testMode && oldContent.equals(content)) {
				String to = jsonObject.getString("to");

				if (to.isEmpty() && _newMessage) {
					continue;
				}

				throw new UpgradeCatchAllException(
					"Unable to process pattern " +
						jsonObject.getString("from") +
							" or there is no test associated with it");
			}
		}

		_testMode = false;

		return content;
	}

	private static List _getInterpolatedNewParameterNames(
		List parameterNames, List newParameterNames,
		String prefix) {

		List interpolatedNewParameterNames = new ArrayList<>();

		for (String newParameterName : newParameterNames) {
			if (newParameterName.contains(prefix)) {
				List indexes = new ArrayList<>();

				Matcher matcher = _parameterNamePattern.matcher(
					newParameterName);

				while (matcher.find()) {
					int index = GetterUtil.getInteger(matcher.group(1));

					indexes.add(index);
				}

				for (int index : indexes) {
					newParameterName = StringUtil.replace(
						newParameterName, prefix + index + CharPool.POUND,
						parameterNames.get(index));
				}
			}

			interpolatedNewParameterNames.add(newParameterName);
		}

		return interpolatedNewParameterNames;
	}

	private static String _getMessage(JSONObject jsonObject) {
		StringBundler sb = new StringBundler(6);

		sb.append("See ");
		sb.append(jsonObject.getString("issueKey"));
		sb.append(StringPool.COMMA_AND_SPACE);

		String[] classNames = JSONUtil.toStringArray(
			jsonObject.getJSONArray("classNames"));

		String classNamesFormated = null;

		if (classNames.length > 0) {
			classNamesFormated = StringUtil.merge(classNames, StringPool.SLASH);
		}

		String from = jsonObject.getString("from");

		int index = from.indexOf(CharPool.PERIOD);

		if (index != -1) {
			from = StringUtil.replace(from, CharPool.PERIOD, CharPool.POUND);
		}
		else if (classNamesFormated != null) {
			sb.append(classNamesFormated);
			sb.append(StringPool.POUND);
		}

		sb.append(from);

		return sb.toString();
	}

	private static Pattern _getPattern(JSONObject jsonObject) {
		String from = jsonObject.getString("from");

		String regex = StringBundler.concat("\\b", from, "\\b");

		if (from.startsWith("regex:")) {
			return Pattern.compile(from.replaceFirst("regex:", ""));
		}
		else if (regex.contains(StringPool.SLASH)) {
			return Pattern.compile(
				StringUtil.replace(regex, CharPool.SLASH, "\\/"));
		}

		if (regex.contains(StringPool.OPEN_PARENTHESIS)) {
			regex = StringUtil.replace(
				regex, CharPool.OPEN_PARENTHESIS, "\\b\\(");

			if (jsonObject.getBoolean("skipParametersValidation") &&
				!from.matches(_CONSTRUCTOR_REGEX)) {

				regex = StringUtil.replace(
					regex, CharPool.CLOSE_PARENTHESIS, "\\)");

				regex = StringUtil.removeSubstring(regex, "\\b");
			}
			else {
				regex = regex.substring(
					0, regex.indexOf(CharPool.OPEN_PARENTHESIS) + 1);
			}
		}
		else if (regex.endsWith(">\\b")) {
			regex = StringUtil.removeLast(regex, "\\b");

			regex = StringUtil.replaceFirst(
				regex, CharPool.LESS_THAN, "\\s*<[?\\s\\w]*");

			regex = StringUtil.replace(
				regex, StringPool.COMMA_AND_SPACE, ",[?\\s\\w]*");
		}
		else {
			regex = regex + "[,;> ({]";
		}

		if (regex.contains(StringPool.PERIOD)) {
			return Pattern.compile(
				StringUtil.replace(regex, CharPool.PERIOD, "\\.\\s*"));
		}

		if (Character.isUpperCase(from.charAt(0)) ||
			StringUtil.startsWith(from, "new")) {

			String[] classNames = JSONUtil.toStringArray(
				jsonObject.getJSONArray("classNames"));

			if (classNames.length == 0) {
				return Pattern.compile(regex);
			}
		}

		return Pattern.compile("\\w+\\.[\\w\\(\\)\\s\\.]*" + regex);
	}

	private static JSONArray _getReplacementsJSONArray(String fileName)
		throws Exception {

		ClassLoader classLoader = UpgradeCatchAllCheck.class.getClassLoader();

		return new JSONArrayImpl(
			StringUtil.read(
				classLoader.getResourceAsStream("dependencies/" + fileName)));
	}

	private String _addNewReference(String content, String newReference) {
		if (!newReference.equals(StringPool.BLANK)) {
			content = JavaSourceUtil.addImports(
				content, "org.osgi.service.component.annotations.Reference");

			return StringUtil.replaceLast(
				content, CharPool.CLOSE_CURLY_BRACE,
				"\t@Reference\n\tprivate " + newReference + ";\n\n}");
		}

		return content;
	}

	private String _addOrReplaceMethodParameters(
		List methodParameterNames, String newMethodCall,
		List newMethodParameterNames) {

		return _addOrReplaceParameters(
			StringPool.CLOSE_PARENTHESIS, newMethodCall,
			newMethodParameterNames, methodParameterNames, "param#");
	}

	private String _addOrReplaceParameters(
		String lastCharacter, String newMethodCall,
		List newParameterNames, List parameterNames,
		String prefix) {

		StringBundler sb = new StringBundler(2 + newParameterNames.size());

		sb.append(newMethodCall);

		sb.append(
			StringUtil.merge(
				_getInterpolatedNewParameterNames(
					parameterNames, newParameterNames, prefix),
				StringPool.COMMA_AND_SPACE));

		sb.append(lastCharacter);

		return sb.toString();
	}

	private String _addOrReplaceTypeParameters(
		String newMethodCall, List newTypeParameterNames,
		List typeParameterNames) {

		return _addOrReplaceParameters(
			StringPool.GREATER_THAN, newMethodCall, newTypeParameterNames,
			typeParameterNames, "typeParam#");
	}

	private String _addReplacementDependencies(
		String fileName, JSONObject jsonObject, String newContent) {

		String[] newImports = JSONUtil.toStringArray(
			jsonObject.getJSONArray("newImports"));

		if (fileName.endsWith(".java")) {
			newContent = JavaSourceUtil.addImports(newContent, newImports);

			return _addNewReference(
				newContent, jsonObject.getString("newReference"));
		}
		else if (fileName.endsWith(".jsp")) {
			newContent = BaseUpgradeCheck.addNewImportsJSPHeader(
				newContent, newImports);
		}

		return newContent;
	}

	private String _formatGeneral(
		String content, String fileName, JSONObject jsonObject) {

		String newContent = content;

		Pattern pattern = _getPattern(jsonObject);

		Matcher matcher = pattern.matcher(content);

		while (matcher.find()) {
			String methodCall = matcher.group();

			String from = jsonObject.getString("from");
			String to = jsonObject.getString("to");

			if (from.startsWith("regex:")) {
				newContent = newContent.replaceAll(pattern.toString(), to);
			}
			else if (from.contains(StringPool.OPEN_PARENTHESIS)) {
				newContent = _formatMethodCall(
					fileName, from, newContent, jsonObject, matcher, newContent,
					to);
			}
			else {
				Set keys = jsonObject.keySet();

				if (!keys.contains("to")) {
					addMessage(fileName, _getMessage(jsonObject));

					_newMessage = true;

					continue;
				}

				newContent = StringUtil.replaceFirst(
					newContent, methodCall,
					StringUtil.replace(methodCall, from, to));
			}
		}

		if (!content.equals(newContent)) {
			newContent = _addReplacementDependencies(
				fileName, jsonObject, newContent);
		}

		return newContent;
	}

	private String _formatJava(
			String content, String fileName, JSONObject jsonObject)
		throws Exception {

		String newContent = content;

		JavaClass javaClass = JavaClassParser.parseJavaClass(fileName, content);

		for (JavaTerm childJavaTerm : javaClass.getChildJavaTerms()) {
			String javaContent = null;

			if (childJavaTerm.isJavaMethod()) {
				JavaMethod javaMethod = (JavaMethod)childJavaTerm;

				javaContent = javaMethod.getContent();
			}
			else if (childJavaTerm.isJavaVariable()) {
				JavaVariable javaVariable = (JavaVariable)childJavaTerm;

				javaContent = javaVariable.getContent();
			}

			if (javaContent == null) {
				continue;
			}

			Pattern pattern = _getPattern(jsonObject);

			Matcher matcher = pattern.matcher(javaContent);

			while (matcher.find()) {
				String methodCall = matcher.group();

				String[] classNames = JSONUtil.toStringArray(
					jsonObject.getJSONArray("classNames"));

				if ((classNames.length > 0) &&
					!_hasValidClassName(
						classNames, javaContent, content, fileName,
						methodCall)) {

					continue;
				}

				String from = jsonObject.getString("from");
				String to = jsonObject.getString("to");

				if (from.startsWith("regex:")) {
					newContent = newContent.replaceAll(pattern.toString(), to);
				}
				else if (from.contains(StringPool.OPEN_PARENTHESIS)) {
					newContent = _formatMethodCall(
						fileName, from, javaContent, jsonObject, matcher,
						newContent, to);
				}
				else if (from.contains(StringPool.LESS_THAN)) {
					newContent = _formatTypeParameters(
						methodCall, newContent, to);
				}
				else {
					newContent = StringUtil.replaceFirst(
						newContent, methodCall,
						StringUtil.replace(methodCall, from, to));
				}
			}
		}

		if (!content.equals(newContent)) {
			newContent = _addReplacementDependencies(
				fileName, jsonObject, newContent);
		}
		else if (!_newMessage) {
			Set keys = jsonObject.keySet();

			if (!keys.contains("to")) {
				Pattern pattern = _getPattern(jsonObject);

				Matcher matcher = pattern.matcher(content);

				if (matcher.find()) {
					addMessage(fileName, _getMessage(jsonObject));

					_newMessage = true;
				}
			}
		}

		return newContent;
	}

	private String _formatMethodCall(
		String fileName, String from, String javaMethodContent,
		JSONObject jsonObject, Matcher matcher, String newContent, String to) {

		String methodCall = JavaSourceUtil.getMethodCall(
			javaMethodContent, matcher.start());

		List parameterNames = JavaSourceUtil.getParameterNames(
			methodCall);

		if (!_hasValidMethodCall(
				fileName, from, javaMethodContent, jsonObject, newContent,
				parameterNames)) {

			return newContent;
		}

		if (to.isEmpty()) {
			String newJavaMethodContent = StringUtil.removeFirst(
				javaMethodContent, methodCall);

			String line = getLine(
				newJavaMethodContent,
				getLineNumber(newJavaMethodContent, matcher.start()));

			return StringUtil.replaceFirst(
				newContent, javaMethodContent,
				StringUtil.removeFirst(
					newJavaMethodContent, line + CharPool.NEW_LINE));
		}

		return _formatParameters(methodCall, newContent, parameterNames, to);
	}

	private String _formatParameters(
		String methodCall, String newContent, List parameterNames,
		String to) {

		String newMethodCall = to.substring(
			0, to.indexOf(CharPool.OPEN_PARENTHESIS) + 1);

		if (!newMethodCall.contains(StringPool.PERIOD) &&
			!Character.isUpperCase(newMethodCall.charAt(0)) &&
			!newMethodCall.contains(StringPool.SPACE)) {

			newMethodCall = StringBundler.concat(
				getVariableName(methodCall), CharPool.PERIOD, newMethodCall);
		}

		newMethodCall = _addOrReplaceMethodParameters(
			parameterNames, newMethodCall, JavaSourceUtil.getParameterList(to));

		return StringUtil.replaceFirst(newContent, methodCall, newMethodCall);
	}

	private String _formatTypeParameters(
		String methodCall, String newContent, String to) {

		String newMethodCall = methodCall.substring(
			0, methodCall.indexOf(CharPool.LESS_THAN) + 1);

		String newTypeParameterName = to.substring(
			to.indexOf(CharPool.LESS_THAN) + 1,
			to.lastIndexOf(CharPool.GREATER_THAN));

		List newTypeParameterNames = Arrays.asList(
			newTypeParameterName.split(StringPool.COMMA_AND_SPACE));

		String typeParameterName = methodCall.substring(
			methodCall.indexOf(CharPool.LESS_THAN) + 1,
			methodCall.lastIndexOf(CharPool.GREATER_THAN));

		List typeParameterNames = Arrays.asList(
			typeParameterName.split(StringPool.COMMA_AND_SPACE));

		newMethodCall = _addOrReplaceTypeParameters(
			newMethodCall, newTypeParameterNames, typeParameterNames);

		return StringUtil.replace(newContent, methodCall, newMethodCall);
	}

	private boolean _hasValidClassName(
			String[] classNames, String content, String fileContent,
			String fileName, String methodCall)
		throws Exception {

		String variableName = getVariableName(methodCall);

		for (String className : classNames) {
			if (Character.isUpperCase(variableName.charAt(0)) &&
				StringUtil.equals(variableName, className)) {

				return true;
			}
			else if (!Character.isUpperCase(variableName.charAt(0)) &&
					 hasClassOrVariableName(
						 className, content, fileContent, fileName,
						 methodCall)) {

				return true;
			}
		}

		return false;
	}

	private boolean _hasValidExtension(String fileName, JSONObject jsonObject) {
		String[] validExtensions = JSONUtil.toStringArray(
			jsonObject.getJSONArray("validExtensions"));

		if (validExtensions.length == 0) {
			validExtensions = new String[] {"java"};
		}

		for (String validExtension : validExtensions) {
			if (fileName.endsWith(CharPool.PERIOD + validExtension)) {
				return true;
			}
		}

		return false;
	}

	private boolean _hasValidMethodCall(
		String fileName, String from, String javaMethodContent,
		JSONObject jsonObject, String newContent, List parameterNames) {

		List fromParameters = JavaSourceUtil.getParameterNames(from);

		boolean skipParametersValidation = jsonObject.getBoolean(
			"skipParametersValidation");

		if (!skipParametersValidation) {
			fromParameters = JavaSourceUtil.getParameterTypes(from);
		}

		if (parameterNames.size() != fromParameters.size()) {
			return false;
		}

		if (skipParametersValidation) {
			return true;
		}

		boolean sendMessage = false;

		Set keys = jsonObject.keySet();

		if (!keys.contains("to")) {
			sendMessage = true;
		}
		else if (fileName.endsWith(".java")) {
			for (int i = 0; i < fromParameters.size(); i++) {
				String parameterName = parameterNames.get(i);

				String variableTypeName = getVariableTypeName(
					javaMethodContent, null, newContent, fileName,
					parameterName.trim(), true, false);

				if (variableTypeName == null) {
					sendMessage = true;
				}
				else if (!StringUtil.equals(
							fromParameters.get(i), variableTypeName)) {

					return false;
				}
			}
		}

		if (sendMessage) {
			addMessage(fileName, _getMessage(jsonObject));

			_newMessage = true;

			return false;
		}

		return true;
	}

	private static final String _CONSTRUCTOR_REGEX =
		"n?e?w? ?(:?[A-Z][a-z]*)+\\(.*\\)";

	private static final Pattern _parameterNamePattern = Pattern.compile(
		"\\w+#(\\d+)#");
	private static boolean _testMode;

	private boolean _newMessage;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy