From d04a85725f41b0c5a0b8fc8958b54d4455cca345 Mon Sep 17 00:00:00 2001 From: Andrei Heidelbacher Date: Sat, 3 Mar 2018 16:24:01 +0200 Subject: [PATCH] Added support for type and return type modifiers. --- build.gradle | 6 ++-- .../org/metanalysis/core/model/Utils.kt | 22 +++++++++++++ .../org/metanalysis/core/model/UtilsTest.kt | 27 ++++++++++++++++ .../org/metanalysis/java/ParserContext.kt | 18 +++++++++-- .../main/kotlin/org/metanalysis/java/Utils.kt | 31 +++++++++++++++++-- .../java/JavaParserAnnotationTest.kt | 18 ++++++++--- .../metanalysis/java/JavaParserClassTest.kt | 12 +++++-- .../metanalysis/java/JavaParserEnumTest.kt | 8 +++-- .../metanalysis/java/JavaParserErrorTest.kt | 5 ++- .../java/JavaParserInterfaceTest.kt | 26 ++++++++++++---- 10 files changed, 149 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 21e3d9a2..d208eb4c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ buildscript { ext { - kotlin_version = '1.2.21' - dokka_version = '0.9.15' - jackson_version = '2.9.4' + kotlin_version = '1.2.30' + dokka_version = '0.9.16' + jackson_version = '2.9.4.1' jdt_version = '3.13.100' junit_version = '4.12' } diff --git a/metanalysis-core/src/main/kotlin/org/metanalysis/core/model/Utils.kt b/metanalysis-core/src/main/kotlin/org/metanalysis/core/model/Utils.kt index 52616177..12c5f8c0 100644 --- a/metanalysis-core/src/main/kotlin/org/metanalysis/core/model/Utils.kt +++ b/metanalysis-core/src/main/kotlin/org/metanalysis/core/model/Utils.kt @@ -59,6 +59,28 @@ fun SourceNode.walkSourceTree(): List { return nodes } +private const val typePrefix = "@type:" +private const val returnTypePrefix = "@return:" + +/** Returns the type of this variable, or `null` if not specified. */ +val Variable.type: String? + get() = modifiers + .singleOrNull { it.startsWith(typePrefix) } + ?.removePrefix(typePrefix) + +/** Returns the return type of this function, or `null` if not specified. */ +val Function.returnType: String? + get() = modifiers + .singleOrNull { it.startsWith(returnTypePrefix) } + ?.removePrefix(returnTypePrefix) + +/** Returns the modifier corresponding to the given [type]. */ +fun typeModifierOf(type: String): String = "$typePrefix$type" + +/** Returns the modifier corresponding to the given [returnType]. */ +fun returnTypeModifierOf(returnType: String): String = + "$returnTypePrefix$returnType" + internal fun NodeHashMap.putSourceTree(root: SourceNode) { this += root.walkSourceTree().associateBy(SourceNode::id) } diff --git a/metanalysis-core/src/test/kotlin/org/metanalysis/core/model/UtilsTest.kt b/metanalysis-core/src/test/kotlin/org/metanalysis/core/model/UtilsTest.kt index bb1dd352..557c2d87 100644 --- a/metanalysis-core/src/test/kotlin/org/metanalysis/core/model/UtilsTest.kt +++ b/metanalysis-core/src/test/kotlin/org/metanalysis/core/model/UtilsTest.kt @@ -22,6 +22,7 @@ import org.metanalysis.test.core.model.sourceUnit import org.metanalysis.test.core.model.type import org.metanalysis.test.core.model.variable import kotlin.test.assertEquals +import kotlin.test.assertNull class UtilsTest { @Test fun `test children of unit are equal to entities`() { @@ -60,4 +61,30 @@ class UtilsTest { val node = variable("$path:getVersion(String):name") {} assertEquals(path, node.sourcePath) } + + @Test fun `test return type`() { + val returnType = "int" + val function = function("src/Test.java:getVersion()") { + modifiers("public", returnTypeModifierOf(returnType)) + } + assertEquals(returnType, function.returnType) + } + + @Test fun `test null return type`() { + val function = function("src/Test.java:getVersion()") {} + assertNull(function.returnType) + } + + @Test fun `test variable type`() { + val variableType = "String" + val variable = variable("src/Test.java:VERSION") { + modifiers("public", typeModifierOf(variableType)) + } + assertEquals(variableType, variable.type) + } + + @Test fun `test null variable type`() { + val variable = variable("src/Test.java:VERSION") {} + assertNull(variable.type) + } } diff --git a/metanalysis-java/src/main/kotlin/org/metanalysis/java/ParserContext.kt b/metanalysis-java/src/main/kotlin/org/metanalysis/java/ParserContext.kt index 4ba9a685..0a021455 100644 --- a/metanalysis-java/src/main/kotlin/org/metanalysis/java/ParserContext.kt +++ b/metanalysis-java/src/main/kotlin/org/metanalysis/java/ParserContext.kt @@ -30,6 +30,8 @@ import org.metanalysis.core.model.SourceNode.Companion.ENTITY_SEPARATOR import org.metanalysis.core.model.SourceUnit import org.metanalysis.core.model.Type import org.metanalysis.core.model.Variable +import org.metanalysis.core.model.returnTypeModifierOf +import org.metanalysis.core.model.typeModifierOf internal data class ParserContext( private val unitId: String, @@ -40,8 +42,18 @@ internal data class ParserContext( private fun ASTNode.toSource(): String = source.substring(startPosition, startPosition + length) - private fun ASTNode.modifierSet(): Set = - getModifiers(this).map { it.toSource() }.requireDistinct() + private fun ASTNode.modifierSet(): Set { + val additionalModifier = when (this) { + is AbstractTypeDeclaration -> typeModifier() + is MethodDeclaration -> returnType()?.let(::returnTypeModifierOf) + else -> type()?.let(::typeModifierOf) + } + val modifiers = getModifiers(this) + .map { it.toSource() } + .requireDistinct() + return if (additionalModifier == null) modifiers + else modifiers + additionalModifier + } private fun AbstractTypeDeclaration.supertypeSet(): Set = supertypes().map { it.toSource() }.requireDistinct() @@ -80,7 +92,7 @@ internal data class ParserContext( return Type( id = id, supertypes = node.supertypeSet(), - modifiers = node.modifierSet() + node.typeModifier(), + modifiers = node.modifierSet(), members = members.toSet() ) } diff --git a/metanalysis-java/src/main/kotlin/org/metanalysis/java/Utils.kt b/metanalysis-java/src/main/kotlin/org/metanalysis/java/Utils.kt index 54e8b7e6..3b4d4f2e 100644 --- a/metanalysis-java/src/main/kotlin/org/metanalysis/java/Utils.kt +++ b/metanalysis-java/src/main/kotlin/org/metanalysis/java/Utils.kt @@ -71,10 +71,37 @@ internal fun AnnotationTypeMemberDeclaration.name(): String = name.identifier internal fun EnumConstantDeclaration.name(): String = name.identifier internal fun VariableDeclaration.name(): String = name.identifier +private fun Type?.asString( + extraDimensions: Int = 0, + isVarargs: Boolean = false +): String? { + val suffix = "[]".repeat(extraDimensions) + if (isVarargs) "..." else "" + return if (this == null) null else "$this$suffix" // TODO: stringify type +} + +private fun getBaseType(node: ASTNode): Type? = when (node) { + is FieldDeclaration -> node.type + is AnnotationTypeMemberDeclaration -> node.type + is SingleVariableDeclaration -> node.type + is VariableDeclarationFragment -> getBaseType(node.parent) + else -> null +} + +internal fun ASTNode.type(): String? = when (this) { + is SingleVariableDeclaration -> + getBaseType(this).asString(extraDimensions, isVarargs) + is VariableDeclarationFragment -> + getBaseType(this).asString(extraDimensions) + else -> getBaseType(this).asString() +} + +internal fun MethodDeclaration.returnType(): String? = + returnType2.asString(extraDimensions) + internal fun MethodDeclaration.signature(): String { val parameterList = getParameters(this).joinToString { parameter -> - val postfix = if (parameter.isVarargs) "..." else "" - "${parameter.type}$postfix" + parameter.type() + ?: throw AssertionError("'$parameter' must have specified type!") } return "${name.identifier}($parameterList)" } diff --git a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserAnnotationTest.kt b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserAnnotationTest.kt index eafa0ba0..cc1a308b 100644 --- a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserAnnotationTest.kt +++ b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserAnnotationTest.kt @@ -17,6 +17,7 @@ package org.metanalysis.java import org.junit.Test +import org.metanalysis.core.model.typeModifierOf import kotlin.test.assertEquals class JavaParserAnnotationTest : JavaParserTest() { @@ -43,8 +44,12 @@ class JavaParserAnnotationTest : JavaParserTest() { val expected = sourceUnit { type("AnnotationClass") { modifiers("@interface") - variable("name") {} - variable("version") {} + variable("name") { + modifiers(typeModifierOf("String")) + } + variable("version") { + modifiers(typeModifierOf("int")) + } } } assertEquals(expected, parse(source)) @@ -60,8 +65,13 @@ class JavaParserAnnotationTest : JavaParserTest() { val expected = sourceUnit { type("AnnotationClass") { modifiers("@interface") - variable("name") {} - variable("version") { +"1" } + variable("name") { + modifiers(typeModifierOf("String")) + } + variable("version") { + modifiers(typeModifierOf("int")) + +"1" + } } } assertEquals(expected, parse(source)) diff --git a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserClassTest.kt b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserClassTest.kt index d2a160c6..37f93207 100644 --- a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserClassTest.kt +++ b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserClassTest.kt @@ -17,6 +17,8 @@ package org.metanalysis.java import org.junit.Test +import org.metanalysis.core.model.returnTypeModifierOf +import org.metanalysis.core.model.typeModifierOf import kotlin.test.assertEquals class JavaParserClassTest : JavaParserTest() { @@ -46,9 +48,15 @@ class JavaParserClassTest : JavaParserTest() { modifiers("abstract", "class") function("println(String...)") { - parameter("args") {} + modifiers( + "@Override", + "abstract", + returnTypeModifierOf("void") + ) - modifiers("abstract", "@Override") + parameter("args") { + modifiers(typeModifierOf("String...")) + } } } } diff --git a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserEnumTest.kt b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserEnumTest.kt index 991796b7..fe346ef9 100644 --- a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserEnumTest.kt +++ b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserEnumTest.kt @@ -17,6 +17,8 @@ package org.metanalysis.java import org.junit.Test +import org.metanalysis.core.model.returnTypeModifierOf +import org.metanalysis.core.model.typeModifierOf import kotlin.test.assertEquals class JavaParserEnumTest : JavaParserTest() { @@ -70,12 +72,12 @@ class JavaParserEnumTest : JavaParserTest() { variable("GREEN") {} variable("BLUE") {} variable("format") { - modifiers("public", "final") + modifiers("public", "final", typeModifierOf("String")) +"\"hex\"" } variable("i") { - modifiers("public", "static") + modifiers("public", "static", typeModifierOf("int")) } } } @@ -136,7 +138,7 @@ class JavaParserEnumTest : JavaParserTest() { } function("getCode()") { - modifiers("abstract") + modifiers("abstract", returnTypeModifierOf("String")) } } } diff --git a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserErrorTest.kt b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserErrorTest.kt index 59e4caaa..9fb0d0d1 100644 --- a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserErrorTest.kt +++ b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserErrorTest.kt @@ -17,6 +17,7 @@ package org.metanalysis.java import org.junit.Test +import org.metanalysis.core.model.typeModifierOf import org.metanalysis.core.parsing.Result import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -78,7 +79,9 @@ class JavaParserErrorTest : JavaParserTest() { val expected = sourceUnit { type("Type") { modifiers("class") - variable("i") {} + variable("i") { + modifiers(typeModifierOf("int")) + } } } val result = parseResult(source) diff --git a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserInterfaceTest.kt b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserInterfaceTest.kt index e56ab4cc..2df0e3fe 100644 --- a/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserInterfaceTest.kt +++ b/metanalysis-java/src/test/kotlin/org/metanalysis/java/JavaParserInterfaceTest.kt @@ -17,6 +17,8 @@ package org.metanalysis.java import org.junit.Test +import org.metanalysis.core.model.returnTypeModifierOf +import org.metanalysis.core.model.typeModifierOf import kotlin.test.assertEquals class JavaParserInterfaceTest : JavaParserTest() { @@ -43,8 +45,14 @@ class JavaParserInterfaceTest : JavaParserTest() { val expected = sourceUnit { type("IInterface") { modifiers("interface") - variable("name") { +"null" } - variable("version") { +"1" } + variable("name") { + modifiers(typeModifierOf("String")) + +"null" + } + variable("version") { + modifiers(typeModifierOf("int")) + +"1" + } } } assertEquals(expected, parse(source)) @@ -60,8 +68,12 @@ class JavaParserInterfaceTest : JavaParserTest() { val expected = sourceUnit { type("IInterface") { modifiers("interface") - function("getName()") {} - function("getVersion()") {} + function("getName()") { + modifiers(returnTypeModifierOf("String")) + } + function("getVersion()") { + modifiers(returnTypeModifierOf("int")) + } } } assertEquals(expected, parse(source)) @@ -79,9 +91,11 @@ class JavaParserInterfaceTest : JavaParserTest() { val expected = sourceUnit { type("IInterface") { modifiers("interface") - function("getName()") {} + function("getName()") { + modifiers(returnTypeModifierOf("String")) + } function("getVersion()") { - modifiers("default") + modifiers("default", returnTypeModifierOf("int")) +"{" +"return 1;"