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

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

There is a newer version: 1.0.1411
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.check;

import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.source.formatter.BNDSettings;
import com.liferay.source.formatter.check.util.BNDSourceUtil;
import com.liferay.source.formatter.parser.JavaClass;
import com.liferay.source.formatter.parser.JavaTerm;
import com.liferay.source.formatter.util.FileUtil;

import java.io.IOException;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Hugo Huijser
 */
public class JavaPackagePathCheck extends BaseJavaTermCheck {

	@Override
	protected String doProcess(
			String fileName, String absolutePath, JavaTerm javaTerm,
			String fileContent)
		throws IOException {

		if (javaTerm.getParentJavaClass() != null) {
			return javaTerm.getContent();
		}

		JavaClass javaClass = (JavaClass)javaTerm;

		if (javaClass.isAnonymous() || javaClass.hasAnnotation("Deprecated")) {
			return javaTerm.getContent();
		}

		String packageName = javaClass.getPackageName();

		if (Validator.isNull(packageName)) {
			addMessage(fileName, "Missing package");

			return javaTerm.getContent();
		}

		_checkPackageName(
			fileName, absolutePath, packageName, javaClass.getName());

		if (isModulesFile(absolutePath) && !isModulesApp(absolutePath, true)) {
			_checkModulePackageName(fileName, absolutePath, packageName);
		}

		_checkPackageNameByClassName(
			fileName, absolutePath, javaClass.getName(), packageName);

		if (absolutePath.contains("-api/")) {
			return javaTerm.getContent();
		}

		List expectedInternalImplementsDataEntries = getAttributeValues(
			_EXPECTED_INTERNAL_IMPLEMENTS_DATA_KEY, absolutePath);

		for (String expectedInternalImplementsDataEntry :
				expectedInternalImplementsDataEntries) {

			String[] array = StringUtil.split(
				expectedInternalImplementsDataEntry, CharPool.COLON);

			if (array.length == 2) {
				_checkInternalPackageName(
					fileName, absolutePath, javaClass, array[0], packageName,
					array[1]);
			}
		}

		return javaTerm.getContent();
	}

	@Override
	protected String[] getCheckableJavaTermNames() {
		return new String[] {JAVA_CLASS};
	}

	private void _checkInternalPackageName(
			String fileName, String absolutePath, JavaClass javaClass,
			String implementedClassName, String packageName,
			String expectedPackageName)
		throws IOException {

		if (absolutePath.contains("/test/")) {
			return;
		}

		List implementedClassNames =
			javaClass.getImplementedClassNames();

		if (!packageName.contains(".internal.") &&
			!packageName.endsWith(".internal") &&
			absolutePath.matches(".*/modules(/dxp)?/apps/.*")) {

			String className = javaClass.getName();

			if (implementedClassNames.contains(implementedClassName) &&
				!className.startsWith("Base")) {

				addMessage(
					fileName,
					StringBundler.concat(
						"Class implementing '", implementedClassName,
						"' should be in 'internal' package"));
			}

			return;
		}

		if (packageName.endsWith(expectedPackageName)) {
			return;
		}

		if (!implementedClassNames.contains(implementedClassName)) {
			List extendedClassNames = javaClass.getExtendedClassNames();

			if (!extendedClassNames.contains("Base" + implementedClassName)) {
				return;
			}
		}
		else {
			if (implementedClassNames.size() != 1) {
				return;
			}

			for (String extendedClassName : javaClass.getExtendedClassNames()) {
				if (extendedClassName.startsWith("Base") &&
					!extendedClassName.endsWith(implementedClassName)) {

					return;
				}
			}
		}

		BNDSettings bndSettings = getBNDSettings(fileName);

		if (bndSettings == null) {
			return;
		}

		String bundleSymbolicName = BNDSourceUtil.getDefinitionValue(
			bndSettings.getContent(), "Bundle-SymbolicName");

		Matcher matcher = _apiOrServiceBundleSymbolicNamePattern.matcher(
			bundleSymbolicName);

		bundleSymbolicName = matcher.replaceAll(StringPool.BLANK);

		if (bundleSymbolicName.endsWith(expectedPackageName)) {
			return;
		}

		int x = -1;

		while (true) {
			x = expectedPackageName.indexOf(".", x + 1);

			if (x == -1) {
				break;
			}

			if (bundleSymbolicName.endsWith(
					expectedPackageName.substring(0, x))) {

				expectedPackageName = expectedPackageName.substring(x + 1);

				break;
			}
		}

		if (packageName.endsWith(expectedPackageName)) {
			return;
		}

		if (implementedClassNames.contains(implementedClassName)) {
			addMessage(
				fileName,
				StringBundler.concat(
					"Package for class implementing '", implementedClassName,
					"' should end with '", expectedPackageName, "'"));
		}
		else {
			addMessage(
				fileName,
				StringBundler.concat(
					"Package for class extending 'Base", implementedClassName,
					"' should end with '", expectedPackageName, "'"));
		}
	}

	private void _checkModulePackageName(
			String fileName, String absolutePath, String packageName)
		throws IOException {

		if (!packageName.startsWith("com.liferay")) {
			return;
		}

		BNDSettings bndSettings = getBNDSettings(fileName);

		if (bndSettings == null) {
			return;
		}

		String bundleSymbolicName = BNDSourceUtil.getDefinitionValue(
			bndSettings.getContent(), "Bundle-SymbolicName");

		if (!bundleSymbolicName.startsWith("com.liferay")) {
			return;
		}

		if (isAttributeValue(_CHECK_SERVICE_PATH_KEY, absolutePath) &&
			bundleSymbolicName.endsWith(".service") &&
			packageName.contains(bundleSymbolicName + ".internal")) {

			int x = absolutePath.lastIndexOf("-service/");

			if ((x != -1) &&
				FileUtil.exists(
					absolutePath.substring(0, x + 9) + "service.xml")) {

				addMessage(
					fileName,
					StringBundler.concat(
						"Package should not contain '", bundleSymbolicName,
						"'. It should contain '",
						bundleSymbolicName.substring(
							0, bundleSymbolicName.length() - 8),
						"' (without .service)"));

				return;
			}
		}

		bundleSymbolicName = bundleSymbolicName.replaceAll(
			"\\.(api|service|test)$", StringPool.BLANK);

		if (packageName.contains(bundleSymbolicName)) {
			return;
		}

		bundleSymbolicName = bundleSymbolicName.replaceAll(
			"\\.impl$", ".internal");

		if (!packageName.contains(bundleSymbolicName)) {
			addMessage(
				fileName,
				"Package should follow Bundle-SymbolicName specified in " +
					bndSettings.getFileName());
		}
	}

	private void _checkPackageName(
		String fileName, String absolutePath, String packageName,
		String className) {

		for (String packagePart :
				StringUtil.split(packageName, CharPool.PERIOD)) {

			if (packagePart.matches("V\\d*(_\\d+)+")) {
				addMessage(
					fileName,
					"Use lower case 'v' when it means version in the package");

				return;
			}
		}

		int pos = fileName.lastIndexOf(CharPool.SLASH);

		String filePath = StringUtil.replace(
			fileName.substring(0, pos), CharPool.SLASH, CharPool.PERIOD);

		if (!filePath.endsWith(packageName)) {
			addMessage(
				fileName,
				"The declared package '" + packageName +
					"' does not match the expected package");

			return;
		}

		if (packageName.matches(".*\\.impl\\.([\\w.]+\\.)?internal") ||
			packageName.matches(".*\\.internal\\.([\\w.]+\\.)?impl")) {

			addMessage(
				fileName,
				"Do not use both 'impl' and 'internal' in the package");
		}

		List allowedInternalPackageDirNames = getAttributeValues(
			_ALLOWED_INTERNAL_PACKAGE_DIR_NAMES_KEY, absolutePath);

		for (String allowedInternalPackageDirName :
				allowedInternalPackageDirNames) {

			if (absolutePath.contains(allowedInternalPackageDirName)) {
				return;
			}
		}

		if (absolutePath.contains("-api/src/")) {
			Matcher matcher = _internalPackagePattern.matcher(packageName);

			if (matcher.find()) {
				addMessage(
					fileName,
					"Do not use '" + matcher.group(1) +
						"' package in API module");
			}

			if (packageName.contains(".api.") || packageName.endsWith(".api")) {
				addMessage(
					fileName,
					"Do not use 'api' in the package for classes in the API " +
						"module");
			}
		}

		if (className.matches(".*(? expectedPackagePathDataEntries = getAttributeValues(
			_EXPECTED_PACKAGE_PATH_DATA_KEY, absolutePath);

		for (String expectedPackagePathDataEntry :
				expectedPackagePathDataEntries) {

			String[] array = StringUtil.split(
				expectedPackagePathDataEntry, CharPool.COLON);

			if ((array.length != 2) || !className.matches(array[0])) {
				continue;
			}

			String expectedPackagePath = array[1];

			if (expectedPackagePath.startsWith(".")) {
				if (!packageName.endsWith(expectedPackagePath)) {
					addMessage(
						fileName,
						StringBundler.concat(
							"Class '", className,
							"' should be in package ending with '", array[1],
							"'"));
				}

				continue;
			}

			if (!packageName.endsWith("." + expectedPackagePath) &&
				!packageName.contains("." + expectedPackagePath + ".")) {

				addMessage(
					fileName,
					StringBundler.concat(
						"Class '", className, "' should be in package .",
						array[1], "'"));
			}
		}
	}

	private static final String _ALLOWED_INTERNAL_PACKAGE_DIR_NAMES_KEY =
		"allowedInternalPackageDirNames";

	private static final String _CHECK_SERVICE_PATH_KEY = "checkServicePath";

	private static final String _EXPECTED_INTERNAL_IMPLEMENTS_DATA_KEY =
		"expectedInternalImplementsData";

	private static final String _EXPECTED_PACKAGE_PATH_DATA_KEY =
		"expectedPackagePathData";

	private static final Pattern _apiOrServiceBundleSymbolicNamePattern =
		Pattern.compile("\\.(api|service)$");
	private static final Pattern _internalPackagePattern = Pattern.compile(
		"\\.(impl|internal)(\\.|\\Z)");

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy