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

org.scaladebugger.api.lowlevel.events.StandardEventManager.scala Maven / Gradle / Ivy

package org.scaladebugger.api.lowlevel.events

import org.scaladebugger.api.lowlevel.events.data.JDIEventDataResult
import org.scaladebugger.api.lowlevel.events.misc.Resume
import org.scaladebugger.api.utils.{Logging, LoopingTaskRunner, MultiMap}
import com.sun.jdi.event.{Event, EventQueue, EventSet}
import EventType._

import scala.util.{Failure, Success, Try}

/**
 * Represents a manager for events coming in from a virtual machine.
 *
 * @param eventQueue The event queue whose events to pull off and process
 * @param loopingTaskRunner The runner used to process events
 * @param autoStart If true, starts the event processing automatically
 * @param onExceptionResume If true, any event handler that throws an exception
 *                          will count towards resuming the event set, otherwise
 *                          it will cause the event set to not resume
 */
class StandardEventManager(
  private val eventQueue: EventQueue,
  private val loopingTaskRunner: LoopingTaskRunner,
  private val autoStart: Boolean = true,
  private val onExceptionResume: Boolean = true
) extends EventManager with Logging {
  /** Contains the events, associated handlers and their ids. */
  private val eventHandlers = new MultiMap[EventType, EventHandlerInfo]

  private var eventTaskId: Option[String] = None

  /**
   * Indicates whether or not the event manager is processing events.
   *
   * @return True if it is running, otherwise false
   */
  override def isRunning: Boolean = eventTaskId.nonEmpty

  /**
   * Begins the processing of events from the virtual machine.
   */
  override def start(): Unit = {
    assert(!isRunning, "Event manager already started!")

    logger.trace("Starting event manager for virtual machine!")
    eventTaskId = Some(loopingTaskRunner.addTask(eventHandlerTask()))

    eventTaskId.foreach(id =>
      logger.trace(s"Event process task: $id"))
  }

  /**
   * Represents the task to be added per event handler. Can be overridden to
   * perform different tasks.
   */
  protected def eventHandlerTask(): Unit = {
    val eventSet = eventQueue.remove()

    // Process the set of events, returning whether or not the event
    // set should resume
    val eventSetProcessor = newEventSetProcessor(eventSet)
    eventSetProcessor.process()
  }

  /**
   * Creates a new event set processor. Can be overridden.
   *
   * @param eventSet The event set to process
   * @return The new event set processor instance
   */
  protected def newEventSetProcessor(
    eventSet: EventSet
  ): EventSetProcessor = new EventSetProcessor(
    eventSet                = eventSet,
    eventFunctionRetrieval  = getHandlersForEventType,
    onExceptionResume       = onExceptionResume
  )

  /**
   * Ends the processing of events from the virtual machine.
   */
  override def stop(): Unit = {
    assert(isRunning, "Event manager not started!")

    logger.trace(s"Stopping event manager ($eventTaskId) for virtual machine!")
    loopingTaskRunner.removeTask(eventTaskId.get)
    eventTaskId = None
  }

  /**
   * Adds the event function to this manager. The return value of the handler
   * function contributes towards whether or not to resume the event set.
   *
   * @param eventHandlerId The id to associate with the event handler
   * @param eventType The type of the event to add a function
   * @param eventHandler The function to add, taking the occurring event and
   *                     a collection of retrieved data from the event
   * @param eventArguments The arguments used when determining whether or not to
   *                       invoke the event handler
   * @return The id associated with the event handler
   */
  override def addEventHandlerWithId(
    eventHandlerId: String,
    eventType: EventType,
    eventHandler: EventHandler,
    eventArguments: JDIEventArgument*
  ): String = {
    logger.trace(s"Created event handler with id '$eventHandlerId'")
    wrapAndAddEventHandler(
      eventHandlerId,
      eventType,
      eventHandler,
      eventArguments: _*
    )
  }

  /**
   * Wraps the provided event handler and adds it to the internal collection.
   *
   * @param eventHandlerId The id to associate with the event handler
   * @param eventType The type of the event to match against the handler
   * @param eventHandler The event handler function to be wrapped
   * @param eventArguments The arguments used when determining whether or not to
   *                       invoke the event handler
   * @return The id associated with the wrapped event handler
   */
  protected def wrapAndAddEventHandler(
    eventHandlerId: String,
    eventType: EventType,
    eventHandler: EventHandler,
    eventArguments: JDIEventArgument*
  ): String = {
    // Create a wrapper that contains our filtering logic
    val wrapperEventHandler =
      newWrapperEventHandler(eventHandler, eventArguments)

    // Store the event handler with the filtering logic
    eventHandlers.putWithId(
      eventHandlerId,
      eventType,
      EventHandlerInfo(
        eventHandlerId,
        eventType,
        wrapperEventHandler,
        eventArguments
      )
    )

    eventHandlerId
  }

  /**
   * Generates a wrapper function around the event handler, using an argument
   * processor to evaluate the provided arguments to determine whether or not
   * to invoke the event handler as well as retrieve any requested data.
   *
   * @param eventHandler The event handler to wrap
   * @param eventArguments The arguments to use when determining if the event
   *                       handler should be invoked and what data to be
   *                       retrieved
   * @return The wrapper around the event handler
   */
  protected def newWrapperEventHandler(
    eventHandler: EventHandler,
    eventArguments: Seq[JDIEventArgument]
  ): EventHandler = {
    val jdiEventArgumentProcessor =
      new JDIEventArgumentProcessor(eventArguments: _*)

    // Create a wrapper function that invokes the event handler only if the
    // filter processor yields a positive result, otherwise skip this handler
    (event: Event, data: Seq[JDIEventDataResult]) => {
      val resumeFlag = eventArguments.collect {
        case r: Resume => r
      }.lastOption.map(_.value)

      Try(jdiEventArgumentProcessor.processAll(event)) match {
        case Success(result) =>
          val (passesFilters, data, _) = result

          if (passesFilters) {
            val result = eventHandler(event, data)
            resumeFlag.getOrElse(result)
          } else true

        case Failure(throwable) =>
          logger.error(s"Failed to process event $event", throwable)
          true
      }
    }
  }

  /**
   * Retrieves the collection of event handler functions for the specific
   * event class.
   *
   * @param eventType The type of event whose functions to retrieve
   * @return The collection of event functions
   */
  override def getHandlersForEventType(
    eventType: EventType
  ) : Seq[EventHandler] = {
    eventHandlers.get(eventType).map(_.map(_.eventHandler)).getOrElse(Nil)
  }

  /**
   * Retrieves the collection of event handler functions for the specific
   * event class.
   *
   * @param eventType The type of event whose functions to retrieve
   * @return The collection of event functions
   */
  override def getHandlerIdsForEventType(eventType: EventType): Seq[String] = {
    eventHandlers.getIdsWithKey(eventType).getOrElse(Nil)
  }

  /**
   * Retrieves the handler with the specified id.
   *
   * @param id The id of the handler to retrieve
   * @return Some event handler if found, otherwise None
   */
  override def getEventHandler(id: String): Option[EventHandler] = {
    eventHandlers.getWithId(id).map(_.eventHandler)
  }

  /**
   * Retrieves information on all event handlers.
   *
   * @return The collection of information on all event handlers
   */
  override def getAllEventHandlerInfo: Seq[EventHandlerInfo] = {
    eventHandlers.values
  }

  /**
   * Removes the event function from this manager.
   *
   * @param id The id of the event handler to remove
   * @return Some event handler if removed, otherwise None
   */
  override def removeEventHandler(id: String): Option[EventHandler] = {
    eventHandlers.removeWithId(id).map(_.eventHandler)
  }

  // ==========================================================================
  // = CONSTRUCTOR
  // ==========================================================================

  // If marked to start automatically, do so
  if (autoStart) start()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy