Skip to content

Commit

Permalink
Done
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidArthurCole committed Dec 14, 2024
1 parent a19df7d commit da7e2d1
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 21 deletions.
9 changes: 8 additions & 1 deletion .live-plugins/event/plugin.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.types.typeUtil.supertypes

val skyhanniEvent = "at.hannibal2.skyhanni.api.event.SkyHanniEvent"
val handleEvent = "HandleEvent"
val eventType = "eventType"

registerInspection(HandleEventInspectionKotlin())

Expand All @@ -30,14 +31,20 @@ class HandleEventInspectionKotlin : AbstractKotlinInspection() {
val hasEventAnnotation = function.annotationEntries.any { it.shortName!!.asString() == handleEvent }
val isEvent = function.valueParameters.firstOrNull()?.type()?.supertypes()
?.any { it.fqName?.asString() == skyhanniEvent } ?: false
val hasEventType = function.annotationEntries
.find { it.shortName!!.asString() == handleEvent }
?.valueArguments
?.find { it.getArgumentName()?.asName?.asString() == eventType }
?.getArgumentExpression()
?.text != null

if (isEvent && !hasEventAnnotation && function.valueParameters.size == 1 && function.isPublic) {
holder.registerProblem(
function,
"Event handler function should be annotated with @HandleEvent",
HandleEventQuickFix()
)
} else if (!isEvent && hasEventAnnotation) {
} else if (!isEvent && !hasEventType && hasEventAnnotation) {
holder.registerProblem(
function,
"Function should not be annotated with @HandleEvent if it does not take a SkyHanniEvent",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ class ModuleProcessor(private val codeGenerator: CodeGenerator, private val logg
}

if (function.annotations.any { it.shortName.asString() == "HandleEvent" }) {
val firstParameter = function.parameters.firstOrNull()?.type?.resolve()!!
if (!skyHanniEvent!!.isAssignableFrom(firstParameter)) {
val firstParameter = function.parameters.firstOrNull()?.type?.resolve()
val handleEventAnnotation = function.annotations.find { it.shortName.asString() == "HandleEvent" }
val eventType = handleEventAnnotation?.arguments?.find { it.name?.asString() == "eventType" }?.value
val isFirstParameterProblem = firstParameter == null && eventType == null
val notAssignable = firstParameter != null && !skyHanniEvent!!.isAssignableFrom(firstParameter)

if (isFirstParameterProblem || notAssignable) {
warnings.add("Function in $className must have an event assignable from $skyHanniEvent because it is annotated with @HandleEvent")
}
}
Expand Down
51 changes: 38 additions & 13 deletions src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@ class EventListeners private constructor(val name: String, private val isGeneric
)

fun addListener(method: Method, instance: Any, options: HandleEvent) {

Check warning on line 20 in src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt

View workflow job for this annotation

GitHub Actions / Run detekt

detekt.style.ThrowsCount

Too many throw statements in the function addListener. The maximum number of allowed throw statements is 2.
require(method.parameterCount == 1)
val generic: Class<*>? = if (isGeneric) {
ReflectionUtils.resolveUpperBoundSuperClassGenericParameter(
method.genericParameterTypes[0],
GenericSkyHanniEvent::class.java.typeParameters[0],
) ?: error(
"Generic event handler type parameter is not present in " +
"event class hierarchy for type ${method.genericParameterTypes[0]}",
)
} else {
null
}
val name = "${method.declaringClass.name}.${method.name}${
method.parameterTypes.joinTo(
StringBuilder(),
Expand All @@ -39,7 +27,44 @@ class EventListeners private constructor(val name: String, private val isGeneric
transform = Class<*>::getTypeName,
)
}"
listeners.add(Listener(name, createEventConsumer(name, instance, method), options, generic))

val eventConsumer: (Any) -> Unit = when (method.parameterCount) {
0 -> {
// Handle methods with no parameters
if (options.eventType == SkyHanniEvent::class) {
throw IllegalArgumentException("Method ${method.name} has no parameters but no eventType was provided in the annotation.")

Check warning on line 35 in src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt

View workflow job for this annotation

GitHub Actions / Run detekt

detekt.style.MaxLineLength

Line detected, which is longer than the defined maximum line length in the code style.

Check warning on line 35 in src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt

View workflow job for this annotation

GitHub Actions / Run detekt

detekt.style.UseRequire

Use require() instead of throwing an IllegalArgumentException.
}
// Verify the provided eventType
val eventType = options.eventType.java
if (!SkyHanniEvent::class.java.isAssignableFrom(eventType)) {
throw IllegalArgumentException("eventType in @HandleEvent must extend SkyHanniEvent. Provided: $eventType")

Check warning on line 40 in src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt

View workflow job for this annotation

GitHub Actions / Run detekt

detekt.style.UseRequire

Use require() instead of throwing an IllegalArgumentException.
}
{ method.invoke(instance) }
}
1 -> {
// Handle methods with a single parameter (original behavior)
if (!SkyHanniEvent::class.java.isAssignableFrom(method.parameterTypes[0])) {
throw IllegalArgumentException("Method ${method.name} parameter must be a subclass of SkyHanniEvent.")

Check warning on line 47 in src/main/java/at/hannibal2/skyhanni/api/event/EventListeners.kt

View workflow job for this annotation

GitHub Actions / Run detekt

detekt.style.UseRequire

Use require() instead of throwing an IllegalArgumentException.
}
{ event -> method.invoke(instance, event) }
}
else -> {
throw IllegalArgumentException("Method ${method.name} must have either 0 or 1 parameters.")
}
}

val generic: Class<*>? = if (isGeneric) {
val genericType = method.genericParameterTypes.getOrNull(0)
?: error("Method ${method.name} does not have a generic parameter type.")
ReflectionUtils.resolveUpperBoundSuperClassGenericParameter(
genericType,
GenericSkyHanniEvent::class.java.typeParameters[0],
) ?: error(
"Generic event handler type parameter is not present in " +
"event class hierarchy for type $genericType",
)
} else null
listeners.add(Listener(name, eventConsumer, options, generic))
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.api.event

import at.hannibal2.skyhanni.data.IslandType
import kotlin.reflect.KClass

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
Expand Down Expand Up @@ -31,6 +32,11 @@ annotation class HandleEvent(
* If the event is cancelled & receiveCancelled is true, then the method will still invoke.
*/
val receiveCancelled: Boolean = false,

/**
* For cases where the event properties are themselves not needed, and solely a listener for an event fire suffices.
*/
val eventType: KClass<out SkyHanniEvent> = SkyHanniEvent::class
) {

companion object {
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,25 @@ object SkyHanniEvents {

@Suppress("UNCHECKED_CAST")
private fun registerMethod(method: Method, instance: Any) {
if (method.parameterCount != 1) return
val options = method.getAnnotation(HandleEvent::class.java) ?: return
val event = method.parameterTypes[0]
if (!SkyHanniEvent::class.java.isAssignableFrom(event)) return
listeners.getOrPut(event as Class<SkyHanniEvent>) { EventListeners(event) }
.addListener(method, instance, options)
val parameterTypes = method.parameterTypes

// Check if the method has no parameters and an eventType is provided
if (parameterTypes.isEmpty()) {
val eventType = options.eventType.java
if (!SkyHanniEvent::class.java.isAssignableFrom(eventType)) return
listeners.getOrPut(eventType as Class<SkyHanniEvent>) { EventListeners(eventType) }
.addListener(method, instance, options)
return
}

// Handle methods with parameters (original behavior)
if (parameterTypes.size == 1) {
val eventType = parameterTypes[0]
if (!SkyHanniEvent::class.java.isAssignableFrom(eventType)) return
listeners.getOrPut(eventType as Class<SkyHanniEvent>) { EventListeners(eventType) }
.addListener(method, instance, options)
}
}

@SubscribeEvent
Expand Down

0 comments on commit da7e2d1

Please sign in to comment.