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

com.liferay.source.formatter.check.BNDExportsCheck 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.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.ArrayUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.source.formatter.check.util.BNDSourceUtil;
import com.liferay.source.formatter.check.util.SourceUtil;
import com.liferay.source.formatter.util.FileUtil;

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

import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

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

/**
 * @author Hugo Huijser
 */
public class BNDExportsCheck extends BaseFileCheck {

	@Override
	public boolean isLiferaySourceCheck() {
		return true;
	}

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

		if (!fileName.endsWith("/bnd.bnd") ||
			absolutePath.contains("/third-party/")) {

			return content;
		}

		if (!absolutePath.contains("/testIntegration/")) {
			_checkExports(
				fileName, content, _exportContentsPattern, "-exportcontents");
			_checkExports(fileName, content, _exportsPattern, "Export-Package");
		}

		List exportPackages = _getExportPackages(content);

		if (exportPackages.isEmpty()) {
			return content;
		}

		if (absolutePath.contains("/modules/apps/")) {
			_checkAllowedExportPackages(fileName, absolutePath, exportPackages);
		}

		_checkExportPackages(
			fileName, exportPackages, isModulesFile(absolutePath));

		return content;
	}

	private void _checkAllowedExportPackages(
		String fileName, String absolutePath, List exportPackages) {

		if (fileName.endsWith("/test-bnd.bnd")) {
			return;
		}

		List allowedExportPackageDirNames = getAttributeValues(
			_ALLOWED_EXPORT_PACKAGE_DIR_NAMES_KEY, absolutePath);

		for (String allowedExportPackageDirName :
				allowedExportPackageDirNames) {

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

		int x = absolutePath.lastIndexOf(StringPool.SLASH);

		int y = absolutePath.lastIndexOf(StringPool.SLASH, x - 1);

		String moduleName = absolutePath.substring(y + 1, x);

		if (moduleName.endsWith("-api") || moduleName.endsWith("-client") ||
			moduleName.endsWith("-spi") || moduleName.endsWith("-taglib") ||
			moduleName.contains("-taglib-") ||
			moduleName.endsWith("-test-util")) {

			return;
		}

		if (!moduleName.endsWith("-service")) {
			addMessage(
				fileName,
				"Exporting packages not allowed in module \"" + moduleName +
					"\"");

			return;
		}

		for (String exportPackage : exportPackages) {
			if (!exportPackage.endsWith(".http")) {
				addMessage(
					fileName,
					"Only allowed to export package \"*.http\" in module \"" +
						moduleName + "\"");

				return;
			}
		}
	}

	private void _checkExportPackage(
			String fileName, String srcDirLocation, String exportPackage,
			boolean modulesFile)
		throws IOException {

		String exportPackagePath = StringUtil.replace(
			exportPackage, CharPool.PERIOD, CharPool.SLASH);

		if (ArrayUtil.isNotEmpty(
				_getExportPackageResourcesFiles(
					srcDirLocation, exportPackagePath, modulesFile)) ||
			ArrayUtil.isNotEmpty(
				_getExportPackageSrcFiles(
					srcDirLocation, exportPackagePath, modulesFile))) {

			File packageinfoFile = null;

			if (modulesFile) {
				packageinfoFile = new File(
					StringBundler.concat(
						srcDirLocation, "main/resources/", exportPackagePath,
						"/packageinfo"));
			}
			else {
				packageinfoFile = new File(
					StringBundler.concat(
						srcDirLocation, exportPackagePath, "/packageinfo"));
			}

			if (!packageinfoFile.exists()) {
				addMessage(fileName, "Added packageinfo for " + exportPackage);

				FileUtil.write(packageinfoFile, "version 1.0.0");
			}
		}
		else if (exportPackage.startsWith("com.liferay.")) {
			int i = fileName.lastIndexOf("/");

			File importedFilesPropertiesFile = new File(
				fileName.substring(0, i) + "/imported-files.properties");

			if (!importedFilesPropertiesFile.exists()) {
				addMessage(
					fileName,
					"Unneeded/incorrect Export-Package: " + exportPackage);
			}
		}
	}

	private void _checkExportPackages(
			String fileName, List exportPackages, boolean modulesFile)
		throws IOException {

		String srcDirLocation = _getSrcDirLocation(fileName, modulesFile);

		if (srcDirLocation == null) {
			return;
		}

		List removedExportPackages = new ArrayList<>();

		for (String exportPackage : exportPackages) {
			if (exportPackage.startsWith(StringPool.EXCLAMATION)) {
				removedExportPackages.add(
					StringUtil.replace(
						exportPackage.substring(1), CharPool.PERIOD,
						CharPool.SLASH));
			}
			else if (exportPackage.endsWith(".*")) {
				_checkWildCardExportPackage(
					fileName, srcDirLocation, exportPackage,
					removedExportPackages);
			}
			else {
				_checkExportPackage(
					fileName, srcDirLocation, exportPackage, modulesFile);
			}
		}
	}

	private void _checkExports(
		String fileName, String content, Pattern pattern,
		String definitionKey) {

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

		if ((bundleSymbolicName == null) ||
			bundleSymbolicName.startsWith(StringPool.DOLLAR) ||
			bundleSymbolicName.endsWith(".compat")) {

			return;
		}

		Matcher matcher = _apiOrServiceBundleSymbolicNamePattern.matcher(
			bundleSymbolicName);

		bundleSymbolicName = matcher.replaceAll(StringPool.BLANK);

		matcher = pattern.matcher(content);

		if (!matcher.find()) {
			return;
		}

		String[] lines = StringUtil.splitLines(matcher.group(2));

		for (int i = 0; i < lines.length; i++) {
			String line = StringUtil.removeChar(
				StringUtil.trim(lines[i]), CharPool.BACK_SLASH);

			if (Validator.isNull(line) || !line.startsWith("com.liferay.") ||
				line.startsWith(bundleSymbolicName)) {

				continue;
			}

			if (bundleSymbolicName.endsWith(".test.util")) {
				String s = bundleSymbolicName.substring(
					0, bundleSymbolicName.length() - 10);

				if (line.startsWith(s + ".test.rule")) {
					continue;
				}
			}

			StringBundler sb = new StringBundler(6);

			sb.append(definitionKey);
			sb.append(" \"");
			sb.append(line);
			sb.append("\" should match Bundle-SymbolicName \"");
			sb.append(bundleSymbolicName);
			sb.append("\"");

			addMessage(
				fileName, sb.toString(),
				getLineNumber(content, matcher.start(2)) + i);
		}
	}

	private void _checkWildCardExportPackage(
			String fileName, String srcDirLocation,
			String wildCardExportPackage, List removedExportPackages)
		throws IOException {

		String wildCardExportPackagePath = StringUtil.replace(
			wildCardExportPackage, CharPool.PERIOD, CharPool.SLASH);

		File exportPackageSrcDir = new File(
			StringBundler.concat(
				srcDirLocation, "/",
				StringUtil.removeSubstring(wildCardExportPackagePath, "/*")));

		if (!exportPackageSrcDir.exists()) {
			addMessage(
				fileName,
				"Unneeded/incorrect Export-Package: " + wildCardExportPackage);

			return;
		}

		Files.walkFileTree(
			exportPackageSrcDir.toPath(),
			new SimpleFileVisitor() {

				@Override
				public FileVisitResult preVisitDirectory(
						Path dirPath, BasicFileAttributes basicFileAttributes)
					throws IOException {

					File dirFile = dirPath.toFile();

					File packageinfoFile = new File(dirFile, "packageinfo");

					if (packageinfoFile.exists()) {
						return FileVisitResult.CONTINUE;
					}

					File[] javaFiles = dirFile.listFiles(
						new FileFilter() {

							@Override
							public boolean accept(File file) {
								if (!file.isFile()) {
									return false;
								}

								String fileName = file.getName();

								if (fileName.endsWith(".java")) {
									return true;
								}

								return false;
							}

						});

					if (ArrayUtil.isEmpty(javaFiles)) {
						return FileVisitResult.CONTINUE;
					}

					String absolutePath = SourceUtil.getAbsolutePath(dirFile);

					for (String removedExportPackage : removedExportPackages) {
						if (!removedExportPackage.endsWith("/*")) {
							if (absolutePath.endsWith(removedExportPackage)) {
								return FileVisitResult.CONTINUE;
							}
						}
						else if (absolutePath.contains(
									StringUtil.removeSubstring(
										removedExportPackage, "/*"))) {

							return FileVisitResult.CONTINUE;
						}
					}

					addMessage(
						fileName, "Added packageinfo in " + absolutePath);

					FileUtil.write(packageinfoFile, "version 1.0.0");

					return FileVisitResult.CONTINUE;
				}

			});
	}

	private File[] _getExportPackageResourcesFiles(
		String srcDirLocation, String exportPackagePath, boolean modulesFile) {

		if (!modulesFile) {
			return new File[0];
		}

		File exportPackageResourcesDir = new File(
			StringBundler.concat(
				srcDirLocation, "/main/resources/", exportPackagePath));

		return exportPackageResourcesDir.listFiles(
			new FileFilter() {

				@Override
				public boolean accept(File file) {
					String fileName = file.getName();

					if (fileName.startsWith(".lfrbuild-") ||
						fileName.equals("packageinfo")) {

						return false;
					}

					return file.isFile();
				}

			});
	}

	private List _getExportPackages(String content) {
		Matcher matcher = _exportsPattern.matcher(content);

		if (!matcher.find()) {
			return Collections.emptyList();
		}

		List exportPackages = new ArrayList<>();

		for (String line : StringUtil.splitLines(matcher.group(3))) {
			line = StringUtil.trim(line);

			if (Validator.isNotNull(line) && !line.equals("\\") &&
				!line.contains(StringPool.SEMICOLON)) {

				exportPackages.add(StringUtil.removeSubstring(line, ",\\"));
			}
		}

		return exportPackages;
	}

	private File[] _getExportPackageSrcFiles(
		String srcDirLocation, String exportPackagePath, boolean modulesFile) {

		File exportPackageSrcDir = null;

		if (modulesFile) {
			exportPackageSrcDir = new File(
				StringBundler.concat(
					srcDirLocation, "/main/java/", exportPackagePath));
		}
		else {
			exportPackageSrcDir = new File(
				StringBundler.concat(srcDirLocation, "/", exportPackagePath));
		}

		return exportPackageSrcDir.listFiles(
			new FileFilter() {

				@Override
				public boolean accept(File file) {
					return file.isFile();
				}

			});
	}

	private String _getSrcDirLocation(String fileName, boolean modulesFile) {
		int i = fileName.lastIndexOf("/");

		String srcDirLocation = fileName.substring(0, i) + "/src/";

		if (modulesFile) {
			File srcMainDir = new File(srcDirLocation + "main/");

			if (srcMainDir.exists()) {
				return srcDirLocation;
			}

			return null;
		}

		File comLiferayDir = new File(srcDirLocation + "com/liferay/");

		if (comLiferayDir.exists()) {
			return srcDirLocation;
		}

		return null;
	}

	private static final String _ALLOWED_EXPORT_PACKAGE_DIR_NAMES_KEY =
		"allowedExportPackageDirNames";

	private static final Pattern _apiOrServiceBundleSymbolicNamePattern =
		Pattern.compile("\\.(api|service)$");
	private static final Pattern _exportContentsPattern = Pattern.compile(
		"\n-exportcontents:(\\\\\n| )((.*?)(\n[^\t]|\\Z))",
		Pattern.DOTALL | Pattern.MULTILINE);
	private static final Pattern _exportsPattern = Pattern.compile(
		"\nExport-Package:(\\\\\n| )((.*?)(\n[^\t]|\\Z))",
		Pattern.DOTALL | Pattern.MULTILINE);

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy