Refactor and fix script annotation processing
This commit is contained in:
@@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common
|
|
||||||
|
|
||||||
import kotlin.reflect.*
|
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
|
||||||
|
|
||||||
fun tryConstructClassFromStringArgs(scriptClass: Class<*>, scriptArgs: List<String>): Any? {
|
|
||||||
|
|
||||||
try {
|
|
||||||
return scriptClass.getConstructor(Array<String>::class.java).newInstance(scriptArgs.toTypedArray())
|
|
||||||
}
|
|
||||||
catch (e: NoSuchMethodException) {
|
|
||||||
for (ctor in scriptClass.kotlin.constructors) {
|
|
||||||
val mapping = tryCreateCallableMapping(ctor, scriptArgs, StringArgsConverter())
|
|
||||||
if (mapping != null) {
|
|
||||||
try {
|
|
||||||
return ctor.callBy(mapping)
|
|
||||||
}
|
|
||||||
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun tryCreateCallableMapping(callable: KCallable<*>, args: List<Any?>): Map<KParameter, Any?>? =
|
|
||||||
tryCreateCallableMapping(callable, args, AnyArgsConverter())
|
|
||||||
|
|
||||||
|
|
||||||
private interface ArgsConverter<T> {
|
|
||||||
|
|
||||||
data class Result(val v: Any?, val argsConsumed: Int)
|
|
||||||
|
|
||||||
fun convert(parameter: KParameter, args: List<T>, startArgIndex: Int): Result?
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T: Any?> tryCreateCallableMapping(callable: KCallable<*>, args: List<T>, converter: ArgsConverter<T>): Map<KParameter, Any?>? {
|
|
||||||
|
|
||||||
var argIdx = 0
|
|
||||||
val res = mutableMapOf<KParameter, Any?>()
|
|
||||||
for (par in callable.parameters) {
|
|
||||||
val (compatibleArg, argsConsumed) = converter.convert(par, args, argIdx) ?: return null
|
|
||||||
if (argsConsumed > 0) {
|
|
||||||
res.put(par, compatibleArg)
|
|
||||||
argIdx += argsConsumed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StringArgsConverter : ArgsConverter<String> {
|
|
||||||
|
|
||||||
override fun convert(parameter: KParameter, args: List<String>, startArgIndex: Int): ArgsConverter.Result? {
|
|
||||||
|
|
||||||
fun convertPrimitive(type: KType?, arg: String): Any? =
|
|
||||||
when (type?.classifier) {
|
|
||||||
String::class -> arg
|
|
||||||
Int::class -> arg.toInt()
|
|
||||||
Long::class -> arg.toLong()
|
|
||||||
Short::class -> arg.toShort()
|
|
||||||
Byte::class -> arg.toByte()
|
|
||||||
Char::class -> arg[0]
|
|
||||||
Float::class -> arg.toFloat()
|
|
||||||
Double::class -> arg.toDouble()
|
|
||||||
Boolean::class -> arg.toBoolean()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertArray(type: KType?, args: List<String>): Any? =
|
|
||||||
when (type?.classifier) {
|
|
||||||
String::class -> args.toTypedArray()
|
|
||||||
Int::class -> args.map(String::toInt).toTypedArray()
|
|
||||||
Long::class -> args.map(String::toLong).toTypedArray()
|
|
||||||
Short::class -> args.map(String::toShort).toTypedArray()
|
|
||||||
Byte::class -> args.map(String::toByte).toTypedArray()
|
|
||||||
Char::class -> args.map { it[0] }.toTypedArray()
|
|
||||||
Float::class -> args.map(String::toFloat).toTypedArray()
|
|
||||||
Double::class -> args.map(String::toDouble).toTypedArray()
|
|
||||||
Boolean::class -> args.map(String::toBoolean).toTypedArray()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (startArgIndex >= args.size && parameter.isOptional)
|
|
||||||
return ArgsConverter.Result(null, 0)
|
|
||||||
|
|
||||||
if (startArgIndex < args.size) {
|
|
||||||
val primArgCandidate = convertPrimitive(parameter.type, args[startArgIndex])
|
|
||||||
if (primArgCandidate != null)
|
|
||||||
return ArgsConverter.Result(primArgCandidate, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parameter.type.classifier as? KClass<*>)?.qualifiedName == Array<Any>::class.qualifiedName) {
|
|
||||||
val arrCompType = parameter.type.arguments.getOrNull(0)?.type
|
|
||||||
val arrayArgCandidate = convertArray(arrCompType, args.drop(startArgIndex))
|
|
||||||
if (arrayArgCandidate != null)
|
|
||||||
return ArgsConverter.Result(arrayArgCandidate, args.size - startArgIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e: NumberFormatException) {}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AnyArgsConverter : ArgsConverter<Any?> {
|
|
||||||
override fun convert(parameter: KParameter, args: List<Any?>, startArgIndex: Int): ArgsConverter.Result? {
|
|
||||||
|
|
||||||
fun convertSingle(type: KType, arg: Any?): Any? = when {
|
|
||||||
type.classifier == arg?.javaClass?.kotlin -> arg
|
|
||||||
type.jvmErasure.java.isAssignableFrom(arg?.javaClass) -> arg
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startArgIndex >= args.size && parameter.isOptional)
|
|
||||||
return ArgsConverter.Result(null, 0)
|
|
||||||
|
|
||||||
if (startArgIndex < args.size) {
|
|
||||||
val primArgCandidate = convertSingle(parameter.type, args[startArgIndex])
|
|
||||||
if (primArgCandidate != null)
|
|
||||||
return ArgsConverter.Result(primArgCandidate, 1)
|
|
||||||
}
|
|
||||||
// TODO: add vararg support
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common.repl
|
package org.jetbrains.kotlin.cli.common.repl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cli.common.tryCreateCallableMapping
|
import org.jetbrains.kotlin.utils.tryCreateCallableMapping
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
|||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||||
|
|||||||
+1
-2
@@ -101,9 +101,8 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
|||||||
// TODO: consider advanced matching using semantic similar to actual resolving
|
// TODO: consider advanced matching using semantic similar to actual resolving
|
||||||
acceptedAnnotations.find { ann ->
|
acceptedAnnotations.find { ann ->
|
||||||
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
|
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
|
||||||
}?.let { KtAnnotationWrapper(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
|
}?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
|
||||||
}
|
}
|
||||||
.map { it.getProxy(classLoader) }
|
|
||||||
}
|
}
|
||||||
catch (ex: Throwable) {
|
catch (ex: Throwable) {
|
||||||
logClassloadingError(ex)
|
logClassloadingError(ex)
|
||||||
|
|||||||
+19
-44
@@ -23,10 +23,9 @@ import org.jetbrains.kotlin.psi.KtUserType
|
|||||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import java.lang.reflect.InvocationHandler
|
import org.jetbrains.kotlin.utils.tryCreateCallableMappingFromNamedArgs
|
||||||
import java.lang.reflect.Method
|
|
||||||
import java.lang.reflect.Proxy
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KParameter
|
||||||
import kotlin.reflect.primaryConstructor
|
import kotlin.reflect.primaryConstructor
|
||||||
|
|
||||||
internal val KtAnnotationEntry.typeName: String get() = (typeReference?.typeElement as? KtUserType)?.referencedName.orAnonymous()
|
internal val KtAnnotationEntry.typeName: String get() = (typeReference?.typeElement as? KtUserType)?.referencedName.orAnonymous()
|
||||||
@@ -34,50 +33,26 @@ internal val KtAnnotationEntry.typeName: String get() = (typeReference?.typeElem
|
|||||||
internal fun String?.orAnonymous(kind: String = ""): String =
|
internal fun String?.orAnonymous(kind: String = ""): String =
|
||||||
this ?: "<anonymous" + (if (kind.isNotBlank()) " $kind" else "") + ">"
|
this ?: "<anonymous" + (if (kind.isNotBlank()) " $kind" else "") + ">"
|
||||||
|
|
||||||
internal class KtAnnotationWrapper(val psi: KtAnnotationEntry, val targetClass: KClass<out Annotation>) {
|
internal fun constructAnnotation(psi: KtAnnotationEntry, targetClass: KClass<out Annotation>): Annotation {
|
||||||
val name: String get() = psi.typeName
|
|
||||||
|
|
||||||
val valueArguments: Map<String, Any?> by lazy {
|
val valueArguments = psi.valueArguments.map { arg ->
|
||||||
var namedStarted = false
|
val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance, LanguageVersionSettingsImpl.DEFAULT)
|
||||||
val res = hashMapOf<String, Any?>()
|
val trace = BindingTraceContext()
|
||||||
// TODO: annotation constructors unsupported yet in kotlin reflection, test and correct when they will be ready
|
val result = evaluator.evaluateToConstantValue(arg.getArgumentExpression()!!, trace, TypeUtils.NO_EXPECTED_TYPE)
|
||||||
val targetAnnParams = targetClass.primaryConstructor?.parameters
|
// TODO: consider inspecting `trace` to find diagnostics reported during the computation (such as division by zero, integer overflow, invalid annotation parameters etc.)
|
||||||
psi.valueArguments.mapIndexed { i, arg ->
|
val argName = arg.getArgumentName()?.asName?.toString()
|
||||||
val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance, LanguageVersionSettingsImpl.DEFAULT)
|
argName to result?.value
|
||||||
val trace = BindingTraceContext()
|
|
||||||
val result = evaluator.evaluateToConstantValue(arg.getArgumentExpression()!!, trace, TypeUtils.NO_EXPECTED_TYPE)
|
|
||||||
// TODO: consider inspecting `trace` to find diagnostics reported during the computation (such as division by zero, integer overflow, invalid annotation parameters etc.)
|
|
||||||
val argName = arg.getArgumentName()?.asName?.toString()
|
|
||||||
// TODO: consider reusing arguments mapping logic from compiler code
|
|
||||||
// TODO: find out how to properly report problems from here (now using bogus arg names as an indicator)
|
|
||||||
val paramName = when {
|
|
||||||
argName == null && !namedStarted && targetAnnParams == null -> "$" // TODO: using invalid name here. Drop when annotation constructors will be accessible (se above)
|
|
||||||
argName == null && !namedStarted -> targetAnnParams?.get(i)?.name ?: "$(Unnamed argument for $name at $i)"
|
|
||||||
argName == null && namedStarted -> "$(Invalid argument sequence for $name at arg $i)"
|
|
||||||
targetAnnParams != null && targetAnnParams.none { it.name == argName } -> "$(Unknown argument $argName for $name)"
|
|
||||||
else -> {
|
|
||||||
namedStarted = true
|
|
||||||
argName!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.put(paramName, result?.value)
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
val mappedArguments: Map<KParameter, Any?> =
|
||||||
|
tryCreateCallableMappingFromNamedArgs(targetClass.constructors.first(), valueArguments)
|
||||||
|
?: return InvalidScriptResolverAnnotation(psi.typeName, valueArguments)
|
||||||
|
|
||||||
internal class AnnProxyInvocationHandler(val targetAnnClass: KClass<out Annotation>, val annParams: Map<String, Any?>) : InvocationHandler {
|
try {
|
||||||
override fun invoke(proxy: Any?, method: Method?, params: Array<out Any>?): Any? = method?.let {
|
return targetClass.primaryConstructor!!.callBy(mappedArguments)
|
||||||
method?.name?.let { annParams[it] }
|
}
|
||||||
}
|
catch (ex: Exception) {
|
||||||
|
return InvalidScriptResolverAnnotation(psi.typeName, valueArguments, ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProxy(classLoader: ClassLoader): Annotation =
|
|
||||||
try {
|
|
||||||
Proxy.newProxyInstance(classLoader, arrayOf(targetClass.java), AnnProxyInvocationHandler(targetClass, valueArguments)) as Annotation
|
|
||||||
}
|
|
||||||
catch (ex: Exception) {
|
|
||||||
InvalidScriptResolverAnnotation(name, valueArguments, ex)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidScriptResolverAnnotation(val name: String, val annParams: Map<String, Any?>, val error: Exception? = null) : Annotation
|
class InvalidScriptResolverAnnotation(val name: String, val annParams: List<Pair<String?, Any?>>?, val error: Exception? = null) : Annotation
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.scripts
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||||
@@ -35,6 +35,8 @@ import org.jetbrains.kotlin.utils.PathUtil
|
|||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@@ -91,7 +93,14 @@ class ScriptTemplateTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithDependsAnn2() {
|
fun testScriptWithDependsAnn2() {
|
||||||
Assert.assertNull(compileScript("fib_ext_ann2.kts", ScriptWithIntParamAndDummyResolver::class, null, includeKotlinRuntime = false))
|
val savedErr = System.err
|
||||||
|
try {
|
||||||
|
System.setErr(PrintStream(NullOutputStream()))
|
||||||
|
Assert.assertNull(compileScript("fib_ext_ann2.kts", ScriptWithIntParamAndDummyResolver::class, null, includeKotlinRuntime = false))
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
System.setErr(savedErr)
|
||||||
|
}
|
||||||
|
|
||||||
val aClass = compileScript("fib_ext_ann2.kts", ScriptWithIntParam::class, null, includeKotlinRuntime = false)
|
val aClass = compileScript("fib_ext_ann2.kts", ScriptWithIntParam::class, null, includeKotlinRuntime = false)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
@@ -324,13 +333,13 @@ class TestKotlinScriptDependenciesResolver : TestKotlinScriptDummyDependenciesRe
|
|||||||
val cp = script.annotations.flatMap {
|
val cp = script.annotations.flatMap {
|
||||||
when (it) {
|
when (it) {
|
||||||
is DependsOn -> if (it.path == "@{runtime}") listOf(kotlinPaths.runtimePath, kotlinPaths.scriptRuntimePath) else listOf(File(it.path))
|
is DependsOn -> if (it.path == "@{runtime}") listOf(kotlinPaths.runtimePath, kotlinPaths.scriptRuntimePath) else listOf(File(it.path))
|
||||||
is DependsOnTwo -> listOf(it.path1, it.path2).flatMap {
|
is DependsOnTwo -> listOf(it.path1, it.path2).flatMap { it?.let {
|
||||||
when {
|
when {
|
||||||
it.isBlank() -> emptyList()
|
it.isBlank() -> emptyList()
|
||||||
it == "@{runtime}" -> listOf(kotlinPaths.runtimePath, kotlinPaths.scriptRuntimePath)
|
it == "@{runtime}" -> listOf(kotlinPaths.runtimePath, kotlinPaths.scriptRuntimePath)
|
||||||
else -> listOf(File(it))
|
else -> listOf(File(it))
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
is InvalidScriptResolverAnnotation -> throw Exception("Invalid annotation ${it.name}", it.error)
|
is InvalidScriptResolverAnnotation -> throw Exception("Invalid annotation ${it.name}", it.error)
|
||||||
else -> throw Exception("Unknown annotation ${it.javaClass}")
|
else -> throw Exception("Unknown annotation ${it.javaClass}")
|
||||||
}
|
}
|
||||||
@@ -394,3 +403,9 @@ annotation class DependsOn(val path: String)
|
|||||||
@Target(AnnotationTarget.FILE)
|
@Target(AnnotationTarget.FILE)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class DependsOnTwo(val unused: String = "", val path1: String = "", val path2: String = "")
|
annotation class DependsOnTwo(val unused: String = "", val path1: String = "", val path2: String = "")
|
||||||
|
|
||||||
|
private class NullOutputStream : OutputStream() {
|
||||||
|
override fun write(b: Int) { }
|
||||||
|
override fun write(b: ByteArray) { }
|
||||||
|
override fun write(b: ByteArray, off: Int, len: Int) { }
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.scripts
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.utils.tryCreateCallableMappingFromNamedArgs
|
||||||
|
import org.jetbrains.kotlin.utils.tryCreateCallableMappingFromStringArgs
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.reflect.KParameter
|
||||||
|
|
||||||
|
class ArgsToParamsMatchingTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMatchFromStrings() {
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromStringArgs(::foo, listOf()))
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "2")))
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "2", "s", "0.1")),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1)
|
||||||
|
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "258", "s", "0.1")))
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "258", "s", "0")))
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "258", "sss", "0.1")))
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromStringArgs(::foo, listOf("1", "2", "s", "0.1", "abc", "true", "1", "2", "3")),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1, "s" to "abc", "t" to true, "v" to arrayOf(1L, 2L, 3L))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMatchNamed() {
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromNamedArgs(::foo, listOf()))
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2)))
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), null to 's', null to 0.1)),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1)
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), "c" to 's', "d" to 0.1)),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1)
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), "d" to 0.1, "c" to 's')),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1)
|
||||||
|
|
||||||
|
assertParamMapsEquals(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), null to 's', null to 0.1, "v" to arrayOf(1L, 2L, 3L))),
|
||||||
|
"i" to 1, "b" to 2.toByte(), "c" to 's', "d" to 0.1, "v" to arrayOf(1L, 2L, 3L))
|
||||||
|
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), null to 's', "x" to 0.1))) // wrong name
|
||||||
|
Assert.assertNull(tryCreateCallableMappingFromNamedArgs(::foo, listOf(null to 1, null to 2.toByte(), "c" to 's', null to 0.1))) // unnamed after named
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertParamMapsEquals(actuals: Map<KParameter, Any?>?, vararg expected: Pair<String, Any?>) {
|
||||||
|
Assert.assertNotNull(actuals)
|
||||||
|
val stringifiedActuals = actuals!!.mapKeys { it.key.name }
|
||||||
|
val mappedExpected = expected.toMap()
|
||||||
|
if (expected != stringifiedActuals) {
|
||||||
|
Assert.assertEquals(stringifiedActuals.keys, mappedExpected.keys)
|
||||||
|
mappedExpected.forEach { exp ->
|
||||||
|
val actVal = stringifiedActuals[exp.key]
|
||||||
|
if (exp.value != actVal) {
|
||||||
|
val msg = "Unexpected value for key '${exp.key}'; expected: ${exp.value}, actual: $actVal"
|
||||||
|
if ((exp.value?.javaClass?.isArray ?: false) && (actVal?.javaClass?.isArray ?: false )) {
|
||||||
|
Assert.assertArrayEquals(msg, arrayOf(exp.value), arrayOf(actVal)) // tricking Array.deepEquals to compare single element arrays (instead of tedious casting to typed array)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert.assertEquals(msg, exp.value, actVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun foo(i: Int, b: Byte, c: Char, d: Double = 0.0, s: String = "", t: Boolean = true, vararg v: Long) {}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.utils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||||
|
import kotlin.reflect.KCallable
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KParameter
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
|
|
||||||
|
fun tryConstructClassFromStringArgs(clazz: Class<*>, args: List<String>): Any? {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return clazz.getConstructor(Array<String>::class.java).newInstance(args.toTypedArray())
|
||||||
|
}
|
||||||
|
catch (e: NoSuchMethodException) {
|
||||||
|
for (ctor in clazz.kotlin.constructors) {
|
||||||
|
val mapping = tryCreateCallableMappingFromStringArgs(ctor, args)
|
||||||
|
if (mapping != null) {
|
||||||
|
try {
|
||||||
|
return ctor.callBy(mapping)
|
||||||
|
}
|
||||||
|
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tryCreateCallableMapping(callable: KCallable<*>, args: List<Any?>): Map<KParameter, Any?>? =
|
||||||
|
tryCreateCallableMapping(callable, args.map { NamedArgument(null, it) }.iterator(), AnyArgsConverter())
|
||||||
|
|
||||||
|
fun tryCreateCallableMappingFromStringArgs(callable: KCallable<*>, args: List<String>): Map<KParameter, Any?>? =
|
||||||
|
tryCreateCallableMapping(callable, args.map { NamedArgument(null, it) }.iterator(), StringArgsConverter())
|
||||||
|
|
||||||
|
fun tryCreateCallableMappingFromNamedArgs(callable: KCallable<*>, args: List<Pair<String?, Any?>>): Map<KParameter, Any?>? =
|
||||||
|
tryCreateCallableMapping(callable, args.map { NamedArgument(it.first, it.second) }.iterator(), AnyArgsConverter())
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
|
private data class NamedArgument<out T>(val name: String?, val value: T?)
|
||||||
|
|
||||||
|
private interface ArgsConverter<T> {
|
||||||
|
|
||||||
|
sealed class Result {
|
||||||
|
object Failure : Result()
|
||||||
|
class Success(val v: Any?) : Result()
|
||||||
|
}
|
||||||
|
fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<T>): Result
|
||||||
|
fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result
|
||||||
|
fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<T>, restArgsIt: Iterator<NamedArgument<T>>): Result
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class ArgsTraversalState { UNNAMED, NAMED, TAIL }
|
||||||
|
|
||||||
|
private fun <T> tryCreateCallableMapping(callable: KCallable<*>, args: Iterator<NamedArgument<T>>, converter: ArgsConverter<T>): Map<KParameter, Any?>? {
|
||||||
|
val res = mutableMapOf<KParameter, Any?>()
|
||||||
|
var state = ArgsTraversalState.UNNAMED
|
||||||
|
val unboundParams = callable.parameters.toMutableList()
|
||||||
|
val argIt = args.iterator()
|
||||||
|
while (argIt.hasNext()) {
|
||||||
|
if (unboundParams.isEmpty()) return null // failed to match: no param left for the arg
|
||||||
|
val arg = argIt.next()
|
||||||
|
when (state) {
|
||||||
|
ArgsTraversalState.UNNAMED -> if (arg.name != null) state = ArgsTraversalState.NAMED
|
||||||
|
ArgsTraversalState.NAMED -> if (arg.name == null) state = ArgsTraversalState.TAIL
|
||||||
|
ArgsTraversalState.TAIL -> if (arg.name != null) throw IllegalArgumentException("Illegal mix of named and unnamed arguments")
|
||||||
|
}
|
||||||
|
// TODO: check the logic of named/unnamed/tail(vararg or lambda) arguments matching
|
||||||
|
when (state) {
|
||||||
|
ArgsTraversalState.UNNAMED -> {
|
||||||
|
val par = unboundParams.removeAt(0)
|
||||||
|
// try single argument first
|
||||||
|
val cvtRes = converter.tryConvertSingle(par, arg)
|
||||||
|
if (cvtRes is ArgsConverter.Result.Success) {
|
||||||
|
if (cvtRes.v == null && !par.type.isMarkedNullable) { // TODO: this is not precise check - see comments to the property; consider better approach
|
||||||
|
// or if we do not allow to overload on nullability, drop this check
|
||||||
|
return null // failed to match: null for a non-nullable value
|
||||||
|
}
|
||||||
|
res.put(par, cvtRes.v)
|
||||||
|
}
|
||||||
|
// TODO: check if second part of the condition is necessary
|
||||||
|
else if ((par.type.classifier as? KClass<*>)?.let { it.java.isArray || it.qualifiedName == Array<Any>::class.qualifiedName } ?: false) {
|
||||||
|
// try vararg
|
||||||
|
val cvtVRes = converter.tryConvertVararg(par, arg, argIt)
|
||||||
|
if (cvtVRes is ArgsConverter.Result.Success) {
|
||||||
|
res.put(par, cvtVRes.v)
|
||||||
|
}
|
||||||
|
else return null // failed to match: no suitable param for unnamed arg
|
||||||
|
}
|
||||||
|
else return null // failed to match: no suitable param for unnamed arg
|
||||||
|
}
|
||||||
|
ArgsTraversalState.NAMED -> {
|
||||||
|
assert(arg.name != null)
|
||||||
|
val parIdx = unboundParams.indexOfFirst { it.name != null && it.name == arg.name }.check { it >= 0 }
|
||||||
|
?: return null // failed to match: no matching named parameter found
|
||||||
|
val par = unboundParams.removeAt(parIdx)
|
||||||
|
val cvtRes = converter.tryConvertSingle(par, arg)
|
||||||
|
if (cvtRes is ArgsConverter.Result.Success) {
|
||||||
|
res.put(par, cvtRes.v)
|
||||||
|
}
|
||||||
|
else return null // failed to match: cannot convert arg to param's type
|
||||||
|
}
|
||||||
|
ArgsTraversalState.TAIL -> {
|
||||||
|
assert(arg.name == null)
|
||||||
|
val par = unboundParams.removeAt(unboundParams.lastIndex)
|
||||||
|
val cvtVRes = converter.tryConvertTail(par, arg, argIt)
|
||||||
|
if (cvtVRes is ArgsConverter.Result.Success) {
|
||||||
|
if (argIt.hasNext()) return null // failed to match: not all tail args are consumed
|
||||||
|
res.put(par, cvtVRes.v)
|
||||||
|
}
|
||||||
|
else return null // failed to match: no suitable param for tail arg(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
unboundParams.any { !it.isOptional && !it.isVararg } -> null // fail to match: non-optional params remained
|
||||||
|
else -> res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class StringArgsConverter : ArgsConverter<String> {
|
||||||
|
|
||||||
|
override fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<String>): ArgsConverter.Result {
|
||||||
|
|
||||||
|
fun convertPrimitive(type: KType?, arg: String): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
String::class -> arg
|
||||||
|
Int::class -> arg.toInt()
|
||||||
|
Long::class -> arg.toLong()
|
||||||
|
Short::class -> arg.toShort()
|
||||||
|
Byte::class -> arg.toByte()
|
||||||
|
Char::class -> if (arg.length != 1) null else arg[0]
|
||||||
|
Float::class -> arg.toFloat()
|
||||||
|
Double::class -> arg.toDouble()
|
||||||
|
Boolean::class -> arg.toBoolean()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (arg.value != null) {
|
||||||
|
val primArgCandidate = convertPrimitive(parameter.type, arg.value)
|
||||||
|
if (primArgCandidate != null)
|
||||||
|
return ArgsConverter.Result.Success(primArgCandidate)
|
||||||
|
}
|
||||||
|
else return ArgsConverter.Result.Success(null)
|
||||||
|
}
|
||||||
|
catch (e: NumberFormatException) {}
|
||||||
|
|
||||||
|
return ArgsConverter.Result.Failure
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<String>, restArgsIt: Iterator<NamedArgument<String>>): ArgsConverter.Result {
|
||||||
|
|
||||||
|
fun convertAnyArray(type: KType?, args: Sequence<String?>): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
String::class -> args.toList().toTypedArray()
|
||||||
|
else -> {
|
||||||
|
(type?.classifier as? KClass<*>)?.constructors?.forEach { ctor ->
|
||||||
|
try {
|
||||||
|
return@convertAnyArray args.map { ctor.call(it) }.toList().toTypedArray()
|
||||||
|
}
|
||||||
|
catch (e: Exception) {}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertPrimitivesArray(type: KType?, args: Sequence<String?>): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
IntArray::class -> args.map { it?.toInt() }.toList().toTypedArray()
|
||||||
|
LongArray::class -> args.map { it?.toLong() }.toList().toTypedArray()
|
||||||
|
ShortArray::class -> args.map { it?.toShort() }.toList().toTypedArray()
|
||||||
|
ByteArray::class -> args.map { it?.toByte() }.toList().toTypedArray()
|
||||||
|
CharArray::class -> args.map { it?.get(0) }.toList().toTypedArray()
|
||||||
|
FloatArray::class -> args.map { it?.toFloat() }.toList().toTypedArray()
|
||||||
|
DoubleArray::class -> args.map { it?.toDouble() }.toList().toTypedArray()
|
||||||
|
BooleanArray::class -> args.map { it?.toBoolean() }.toList().toTypedArray()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: check if second part of the condition is necessary
|
||||||
|
if ((parameter.type.classifier as? KClass<*>)?.let { it.java.isArray || it.qualifiedName == Array<Any>::class.qualifiedName } ?: false) {
|
||||||
|
val argsSequence = sequenceOf(firstArg.value) + restArgsIt.asSequence().map { it.value }
|
||||||
|
val primArrayArgCandidate = convertPrimitivesArray(parameter.type, argsSequence)
|
||||||
|
if (primArrayArgCandidate != null)
|
||||||
|
return ArgsConverter.Result.Success(primArrayArgCandidate)
|
||||||
|
val arrCompType = parameter.type.arguments.getOrNull(0)?.type
|
||||||
|
val arrayArgCandidate = convertAnyArray(arrCompType, argsSequence)
|
||||||
|
if (arrayArgCandidate != null)
|
||||||
|
return ArgsConverter.Result.Success(arrayArgCandidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: NumberFormatException) {}
|
||||||
|
|
||||||
|
return ArgsConverter.Result.Failure
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<String>, restArgsIt: Iterator<NamedArgument<String>>): ArgsConverter.Result =
|
||||||
|
tryConvertVararg(parameter, firstArg, restArgsIt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AnyArgsConverter : ArgsConverter<Any> {
|
||||||
|
override fun tryConvertSingle(parameter: KParameter, arg: NamedArgument<Any>): ArgsConverter.Result {
|
||||||
|
|
||||||
|
fun convertPrimitivesArray(type: KType?, arg: Any?): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
IntArray::class -> (arg as? Array<Int>)?.toIntArray()
|
||||||
|
LongArray::class -> (arg as? Array<Long>)?.toLongArray()
|
||||||
|
ShortArray::class -> (arg as? Array<Short>)?.toShortArray()
|
||||||
|
ByteArray::class -> (arg as? Array<Byte>)?.toByteArray()
|
||||||
|
CharArray::class -> (arg as? Array<Char>)?.toCharArray()
|
||||||
|
FloatArray::class -> (arg as? Array<Float>)?.toFloatArray()
|
||||||
|
DoubleArray::class -> (arg as? Array<Double>)?.toDoubleArray()
|
||||||
|
BooleanArray::class -> (arg as? Array<Boolean>)?.toBooleanArray()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertSingle(type: KType, arg: Any?): Any? = when {
|
||||||
|
type.classifier == arg?.javaClass?.kotlin -> arg
|
||||||
|
type.jvmErasure.java.isAssignableFrom(arg?.javaClass) -> arg
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (convertSingle(parameter.type, arg.value) ?: convertPrimitivesArray(parameter.type, arg.value))
|
||||||
|
?.let { ArgsConverter.Result.Success(it) }
|
||||||
|
?: ArgsConverter.Result.Failure
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tryConvertVararg(parameter: KParameter, firstArg: NamedArgument<Any>, restArgsIt: Iterator<NamedArgument<Any>>): ArgsConverter.Result =
|
||||||
|
ArgsConverter.Result.Failure
|
||||||
|
|
||||||
|
override fun tryConvertTail(parameter: KParameter, firstArg: NamedArgument<Any>, restArgsIt: Iterator<NamedArgument<Any>>): ArgsConverter.Result =
|
||||||
|
tryConvertSingle(parameter, firstArg)
|
||||||
|
}
|
||||||
|
|
||||||
+1
-1
@@ -35,7 +35,7 @@ import org.apache.maven.plugins.annotations.ResolutionScope;
|
|||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
import org.codehaus.plexus.component.repository.ComponentDependency;
|
import org.codehaus.plexus.component.repository.ComponentDependency;
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
|
||||||
import org.jetbrains.kotlin.cli.common.ReflectionUtilKt;
|
import org.jetbrains.kotlin.utils.ReflectionUtilKt;
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||||
|
|||||||
+3
-3
@@ -28,7 +28,7 @@ interface Resolver {
|
|||||||
|
|
||||||
class DirectResolver : Resolver {
|
class DirectResolver : Resolver {
|
||||||
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
|
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
|
||||||
dependsOn.value?.let(::File)?.check { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
|
dependsOn.value.check(String::isNotBlank)?.let(::File)?.check { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlatLibDirectoryResolver(val path: File) : Resolver {
|
class FlatLibDirectoryResolver(val path: File) : Resolver {
|
||||||
@@ -39,10 +39,10 @@ class FlatLibDirectoryResolver(val path: File) : Resolver {
|
|||||||
|
|
||||||
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
|
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? =
|
||||||
// TODO: add coordinates and wildcard matching
|
// TODO: add coordinates and wildcard matching
|
||||||
dependsOn.value?.let{ File(path, it) }?.check { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
|
dependsOn.value.check(String::isNotBlank)?.let{ File(path, it) }?.check { it.exists() && (it.isFile || it.isDirectory) }?.let { listOf(it) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun tryCreate(annotation: Repository): FlatLibDirectoryResolver? =
|
fun tryCreate(annotation: Repository): FlatLibDirectoryResolver? =
|
||||||
annotation.value?.check(String::isNotBlank)?.let(::File)?.check { it.exists() && it.isDirectory }?.let(::FlatLibDirectoryResolver)
|
annotation.value.check(String::isNotBlank)?.let(::File)?.check { it.exists() && it.isDirectory }?.let(::FlatLibDirectoryResolver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-7
@@ -43,7 +43,7 @@ class MavenResolver(val reportError: ((String) -> Unit)? = null): Resolver {
|
|||||||
|
|
||||||
private fun currentRepos() = if (repos.isEmpty()) arrayListOf(mavenCentral) else repos
|
private fun currentRepos() = if (repos.isEmpty()) arrayListOf(mavenCentral) else repos
|
||||||
|
|
||||||
private fun String?.isValidParam() = if (this != null && isNotBlank()) true else false
|
private fun String.isValidParam() = isNotBlank()
|
||||||
|
|
||||||
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? {
|
override fun tryResolve(dependsOn: DependsOn): Iterable<File>? {
|
||||||
|
|
||||||
@@ -55,13 +55,10 @@ class MavenResolver(val reportError: ((String) -> Unit)? = null): Resolver {
|
|||||||
|
|
||||||
val artifactId: DefaultArtifact = when {
|
val artifactId: DefaultArtifact = when {
|
||||||
dependsOn.groupId.isValidParam() || dependsOn.artifactId.isValidParam() -> {
|
dependsOn.groupId.isValidParam() || dependsOn.artifactId.isValidParam() -> {
|
||||||
val s1 = dependsOn.groupId
|
|
||||||
val s2 = dependsOn.artifactId
|
|
||||||
DefaultArtifact(dependsOn.groupId.orNullIfBlank(), dependsOn.artifactId.orNullIfBlank(), null, dependsOn.version.orNullIfBlank())
|
DefaultArtifact(dependsOn.groupId.orNullIfBlank(), dependsOn.artifactId.orNullIfBlank(), null, dependsOn.version.orNullIfBlank())
|
||||||
}
|
}
|
||||||
dependsOn.value.isValidParam() && dependsOn.value.count { it == ':' } == 2 -> {
|
dependsOn.value.isValidParam() && dependsOn.value.count { it == ':' } == 2 -> {
|
||||||
val s = dependsOn.value
|
DefaultArtifact(dependsOn.value)
|
||||||
DefaultArtifact(s)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
error("Unknown set of arguments to maven resolver: ${dependsOn.value}")
|
error("Unknown set of arguments to maven resolver: ${dependsOn.value}")
|
||||||
@@ -84,7 +81,7 @@ class MavenResolver(val reportError: ((String) -> Unit)? = null): Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tryAddRepo(annotation: Repository): Boolean {
|
fun tryAddRepo(annotation: Repository): Boolean {
|
||||||
val urlStr = if (annotation.url.isValidParam()) annotation.url else annotation.value ?: return false
|
val urlStr = annotation.url.check { it.isValidParam() } ?: annotation.value.check { it.isValidParam() } ?: return false
|
||||||
try {
|
try {
|
||||||
URL(urlStr)
|
URL(urlStr)
|
||||||
} catch (_: MalformedURLException) {
|
} catch (_: MalformedURLException) {
|
||||||
@@ -92,7 +89,7 @@ class MavenResolver(val reportError: ((String) -> Unit)? = null): Resolver {
|
|||||||
}
|
}
|
||||||
repos.add(
|
repos.add(
|
||||||
RemoteRepository(
|
RemoteRepository(
|
||||||
if (annotation.id.isValidParam()) annotation.id else "cantral",
|
if (annotation.id.isValidParam()) annotation.id else "central",
|
||||||
"default",
|
"default",
|
||||||
urlStr
|
urlStr
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user