From c39789f06ba56368e74169c677e9e9da88a604ea Mon Sep 17 00:00:00 2001 From: Jingkei Ly Date: Thu, 20 Sep 2018 18:03:39 +0100 Subject: [PATCH 1/2] Throw an appropriate exception if createComponent fails Note that PooledEngine throws a RuntimeException with an appropriate message "Class cannot be created (missing no-arg constructor)", instead of returning null. `Engine.create` still catches the Exception and throws a `CreateComponentException` for consistency between the two Engine types. --- ashley/src/main/kotlin/ktx/ashley/engines.kt | 15 ++++++++++++-- .../src/test/kotlin/ktx/ashley/EnginesSpec.kt | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/ashley/src/main/kotlin/ktx/ashley/engines.kt b/ashley/src/main/kotlin/ktx/ashley/engines.kt index 2bfe8b09..fba4ffac 100644 --- a/ashley/src/main/kotlin/ktx/ashley/engines.kt +++ b/ashley/src/main/kotlin/ktx/ashley/engines.kt @@ -23,6 +23,7 @@ class EngineEntity( * @param T the [Component] type to get or create. * @param configure inlined function with [T] as the receiver to allow additional configuration of the [Component]. * @return the created ƒ[Component]. + * @throws [CreateComponentException] if the engine was unable to create the component * @see [create] */ inline fun with(configure: (@AshleyDsl T).() -> Unit = {}): T { @@ -36,12 +37,20 @@ class EngineEntity( /** * Get or create a [Component] by calling [Engine.createComponent]. * + * The [Component] must have a visible no-arg constructor. + * * @param T the type of [Component] to get or create. * @param configure inlined function with [T] as the receiver to allow further configuration. * @return an [Component] instance of the selected type. + * @throws [CreateComponentException] if the engine was unable to create the component */ -inline fun Engine.create(configure: T.() -> Unit = {}): T - = createComponent(T::class.java).apply(configure) +inline fun Engine.create(configure: T.() -> Unit = {}): T { + return try { + createComponent(T::class.java) + } catch (e: Exception) { + throw CreateComponentException(T::class.java, e) + }?.apply(configure)?:throw CreateComponentException(T::class.java) +} /** * Builder function for [Engine]. @@ -63,3 +72,5 @@ inline fun Engine.entity(configure: EngineEntity.() -> Unit = {}): Entity { addEntity(entity) return entity } + +class CreateComponentException(type: Class<*>, cause: Throwable? = null): RuntimeException("Could not instantiate component ${type.name} - the component must have a visible no-arg constructor", cause) diff --git a/ashley/src/test/kotlin/ktx/ashley/EnginesSpec.kt b/ashley/src/test/kotlin/ktx/ashley/EnginesSpec.kt index de686a7a..0a798f86 100644 --- a/ashley/src/test/kotlin/ktx/ashley/EnginesSpec.kt +++ b/ashley/src/test/kotlin/ktx/ashley/EnginesSpec.kt @@ -1,7 +1,10 @@ package ktx.ashley +import com.badlogic.ashley.core.Component +import com.badlogic.ashley.core.Engine import com.badlogic.ashley.core.PooledEngine import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.describe import org.jetbrains.spek.api.dsl.it @@ -18,6 +21,23 @@ object EnginesSpec : Spek({ assertThat(component.y).isEqualTo(0f) } } + describe("creating a component that has no no-arg constructor") { + @Suppress("UNUSED_PARAMETER") + class NoNoArgConstructorComponent(body: String): Component + + it("should throw an exception if the non-pooled engine was unable to create the component") { + val nonPooledEngine = Engine() + assertThatExceptionOfType(CreateComponentException::class.java).isThrownBy { + nonPooledEngine.create() + } + } + + it("should throw an exception if the pooled engine was unable to create the component") { + assertThatExceptionOfType(CreateComponentException::class.java).isThrownBy { + engine.create() + } + } + } describe("creating a component with configuration") { val component = engine.create { x = 1f From 53f9321308fd694f84e5f3e1a8b8cb4a6345c3d8 Mon Sep 17 00:00:00 2001 From: Jingkei Ly Date: Thu, 20 Sep 2018 18:32:57 +0100 Subject: [PATCH 2/2] Use KClass in exception constructor --- ashley/src/main/kotlin/ktx/ashley/engines.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ashley/src/main/kotlin/ktx/ashley/engines.kt b/ashley/src/main/kotlin/ktx/ashley/engines.kt index fba4ffac..fdb4d29e 100644 --- a/ashley/src/main/kotlin/ktx/ashley/engines.kt +++ b/ashley/src/main/kotlin/ktx/ashley/engines.kt @@ -3,6 +3,7 @@ package ktx.ashley import com.badlogic.ashley.core.Component import com.badlogic.ashley.core.Engine import com.badlogic.ashley.core.Entity +import kotlin.reflect.KClass /** * An [Entity] created by the provided [Engine]. @@ -48,8 +49,8 @@ inline fun Engine.create(configure: T.() -> Unit = {}): return try { createComponent(T::class.java) } catch (e: Exception) { - throw CreateComponentException(T::class.java, e) - }?.apply(configure)?:throw CreateComponentException(T::class.java) + throw CreateComponentException(T::class, e) + }?.apply(configure)?:throw CreateComponentException(T::class) } /** @@ -73,4 +74,4 @@ inline fun Engine.entity(configure: EngineEntity.() -> Unit = {}): Entity { return entity } -class CreateComponentException(type: Class<*>, cause: Throwable? = null): RuntimeException("Could not instantiate component ${type.name} - the component must have a visible no-arg constructor", cause) +class CreateComponentException(type: KClass<*>, cause: Throwable? = null): RuntimeException("Could not instantiate component ${type::java.name} - the component must have a visible no-arg constructor", cause)