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

com.liferay.portal.tools.ToolsUtil Maven / Gradle / Ivy

There is a newer version: 7.4.3.112-ga112
Show newest version
/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.portal.tools;

import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.xml.SAXReaderFactory;

import de.hunsicker.io.FileFormat;
import de.hunsicker.jalopy.Jalopy;
import de.hunsicker.jalopy.storage.Convention;
import de.hunsicker.jalopy.storage.ConventionKeys;
import de.hunsicker.jalopy.storage.Environment;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

import java.net.URL;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * @author Brian Wing Shun Chan
 * @author Charles May
 * @author Alexander Chow
 * @author Harry Mark
 * @author Tariq Dweik
 * @author Glenn Powell
 * @author Raymond Augé
 * @author Prashant Dighe
 * @author Shuyang Zhou
 * @author James Lefeu
 * @author Miguel Pastor
 * @author Cody Hoag
 * @author James Hinkey
 * @author Hugo Huijser
 */
public class ToolsUtil {

	public static final String AUTHOR = "Brian Wing Shun Chan";

	public static final int PLUGINS_MAX_DIR_LEVEL = 3;

	public static final int PORTAL_MAX_DIR_LEVEL = 7;

	public static String getContent(String fileName) throws Exception {
		Document document = _getContentDocument(fileName);

		Element rootElement = document.getRootElement();

		Element authorElement = null;
		Element namespaceElement = null;
		Map entityElements = new TreeMap<>();
		Map exceptionElements = new TreeMap<>();

		List elements = rootElement.elements();

		for (Element element : elements) {
			String elementName = element.getName();

			if (elementName.equals("author")) {
				element.detach();

				if (authorElement != null) {
					throw new IllegalArgumentException(
						"There can only be one author element");
				}

				authorElement = element;
			}
			else if (elementName.equals("namespace")) {
				element.detach();

				if (namespaceElement != null) {
					throw new IllegalArgumentException(
						"There can only be one namespace element");
				}

				namespaceElement = element;
			}
			else if (elementName.equals("entity")) {
				element.detach();

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

				entityElements.put(StringUtil.toLowerCase(name), element);
			}
			else if (elementName.equals("exceptions")) {
				element.detach();

				List exceptionElementsList = element.elements(
					"exception");

				for (Element exceptionElement : exceptionElementsList) {
					exceptionElement.detach();

					exceptionElements.put(
						exceptionElement.getText(), exceptionElement);
				}
			}
		}

		if (authorElement != null) {
			rootElement.add(authorElement);
		}

		if (namespaceElement == null) {
			throw new IllegalArgumentException(
				"The namespace element is required");
		}

		rootElement.add(namespaceElement);

		_addElements(rootElement, entityElements);

		if (!exceptionElements.isEmpty()) {
			Element exceptionsElement = rootElement.addElement("exceptions");

			_addElements(exceptionsElement, exceptionElements);
		}

		return document.asXML();
	}

	public static int getLevel(String s) {
		return getLevel(
			s, new String[] {StringPool.OPEN_PARENTHESIS},
			new String[] {StringPool.CLOSE_PARENTHESIS}, 0);
	}

	public static int getLevel(
		String s, String increaseLevelString, String decreaseLevelString) {

		return getLevel(
			s, new String[] {increaseLevelString},
			new String[] {decreaseLevelString}, 0);
	}

	public static int getLevel(
		String s, String[] increaseLevelStrings,
		String[] decreaseLevelStrings) {

		return getLevel(s, increaseLevelStrings, decreaseLevelStrings, 0);
	}

	public static int getLevel(
		String s, String[] increaseLevelStrings, String[] decreaseLevelStrings,
		int startLevel) {

		int level = startLevel;

		for (String increaseLevelString : increaseLevelStrings) {
			level = _adjustLevel(level, s, increaseLevelString, 1);
		}

		for (String decreaseLevelString : decreaseLevelStrings) {
			level = _adjustLevel(level, s, decreaseLevelString, -1);
		}

		return level;
	}

	public static String getPackagePath(File file) {
		String fileName = StringUtil.replace(
			file.toString(), CharPool.BACK_SLASH, CharPool.SLASH);

		return getPackagePath(fileName);
	}

	public static String getPackagePath(String fileName) {
		int x = Math.max(
			fileName.lastIndexOf("/com/"), fileName.lastIndexOf("/org/"));
		int y = fileName.lastIndexOf(CharPool.SLASH);

		String packagePath = fileName.substring(x + 1, y);

		return StringUtil.replace(packagePath, CharPool.SLASH, CharPool.PERIOD);
	}

	public static boolean isInsideQuotes(String s, int pos) {
		return isInsideQuotes(s, pos, true);
	}

	public static boolean isInsideQuotes(
		String s, int pos, boolean allowEscapedQuotes) {

		int start = s.lastIndexOf(CharPool.NEW_LINE, pos);

		if (start == -1) {
			start = 0;
		}

		int end = s.indexOf(CharPool.NEW_LINE, pos);

		if (end == -1) {
			end = s.length();
		}

		String line = s.substring(start, end);

		pos -= start;

		char delimeter = CharPool.SPACE;
		boolean insideQuotes = false;

		for (int i = 0; i < line.length(); i++) {
			char c = line.charAt(i);

			if (insideQuotes) {
				if (c == delimeter) {
					if (!allowEscapedQuotes) {
						insideQuotes = false;
					}
					else {
						int precedingBackSlashCount = 0;

						for (int j = i - 1; j >= 0; j--) {
							if (line.charAt(j) == CharPool.BACK_SLASH) {
								precedingBackSlashCount += 1;
							}
							else {
								break;
							}
						}

						if ((precedingBackSlashCount == 0) ||
							((precedingBackSlashCount % 2) == 0)) {

							insideQuotes = false;
						}
					}
				}
			}
			else if ((c == CharPool.APOSTROPHE) || (c == CharPool.QUOTE)) {
				delimeter = c;
				insideQuotes = true;
			}

			if (pos == i) {
				return insideQuotes;
			}
		}

		return false;
	}

	/**
	 * @deprecated As of Judson (7.1.x), replaced by {@link
	 *             #stripFullyQualifiedClassNames(String, String)}
	 */
	@Deprecated
	public static String stripFullyQualifiedClassNames(String content)
		throws IOException {

		return stripFullyQualifiedClassNames(content, null);
	}

	public static String stripFullyQualifiedClassNames(
			String content, String packagePath)
		throws IOException {

		String imports = JavaImportsFormatter.getImports(content);

		return stripFullyQualifiedClassNames(content, imports, packagePath);
	}

	public static String stripFullyQualifiedClassNames(
			String content, String imports, String packagePath)
		throws IOException {

		if (Validator.isNull(content) || Validator.isNull(imports)) {
			return content;
		}

		String afterImportsContent = null;

		int pos = content.lastIndexOf("\nimport ");

		if (pos == -1) {
			afterImportsContent = content;
		}
		else {
			pos = content.indexOf("\n", pos + 1);

			afterImportsContent = content.substring(pos);
		}

		afterImportsContent = _stripFullyQualifiedClassNames(
			imports, afterImportsContent, packagePath);
		afterImportsContent = _stripFullyQualifiedClassNames(
			imports, afterImportsContent, "java.lang");

		try (UnsyncBufferedReader unsyncBufferedReader =
				new UnsyncBufferedReader(new UnsyncStringReader(imports))) {

			String line = null;

			while ((line = unsyncBufferedReader.readLine()) != null) {
				int x = line.indexOf("import ");

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

				String importPackageAndClassName = line.substring(
					x + 7, line.lastIndexOf(StringPool.SEMICOLON));

				if (importPackageAndClassName.contains(StringPool.STAR)) {
					continue;
				}

				while (true) {
					x = afterImportsContent.indexOf(
						importPackageAndClassName, x + 1);

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

					char previousChar = afterImportsContent.charAt(x - 1);

					if (Character.isLetterOrDigit(previousChar) ||
						(previousChar == CharPool.PERIOD)) {

						continue;
					}

					char nextChar = afterImportsContent.charAt(
						x + importPackageAndClassName.length());

					if (Character.isLetterOrDigit(nextChar)) {
						continue;
					}

					int y = afterImportsContent.lastIndexOf(
						CharPool.NEW_LINE, x);

					if (y == -1) {
						y = 0;
					}

					String s = afterImportsContent.substring(y, x + 1);

					if (isInsideQuotes(s, x - y)) {
						continue;
					}

					s = StringUtil.trim(s);

					if (s.startsWith("//")) {
						continue;
					}

					String importClassName =
						importPackageAndClassName.substring(
							importPackageAndClassName.lastIndexOf(
								StringPool.PERIOD) + 1);

					afterImportsContent = StringUtil.replaceFirst(
						afterImportsContent, importPackageAndClassName,
						importClassName, x);
				}
			}

			if (pos == -1) {
				return afterImportsContent;
			}

			return content.substring(0, pos) + afterImportsContent;
		}
	}

	public static void writeFile(
			File file, String content, Set modifiedFileNames)
		throws IOException {

		writeFile(file, content, AUTHOR, modifiedFileNames);
	}

	public static void writeFile(
			File file, String content, String author,
			Map jalopySettings, Set modifiedFileNames)
		throws IOException {

		writeFile(
			file, content, null, author, jalopySettings, modifiedFileNames,
			null);
	}

	public static void writeFile(
			File file, String content, String author,
			Map jalopySettings, Set modifiedFileNames,
			String packagePath)
		throws IOException {

		writeFile(
			file, content, null, author, jalopySettings, modifiedFileNames,
			packagePath);
	}

	public static void writeFile(
			File file, String content, String author,
			Set modifiedFileNames)
		throws IOException {

		writeFile(file, content, author, null, modifiedFileNames);
	}

	public static void writeFile(
			File file, String content, String header, String author,
			Map jalopySettings, Set modifiedFileNames,
			String packagePath)
		throws IOException {

		if (!file.exists()) {
			_write(file, StringPool.BLANK);
		}

		if (Validator.isNull(packagePath)) {
			packagePath = getPackagePath(file);
		}

		String className = file.getName();

		className = className.substring(0, className.length() - 5);

		ImportsFormatter importsFormatter = new JavaImportsFormatter();

		content = importsFormatter.format(content, packagePath, className);

		// Beautify

		String jalopyIgnoreStart = "/* @start-ignoring-jalopy@ */";

		int start = content.indexOf(jalopyIgnoreStart);

		String jalopyIgnoreEnd = "/* @stop-ignoring-jalopy@ */";

		String jalopyIgnoreBody = null;

		if (start != -1) {
			start += jalopyIgnoreStart.length();

			int end = content.indexOf(jalopyIgnoreEnd);

			if (end != -1) {
				jalopyIgnoreBody = content.substring(start, end);

				content = content.substring(0, start) + content.substring(end);
			}
		}

		StringBuffer sb = new StringBuffer();

		Jalopy jalopy = new Jalopy();

		jalopy.setFileFormat(FileFormat.UNIX);
		jalopy.setInput(
			new ByteArrayInputStream(content.getBytes()), file.getPath());
		jalopy.setOutput(sb);

		File jalopyXmlFile = new File("tools/jalopy.xml");

		if (!jalopyXmlFile.exists()) {
			jalopyXmlFile = new File("../tools/jalopy.xml");
		}

		if (!jalopyXmlFile.exists()) {
			jalopyXmlFile = new File("misc/jalopy.xml");
		}

		if (!jalopyXmlFile.exists()) {
			jalopyXmlFile = new File("../misc/jalopy.xml");
		}

		if (!jalopyXmlFile.exists()) {
			jalopyXmlFile = new File("../../misc/jalopy.xml");
		}

		if (jalopyXmlFile.exists()) {
			Jalopy.setConvention(jalopyXmlFile);
		}
		else {
			URL url = _readJalopyXmlFromClassLoader();

			Jalopy.setConvention(url);
		}

		if (jalopySettings == null) {
			jalopySettings = new HashMap<>();
		}

		Environment env = Environment.getInstance();

		// Author

		author = GetterUtil.getString(
			(String)jalopySettings.get("author"), author);

		env.set("author", author);

		// Fail on format error

		boolean failOnFormatError = MapUtil.getBoolean(
			jalopySettings, "failOnFormatError");

		// File name

		env.set("fileName", file.getName());

		Convention convention = Convention.getInstance();

		if (Validator.isNotNull(header)) {
			convention.put(ConventionKeys.HEADER_TEXT, header);
		}

		String classMask = "/**\n * @author $author$\n*/";

		convention.put(
			ConventionKeys.COMMENT_JAVADOC_TEMPLATE_CLASS,
			env.interpolate(classMask));

		convention.put(
			ConventionKeys.COMMENT_JAVADOC_TEMPLATE_INTERFACE,
			env.interpolate(classMask));

		boolean formatSuccess = jalopy.format();

		String newContent = sb.toString();

		if (jalopyIgnoreBody != null) {
			start = newContent.indexOf(jalopyIgnoreStart);

			start = newContent.lastIndexOf('\n', start);

			int end = newContent.indexOf(jalopyIgnoreEnd);

			newContent =
				newContent.substring(0, start) + jalopyIgnoreBody +
					newContent.substring(end + jalopyIgnoreEnd.length());
		}

		// Remove double blank lines after the package or last import

		newContent = newContent.replaceFirst(
			"(?m)^[ \t]*((?:package|import) .*;)\\s*^[ \t]*/\\*\\*",
			"$1\n\n/**");

		/*// Remove blank lines after try {

		newContent = StringUtil.replace(newContent, "try {\n\n", "try {\n");

		// Remove blank lines after ) {

		newContent = StringUtil.replace(newContent, ") {\n\n", ") {\n");

		// Remove blank lines empty braces { }

		newContent = StringUtil.replace(newContent, "\n\n\t}", "\n\t}");

		// Add space to last }

		newContent =
			newContent.substring(0, newContent.length() - 2) + "\n\n}";*/

		writeFileRaw(file, newContent, modifiedFileNames);

		if (failOnFormatError && !formatSuccess) {
			throw new IOException("Unable to beautify " + file);
		}
	}

	public static void writeFileRaw(
			File file, String content, Set modifiedFileNames)
		throws IOException {

		// Write file if and only if the file has changed

		if (!file.exists() || !content.equals(_read(file))) {
			_write(file, content);

			if (modifiedFileNames != null) {
				modifiedFileNames.add(file.getAbsolutePath());
			}

			System.out.println("Writing " + file);
		}
	}

	private static void _addElements(
		Element element, Map elements) {

		for (Map.Entry entry : elements.entrySet()) {
			Element childElement = entry.getValue();

			element.add(childElement);
		}
	}

	private static int _adjustLevel(
		int level, String text, String s, int diff) {

		boolean multiLineComment = false;

		forLoop:
		for (String line : StringUtil.splitLines(text)) {
			line = StringUtil.trim(line);

			if (line.startsWith("/*")) {
				multiLineComment = true;
			}

			if (multiLineComment) {
				if (line.endsWith("*/")) {
					multiLineComment = false;
				}

				continue;
			}

			if (line.startsWith("//") || line.startsWith("*")) {
				continue;
			}

			int x = -1;

			while (true) {
				x = line.indexOf(s, x + 1);

				if (x == -1) {
					continue forLoop;
				}

				if (!isInsideQuotes(line, x)) {
					level += diff;
				}
			}
		}

		return level;
	}

	private static Document _getContentDocument(String fileName)
		throws Exception {

		SAXReader saxReader = _getSAXReader();

		Document document = saxReader.read(new File(fileName));

		Element rootElement = document.getRootElement();

		List elements = rootElement.elements();

		for (Element element : elements) {
			String elementName = element.getName();

			if (!elementName.equals("service-builder-import")) {
				continue;
			}

			element.detach();

			String dirName = fileName.substring(
				0, fileName.lastIndexOf(StringPool.SLASH) + 1);
			String serviceBuilderImportFileName = element.attributeValue(
				"file");

			Document serviceBuilderImportDocument = _getContentDocument(
				dirName + serviceBuilderImportFileName);

			Element serviceBuilderImportRootElement =
				serviceBuilderImportDocument.getRootElement();

			List serviceBuilderImportElements =
				serviceBuilderImportRootElement.elements();

			for (Element serviceBuilderImportElement :
					serviceBuilderImportElements) {

				serviceBuilderImportElement.detach();

				rootElement.add(serviceBuilderImportElement);
			}
		}

		return document;
	}

	private static SAXReader _getSAXReader() {
		return SAXReaderFactory.getSAXReader(null, false, false);
	}

	private static String _read(File file) throws IOException {
		String s = new String(
			Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);

		return StringUtil.replace(
			s, StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE);
	}

	private static URL _readJalopyXmlFromClassLoader() {
		ClassLoader classLoader = ToolsUtil.class.getClassLoader();

		URL url = classLoader.getResource("jalopy.xml");

		if (url == null) {
			throw new RuntimeException(
				"Unable to load jalopy.xml from the class loader");
		}

		return url;
	}

	private static String _stripFullyQualifiedClassNames(
		String imports, String afterImportsContent, String packagePath) {

		Pattern pattern1 = Pattern.compile(
			"\n(.*)" + StringUtil.replace(packagePath, CharPool.PERIOD, "\\.") +
				"\\.([A-Z]\\w+)\\W");

		outerLoop:
		while (true) {
			Matcher matcher1 = pattern1.matcher(afterImportsContent);

			while (matcher1.find()) {
				String lineStart = StringUtil.trimLeading(matcher1.group(1));

				if (lineStart.contains("//") ||
					isInsideQuotes(afterImportsContent, matcher1.start(2))) {

					continue;
				}

				String className = matcher1.group(2);

				Pattern pattern2 = Pattern.compile(
					"import [\\w.]+\\." + className + ";");

				Matcher matcher2 = pattern2.matcher(imports);

				if (matcher2.find()) {
					continue;
				}

				afterImportsContent = StringUtil.replaceFirst(
					afterImportsContent, packagePath + ".", StringPool.BLANK,
					matcher1.start());

				continue outerLoop;
			}

			break;
		}

		return afterImportsContent;
	}

	private static void _write(File file, String s) throws IOException {
		Path path = file.toPath();

		Files.createDirectories(path.getParent());

		Files.write(path, s.getBytes(StandardCharsets.UTF_8));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy