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

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

There is a newer version: 1.0.1437
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.StringBundler;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.source.formatter.check.comparator.ElementComparator;
import com.liferay.source.formatter.check.util.SourceUtil;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.tree.DefaultComment;

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

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

		if (fileName.endsWith("/service.xml")) {
			_checkServiceXML(fileName, absolutePath, content);
		}

		return content;
	}

	private void _checkCategoryOrder(
		String fileName, Element entityElement, String entityName) {

		Iterator iterator = entityElement.nodeIterator();

		int previousCategoryIndex = -1;
		String previousCategory = null;

		while (iterator.hasNext()) {
			Node node = (Node)iterator.next();

			if (node instanceof DefaultComment) {
				DefaultComment defaultComment = (DefaultComment)node;

				String comment = defaultComment.asXML();

				if (!comment.matches("")) {
					continue;
				}

				String category = StringUtil.trim(
					comment.substring(4, comment.length() - 3));

				int categoryIndex = _getCategoryIndex(category);

				if (categoryIndex == -1) {
					continue;
				}

				if (previousCategoryIndex > categoryIndex) {
					addMessage(
						fileName,
						StringBundler.concat(
							"Incorrect order '", entityName, "': Category '",
							previousCategory, "' should come after '", category,
							"'"));
				}

				previousCategoryIndex = categoryIndex;
				previousCategory = category;
			}
		}
	}

	private void _checkColumnsThatShouldComeLast(
		String fileName, String absolutePath, Element entityElement,
		String entityName) {

		Iterator iterator = entityElement.nodeIterator();

		boolean otherFields = false;
		String previousColumnName = null;

		while (iterator.hasNext()) {
			Node node = (Node)iterator.next();

			if (node instanceof DefaultComment) {
				DefaultComment defaultComment = (DefaultComment)node;

				if (Objects.equals(
						defaultComment.asXML(), "")) {

					otherFields = true;
				}
				else if (otherFields) {
					return;
				}
			}
			else if (otherFields && (node instanceof Element)) {
				Element element = (Element)node;

				if (!Objects.equals(element.getName(), "column")) {
					continue;
				}

				String columnName = element.attributeValue("name");

				if ((previousColumnName == null) ||
					_isStatusColumnName(columnName)) {

					previousColumnName = columnName;

					continue;
				}

				if (_isStatusColumnName(previousColumnName)) {
					addMessage(
						fileName,
						StringBundler.concat(
							"Incorrect order '", entityName, "#",
							previousColumnName, "'. Status columns should ",
							"come last in the category 'Other fields'."));
				}
				else if (previousColumnName.equals("lastPublishDate")) {
					List allowedIncorrectLastPublishDateEntities =
						getAttributeValues(
							_ALLOWED_INCORRECT_LAST_PUBLISHED_DATE_ENTITIES_KEY,
							absolutePath);

					if (!allowedIncorrectLastPublishDateEntities.contains(
							entityName)) {

						addMessage(
							fileName,
							StringBundler.concat(
								"Incorrect order '", entityName,
								"#lastPublishDate'. 'lastPublishDate' column ",
								"should come last (only followed by status ",
								"columns) in the category 'Other fields'."));
					}
				}

				previousColumnName = columnName;
			}
		}
	}

	private void _checkMVCCEnabled(
		String fileName, String absolutePath, Element element) {

		if (!isAttributeValue(_CHECK_MVCC_ENABLED_KEY, absolutePath) ||
			(element.attributeValue("mvcc-enabled") != null)) {

			return;
		}

		List allowedFileNames = getAttributeValues(
			_ALLOWED_MISSING_MVCC_ENABLED_FILE_NAMES_KEY, absolutePath);

		for (String allowedFileName : allowedFileNames) {
			if (absolutePath.endsWith(allowedFileName)) {
				return;
			}
		}

		addMessage(
			fileName,
			"Attribute 'mvcc-enabled' should always be set in service.xml. " +
				"Preferably, set 'mvcc-enabled=\"true\"'.");
	}

	private void _checkServiceXML(
		String fileName, String absolutePath, String content) {

		Document document = SourceUtil.readXML(content);

		if (document == null) {
			return;
		}

		Element rootElement = document.getRootElement();

		_checkMVCCEnabled(fileName, absolutePath, rootElement);

		ServiceReferenceElementComparator serviceReferenceElementComparator =
			new ServiceReferenceElementComparator("entity");

		for (Element entityElement :
				(List)rootElement.elements("entity")) {

			if (GetterUtil.getBoolean(
					entityElement.attributeValue("deprecated"))) {

				continue;
			}

			String entityName = entityElement.attributeValue("name");

			_checkCategoryOrder(fileName, entityElement, entityName);

			_checkColumnsThatShouldComeLast(
				fileName, absolutePath, entityElement, entityName);

			List columnNames = new ArrayList<>();

			for (Element columnElement :
					(List)entityElement.elements("column")) {

				columnNames.add(columnElement.attributeValue("name"));
			}

			if (!columnNames.isEmpty() && !columnNames.contains("companyId")) {
				List allowedEntityNames = getAttributeValues(
					_ALLOWED_MISSING_COMPANY_ID_ENTITY_NAMES_KEY, absolutePath);

				if (!allowedEntityNames.isEmpty() &&
					!allowedEntityNames.contains(entityName)) {

					addMessage(
						fileName,
						StringBundler.concat(
							"Entity '", entityName,
							"' should have a column named 'companyId', See ",
							"LPS-107076"));
				}
			}

			ServiceFinderColumnElementComparator
				serviceFinderColumnElementComparator =
					new ServiceFinderColumnElementComparator(columnNames);

			if (!isExcludedPath(
					_SERVICE_FINDER_COLUMN_SORT_EXCLUDES, absolutePath,
					entityName)) {

				for (Element finderElement :
						(List)entityElement.elements("finder")) {

					String finderName = finderElement.attributeValue("name");

					checkElementOrder(
						fileName, finderElement, "finder-column",
						entityName + "#" + finderName,
						serviceFinderColumnElementComparator);
				}
			}

			checkElementOrder(
				fileName, entityElement, "finder", entityName,
				new ServiceFinderElementComparator(columnNames));
			checkElementOrder(
				fileName, entityElement, "reference", entityName,
				serviceReferenceElementComparator);
		}

		checkElementOrder(
			fileName, rootElement, "entity", null, new ElementComparator());
		checkElementOrder(
			fileName, rootElement.element("exceptions"), "exception", null,
			new ServiceExceptionElementComparator());
	}

	private int _getCategoryIndex(String category) {
		if (StringUtil.equalsIgnoreCase(category, "PK fields")) {
			return 1;
		}

		if (StringUtil.equalsIgnoreCase(category, "Resource")) {
			return 2;
		}

		if (StringUtil.equalsIgnoreCase(category, "Group instance")) {
			return 3;
		}

		if (StringUtil.equalsIgnoreCase(category, "Audit fields")) {
			return 4;
		}

		if (StringUtil.equalsIgnoreCase(category, "Other fields")) {
			return 5;
		}

		if (StringUtil.equalsIgnoreCase(category, "Localized entity")) {
			return 6;
		}

		if (StringUtil.equalsIgnoreCase(category, "Relationships")) {
			return 7;
		}

		if (StringUtil.equalsIgnoreCase(category, "Order")) {
			return 8;
		}

		if (StringUtil.equalsIgnoreCase(category, "Finder methods")) {
			return 9;
		}

		if (StringUtil.equalsIgnoreCase(category, "References")) {
			return 10;
		}

		return -1;
	}

	private boolean _isStatusColumnName(String columnName) {
		if ((columnName != null) &&
			(columnName.equals("status") ||
			 columnName.equals("statusByUserId") ||
			 columnName.equals("statusByUserName") ||
			 columnName.equals("statusDate") ||
			 columnName.equals("statusMessage"))) {

			return true;
		}

		return false;
	}

	private static final String
		_ALLOWED_INCORRECT_LAST_PUBLISHED_DATE_ENTITIES_KEY =
			"allowedIncorrectLastPublishDateEntities";

	private static final String _ALLOWED_MISSING_COMPANY_ID_ENTITY_NAMES_KEY =
		"allowedMissingCompanyIdEntityNames";

	private static final String _ALLOWED_MISSING_MVCC_ENABLED_FILE_NAMES_KEY =
		"allowedMissingMVVCEnabledFileNames";

	private static final String _CHECK_MVCC_ENABLED_KEY = "checkMVCCEnabled";

	private static final String _SERVICE_FINDER_COLUMN_SORT_EXCLUDES =
		"service.finder.column.sort.excludes";

	private class ServiceExceptionElementComparator extends ElementComparator {

		@Override
		public String getElementName(Element exceptionElement) {
			return exceptionElement.getStringValue();
		}

	}

	private class ServiceFinderColumnElementComparator
		extends ElementComparator {

		public ServiceFinderColumnElementComparator(List columnNames) {
			_columnNames = columnNames;
		}

		@Override
		public int compare(
			Element finderColumnElement1, Element finderColumnElement2) {

			String finderColumnName1 = finderColumnElement1.attributeValue(
				"name");
			String finderColumnName2 = finderColumnElement2.attributeValue(
				"name");

			int index1 = _columnNames.indexOf(finderColumnName1);
			int index2 = _columnNames.indexOf(finderColumnName2);

			if ((index1 == -1) || (index2 == -1)) {
				return 0;
			}

			return index1 - index2;
		}

		private final List _columnNames;

	}

	private class ServiceFinderElementComparator extends ElementComparator {

		public ServiceFinderElementComparator(List columnNames) {
			_columnNames = columnNames;
		}

		@Override
		public int compare(Element finderElement1, Element finderElement2) {
			List finderColumnElements1 = finderElement1.elements(
				"finder-column");
			List finderColumnElements2 = finderElement2.elements(
				"finder-column");

			int finderColumnCount1 = finderColumnElements1.size();
			int finderColumnCount2 = finderColumnElements2.size();

			if (finderColumnCount1 != finderColumnCount2) {
				return finderColumnCount1 - finderColumnCount2;
			}

			for (int i = 0; i < finderColumnCount1; i++) {
				Element finderColumnElement1 = finderColumnElements1.get(i);
				Element finderColumnElement2 = finderColumnElements2.get(i);

				String finderColumnName1 = finderColumnElement1.attributeValue(
					"name");
				String finderColumnName2 = finderColumnElement2.attributeValue(
					"name");

				int index1 = _columnNames.indexOf(finderColumnName1);
				int index2 = _columnNames.indexOf(finderColumnName2);

				if (index1 != index2) {
					return index1 - index2;
				}
			}

			String finderName1 = finderElement1.attributeValue("name");
			String finderName2 = finderElement2.attributeValue("name");

			int startsWithWeight = StringUtil.startsWithWeight(
				finderName1, finderName2);

			String strippedFinderName1 = finderName1.substring(
				startsWithWeight);

			if (strippedFinderName1.startsWith("Gt") ||
				strippedFinderName1.startsWith("Like") ||
				strippedFinderName1.startsWith("Lt") ||
				strippedFinderName1.startsWith("Not")) {

				String strippedFinderName2 = finderName2.substring(
					startsWithWeight);

				if (!strippedFinderName2.startsWith("Gt") &&
					!strippedFinderName2.startsWith("Like") &&
					!strippedFinderName2.startsWith("Lt") &&
					!strippedFinderName2.startsWith("Not")) {

					return 1;
				}

				return strippedFinderName1.compareTo(strippedFinderName2);
			}

			return 0;
		}

		private final List _columnNames;

	}

	private class ServiceReferenceElementComparator extends ElementComparator {

		public ServiceReferenceElementComparator(String nameAttribute) {
			super(nameAttribute);
		}

		@Override
		public int compare(
			Element referenceElement1, Element referenceElement2) {

			String packageName1 = referenceElement1.attributeValue(
				"package-path");
			String packageName2 = referenceElement2.attributeValue(
				"package-path");

			if (!packageName1.equals(packageName2)) {
				return packageName1.compareToIgnoreCase(packageName2);
			}

			String entityName1 = referenceElement1.attributeValue("entity");
			String entityName2 = referenceElement2.attributeValue("entity");

			return entityName1.compareToIgnoreCase(entityName2);
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy