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

com.liferay.source.formatter.parser.JavaClassParser 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.parser;

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.tools.ToolsUtil;
import com.liferay.source.formatter.check.util.JavaSourceUtil;
import com.liferay.source.formatter.check.util.SourceUtil;

import java.io.IOException;

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

/**
 * @author Hugo Huijser
 */
public class JavaClassParser {

	public static List parseAnonymousClasses(String content)
		throws IOException, ParseException {

		return parseAnonymousClasses(content, null, Collections.emptyList());
	}

	public static List parseAnonymousClasses(
			String content, String packageName, List importNames)
		throws IOException, ParseException {

		List anonymousClasses = new ArrayList<>();

		Matcher matcher = _anonymousClassPattern.matcher(content);

		while (matcher.find()) {
			String anonymousClassContent = _getAnonymousClassContent(
				content, matcher.start() + 1,
				StringUtil.equals(matcher.group(1), "<"));

			if (anonymousClassContent != null) {
				anonymousClasses.add(
					_parseJavaClass(
						StringPool.BLANK, packageName, importNames,
						anonymousClassContent,
						SourceUtil.getLineNumber(content, matcher.start()),
						JavaTerm.ACCESS_MODIFIER_PRIVATE, false, false, false,
						false, false, true));
			}
		}

		return anonymousClasses;
	}

	public static JavaClass parseJavaClass(String fileName, String content)
		throws IOException, ParseException {

		String className = JavaSourceUtil.getClassName(fileName);

		Pattern pattern = Pattern.compile(
			StringBundler.concat(
				"\n(public\\s+)?(abstract\\s+)?(final\\s+)?@?",
				"(class|enum|interface)\\s+", className,
				"([<|\\s][^\\{]*)\\{"));

		Matcher matcher = pattern.matcher(content);

		if (!matcher.find()) {
			throw new ParseException("Parsing error");
		}

		int x = matcher.start() + 1;

		int y = x + 1;

		while (true) {
			y = content.lastIndexOf("\n\n", y - 1);

			if (y == -1) {
				throw new ParseException("Parsing error");
			}

			if (ToolsUtil.getLevel(content.substring(y, x)) == 0) {
				break;
			}
		}

		int lineNumber = SourceUtil.getLineNumber(content, y + 2);

		String classContent = content.substring(y + 2);

		boolean isAbstract = false;

		if (matcher.group(2) != null) {
			isAbstract = true;
		}

		boolean isEnum = false;

		boolean isFinal = false;

		if (matcher.group(3) != null) {
			isFinal = true;
		}

		boolean isInterface = false;

		if (matcher.group(4) != null) {
			String token = matcher.group(4);

			if (token.equals("enum")) {
				isEnum = true;
			}
			else if (token.equals("interface")) {
				isInterface = true;
			}
		}

		JavaClass javaClass = _parseJavaClass(
			className, JavaSourceUtil.getPackageName(content),
			JavaSourceUtil.getImportNames(content), classContent, lineNumber,
			JavaTerm.ACCESS_MODIFIER_PUBLIC, isAbstract, isFinal, false, isEnum,
			isInterface, false);

		return _parseExtendsImplements(
			javaClass, StringUtil.trim(matcher.group(5)));
	}

	private static String _getAnonymousClassContent(
		String content, int start, boolean genericClass) {

		int x = start;

		if (genericClass) {
			while (true) {
				x = content.indexOf('>', x + 1);

				if (x == -1) {
					return null;
				}

				int level = ToolsUtil.getLevel(
					content.substring(start, x + 1), "<", ">");

				if (level == 0) {
					break;
				}
			}

			if (!Objects.equals(content.charAt(x + 1), '(')) {
				return null;
			}
		}

		while (true) {
			x = content.indexOf(')', x + 1);

			if (x == -1) {
				return null;
			}

			if (ToolsUtil.getLevel(content.substring(start, x + 1), "(", ")") ==
					0) {

				break;
			}
		}

		String s = StringUtil.trim(content.substring(x + 1));

		if (!s.startsWith("{\n")) {
			return null;
		}

		while (true) {
			x = content.indexOf('}', x + 1);

			if (x == -1) {
				return null;
			}

			String anonymousClassContent = content.substring(start, x + 1);

			if (ToolsUtil.getLevel(anonymousClassContent, "{", "}") == 0) {
				return anonymousClassContent;
			}
		}
	}

	private static String _getClassName(String line) {
		int pos = line.indexOf(" extends ");

		if (pos == -1) {
			pos = line.indexOf(" implements ");
		}

		if (pos == -1) {
			pos = line.indexOf(CharPool.OPEN_CURLY_BRACE);
		}

		if (pos != -1) {
			line = line.substring(0, pos);
		}

		pos = line.indexOf(CharPool.LESS_THAN);

		if (pos != -1) {
			line = line.substring(0, pos);
		}

		line = line.trim();

		pos = line.lastIndexOf(CharPool.SPACE);

		return line.substring(pos + 1);
	}

	private static int _getCommentEndLineNumber(
		String classContent, int lineNumber) {

		while (true) {
			String line = SourceUtil.getLine(classContent, lineNumber);

			if (line.endsWith("*/")) {
				return lineNumber;
			}

			lineNumber++;
		}
	}

	private static String _getConstructorOrMethodName(String line) {
		int x = line.lastIndexOf(CharPool.SPACE);

		return line.substring(x + 1);
	}

	private static JavaTerm _getJavaTerm(
			String packageName, List importNames, String metadata,
			String javaTermContent, int lineNumber)
		throws IOException, ParseException {

		Matcher matcher1 = _javaTermStartLinePattern.matcher(javaTermContent);

		if (!matcher1.find()) {
			return null;
		}

		String startLine = StringUtil.trim(matcher1.group());

		int x = startLine.indexOf(CharPool.OPEN_PARENTHESIS);

		if (x != -1) {
			startLine = startLine.substring(0, x);
		}

		startLine = StringUtil.replace(
			startLine, new String[] {"\t", "\n", " synchronized "},
			new String[] {"", " ", " "});

		javaTermContent = metadata + javaTermContent;

		if (startLine.startsWith("static {")) {
			return new JavaStaticBlock(javaTermContent, lineNumber);
		}

		String accessModifier = JavaTerm.ACCESS_MODIFIER_DEFAULT;

		for (String curAccessModifier : JavaTerm.ACCESS_MODIFIERS) {
			if (startLine.startsWith(curAccessModifier)) {
				accessModifier = curAccessModifier;

				break;
			}
		}

		boolean isAbstract = SourceUtil.containsUnquoted(
			startLine, " abstract ");
		boolean isEnum = SourceUtil.containsUnquoted(startLine, " enum ");
		boolean isFinal = SourceUtil.containsUnquoted(startLine, " final ");
		boolean isInterface = SourceUtil.containsUnquoted(
			startLine, " interface ");
		boolean isStatic = SourceUtil.containsUnquoted(startLine, " static ");

		int y = startLine.indexOf(CharPool.EQUAL);

		if (SourceUtil.containsUnquoted(startLine, " @interface ") ||
			SourceUtil.containsUnquoted(startLine, " class ") ||
			SourceUtil.containsUnquoted(startLine, " enum ") ||
			SourceUtil.containsUnquoted(startLine, " interface ")) {

			JavaClass javaClass = _parseJavaClass(
				_getClassName(startLine), packageName, importNames,
				javaTermContent, lineNumber, accessModifier, isAbstract,
				isFinal, isStatic, isEnum, isInterface, false);

			Pattern pattern = Pattern.compile(
				StringBundler.concat(
					"\\s(class|enum|interface)\\s+", javaClass.getName(),
					"([<|\\s][^\\{]*)\\{"));

			Matcher matcher2 = pattern.matcher(javaTermContent);

			if (matcher2.find()) {
				javaClass = _parseExtendsImplements(
					javaClass, matcher2.group(2));
			}

			return javaClass;
		}

		if (((y > 0) && ((x == -1) || (x > y))) ||
			(startLine.endsWith(StringPool.SEMICOLON) && (x == -1))) {

			return new JavaVariable(
				_getVariableName(startLine), javaTermContent, accessModifier,
				lineNumber, isAbstract, isFinal, isStatic);
		}

		if (x == -1) {
			return null;
		}

		int spaceCount = StringUtil.count(startLine, CharPool.SPACE);

		if (isStatic || (spaceCount > 1) ||
			(accessModifier.equals(JavaTerm.ACCESS_MODIFIER_DEFAULT) &&
			 (spaceCount > 0))) {

			return new JavaMethod(
				_getConstructorOrMethodName(startLine), javaTermContent,
				accessModifier, lineNumber, isAbstract, isFinal, isStatic);
		}

		if ((spaceCount == 1) ||
			(accessModifier.equals(JavaTerm.ACCESS_MODIFIER_DEFAULT) &&
			 (spaceCount == 0))) {

			return new JavaConstructor(
				_getConstructorOrMethodName(startLine), javaTermContent,
				accessModifier, lineNumber, isAbstract, isFinal, isStatic);
		}

		return null;
	}

	private static int _getJavaTermEndLineNumber(
		String classContent, int lineNumber) {

		int x = SourceUtil.getLineStartPos(classContent, lineNumber);

		String s = classContent.substring(x);

		Matcher matcher = _javaTermEndPattern.matcher(s);

		while (matcher.find()) {
			String javaTermContent = s.substring(0, matcher.end());

			if ((ToolsUtil.getLevel(javaTermContent, "(", ")") == 0) &&
				(ToolsUtil.getLevel(javaTermContent, "{", "}") == 0)) {

				return lineNumber + StringUtil.count(javaTermContent, "\n") - 1;
			}
		}

		return -1;
	}

	private static int _getMatchingEndLineNumber(
		String classContent, int lineNumber, String increaseLevelString,
		String decreaseLevelString) {

		int level = 0;

		while (true) {
			level += ToolsUtil.getLevel(
				SourceUtil.getLine(classContent, lineNumber),
				increaseLevelString, decreaseLevelString);

			if (level == 0) {
				return lineNumber;
			}

			lineNumber++;
		}
	}

	private static String _getVariableName(String line) {
		int x = line.indexOf(CharPool.EQUAL);
		int y = line.lastIndexOf(CharPool.SPACE);

		if (x != -1) {
			line = line.substring(0, x);
			line = StringUtil.trim(line);

			y = line.lastIndexOf(CharPool.SPACE);

			return line.substring(y + 1);
		}

		if (line.endsWith(StringPool.SEMICOLON)) {
			return line.substring(y + 1, line.length() - 1);
		}

		return StringPool.BLANK;
	}

	private static JavaClass _parseExtendsImplements(
			JavaClass javaClass, String s)
		throws ParseException {

		if (ToolsUtil.getLevel(s, "<", ">") != 0) {
			throw new ParseException("Parsing error around class declaration");
		}

		outerLoop:
		while (true) {
			int x = s.indexOf("<");

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

			int y = x;

			while (true) {
				y = s.indexOf(">", y + 1);

				if (ToolsUtil.getLevel(s.substring(x, y + 1), "<", ">") == 0) {
					s = StringUtil.trim(s.substring(0, x) + s.substring(y + 1));

					continue outerLoop;
				}
			}
		}

		Matcher matcher = _implementsPattern.matcher(s);

		if (matcher.find()) {
			javaClass.addImplementedClassNames(
				StringUtil.split(s.substring(matcher.end())));

			s = StringUtil.trim(s.substring(0, matcher.start()));
		}

		s = StringUtil.trim(s);

		if (s.startsWith("extends")) {
			javaClass.addExtendedClassNames(StringUtil.split(s.substring(7)));
		}

		return javaClass;
	}

	private static JavaClass _parseJavaClass(
			String className, String packageName, List importNames,
			String classContent, int classLineNumber, String accessModifier,
			boolean isAbstract, boolean isFinal, boolean isStatic,
			boolean isEnum, boolean isInterface, boolean anonymous)
		throws IOException, ParseException {

		JavaClass javaClass = new JavaClass(
			className, packageName, importNames, classContent, accessModifier,
			classLineNumber, isAbstract, isFinal, isStatic, isInterface,
			anonymous);

		int lineNumber = 0;

		int annotationLevel = 0;
		int level = 0;

		if (classContent.startsWith("/*")) {
			while (true) {
				String line = SourceUtil.getLine(classContent, ++lineNumber);

				if (line.endsWith("*/")) {
					break;
				}
			}
		}

		while (true) {
			String line = SourceUtil.getLine(classContent, ++lineNumber);

			annotationLevel += ToolsUtil.getLevel(line);
			level += ToolsUtil.getLevel(line, "{", "}");

			if ((annotationLevel == 0) && (level != 0)) {
				break;
			}
		}

		if (isEnum) {
			while (true) {
				String line = StringUtil.trim(
					SourceUtil.getLine(classContent, ++lineNumber));

				level += ToolsUtil.getLevel(line, "{", "}");

				if (level == 0) {
					return javaClass;
				}

				if (line.endsWith(";") && (level == 1)) {
					break;
				}
			}
		}

		int javaTermLineNumber = -1;

		while (true) {
			String line = StringUtil.trim(
				SourceUtil.getLine(classContent, ++lineNumber));

			if ((line == null) || line.equals("}")) {
				return javaClass;
			}

			if (line.equals(StringPool.BLANK) || line.startsWith("//")) {
				continue;
			}

			if (line.equals("/*") || line.matches("/\\*[^*].*")) {
				lineNumber = _getCommentEndLineNumber(classContent, lineNumber);

				continue;
			}

			if (line.equals("{")) {
				lineNumber = _getMatchingEndLineNumber(
					classContent, lineNumber, "{", "}");

				continue;
			}

			if (javaTermLineNumber == -1) {
				javaTermLineNumber = lineNumber;
			}

			if (line.startsWith("@")) {
				lineNumber = _getMatchingEndLineNumber(
					classContent, lineNumber, "(", ")");
			}
			else if (line.startsWith("/**")) {
				lineNumber = _getCommentEndLineNumber(classContent, lineNumber);
			}
			else {
				int x = SourceUtil.getLineStartPos(
					classContent, javaTermLineNumber);
				int y = SourceUtil.getLineStartPos(classContent, lineNumber);

				String metadata = classContent.substring(x, y);

				int javaTermEndLineNumber = _getJavaTermEndLineNumber(
					classContent, lineNumber);

				if (javaTermEndLineNumber == -1) {
					throw new ParseException(
						"Parsing error at line \"" + StringUtil.trim(line) +
							"\"");
				}

				int z = SourceUtil.getLineStartPos(
					classContent, javaTermEndLineNumber + 1);

				String javaTermContent = classContent.substring(y, z);

				JavaTerm javaTerm = _getJavaTerm(
					packageName, importNames, metadata, javaTermContent,
					classLineNumber + javaTermLineNumber - 1);

				if (javaTerm == null) {
					throw new ParseException(
						"Parsing error at line \"" + StringUtil.trim(line) +
							"\"");
				}

				javaClass.addChildJavaTerm(javaTerm);

				javaTermLineNumber = -1;
				lineNumber = javaTermEndLineNumber;
			}
		}
	}

	private static final Pattern _anonymousClassPattern = Pattern.compile(
		"\\snew [\\w\\.\t\n]+(\\(|\\<)");
	private static final Pattern _implementsPattern = Pattern.compile(
		"(\\A|\\s)implements\\s");
	private static final Pattern _javaTermEndPattern = Pattern.compile(
		"[;}]\\s*?\n");
	private static final Pattern _javaTermStartLinePattern = Pattern.compile(
		".*?[{;]\\s*?\n", Pattern.DOTALL);

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy