Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2009-2018 Lightbend Inc.
*/
package akka.actor.typed.javadsl
import scala.annotation.tailrec
import akka.japi.function.{ Function, Function2, Predicate }
import akka.annotation.InternalApi
import akka.actor.typed
import akka.actor.typed.{ Behavior, ExtensibleBehavior, Signal }
import akka.actor.typed.Behavior.unhandled
import BehaviorBuilder._
/**
* Used for creating a [[Behavior]] by 'chaining' message and signal handlers.
*
* When handling a message or signal, this [[Behavior]] will consider all handlers in the order they were added,
* looking for the first handler for which both the type and the (optional) predicate match.
*
* @tparam T the common superclass of all supported messages.
*/
class BehaviorBuilder[T] private (
private val messageHandlers: List[Case[T, T]],
private val signalHandlers: List[Case[T, Signal]]
) {
def build(): Behavior[T] = new BuiltBehavior(messageHandlers.reverse, signalHandlers.reverse)
/**
* Add a new case to the message handling.
*
* @param type type of message to match
* @param handler action to apply if the type matches
* @tparam M type of message to match
* @return a new behavior with the specified handling appended
*/
def onMessage[M <: T](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withMessage(`type`, None, (i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M]))
/**
* Add a new predicated case to the message handling.
*
* @param type type of message to match
* @param test a predicate that will be evaluated on the argument if the type matches
* @param handler action to apply if the type matches and the predicate returns true
* @tparam M type of message to match
* @return a new behavior with the specified handling appended
*/
def onMessage[M <: T](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withMessage(
`type`,
Some((t: T) ⇒ test.test(t.asInstanceOf[M])),
(i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M])
)
/**
* Add a new case to the message handling without compile time type check.
*
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. List.class and (List<String> list) -> {...}
*
* @param type type of message to match
* @param handler action to apply when the type matches
* @return a new behavior with the specified handling appended
*/
def onMessageUnchecked[M <: T](`type`: Class[_ <: T], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withMessage(`type`, None, (i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M]))
/**
* Add a new case to the message handling matching equal messages.
*
* @param msg the message to compare to
* @param handler action to apply when the message matches
* @return a new behavior with the specified handling appended
*/
def onMessageEquals(msg: T, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
withMessage(msg.getClass, Some(_.equals(msg)), (ctx: ActorContext[T], _: T) ⇒ handler.apply(ctx))
/**
* Add a new case to the signal handling.
*
* @param type type of signal to match
* @param handler action to apply if the type matches
* @tparam M type of signal to match
* @return a new behavior with the specified handling appended
*/
def onSignal[M <: Signal](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withSignal(`type`, None, (ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M]))
/**
* Add a new predicated case to the signal handling.
*
* @param type type of signals to match
* @param test a predicate that will be evaluated on the argument if the type matches
* @param handler action to apply if the type matches and the predicate returns true
* @tparam M type of signal to match
* @return a new behavior with the specified handling appended
*/
def onSignal[M <: Signal](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withSignal(
`type`,
Some((t: Signal) ⇒ test.test(t.asInstanceOf[M])),
(ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M])
)
/**
* Add a new case to the signal handling without compile time type check.
*
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. GenMsg.class and (ActorContext ctx, GenMsg<String> list) -> {...}
*
* @param type type of signal to match
* @param handler action to apply when the type matches
* @return a new behavior with the specified handling appended
*/
def onSignalUnchecked[M <: Signal](`type`: Class[_ <: Signal], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
withSignal(`type`, None, (ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M]))
/**
* Add a new case to the signal handling matching equal signals.
*
* @param signal the signal to compare to
* @param handler action to apply when the message matches
* @return a new behavior with the specified handling appended
*/
def onSignalEquals(signal: Signal, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
withSignal(signal.getClass, Some(_.equals(signal)), (ctx: ActorContext[T], _: Signal) ⇒ handler.apply(ctx))
private def withMessage(`type`: Class[_ <: T], test: Option[T ⇒ Boolean], handler: (ActorContext[T], T) ⇒ Behavior[T]): BehaviorBuilder[T] =
new BehaviorBuilder[T](Case[T, T](`type`, test, handler) +: messageHandlers, signalHandlers)
private def withSignal[M <: Signal](`type`: Class[M], test: Option[Signal ⇒ Boolean], handler: (ActorContext[T], Signal) ⇒ Behavior[T]): BehaviorBuilder[T] =
new BehaviorBuilder[T](messageHandlers, Case[T, Signal](`type`, test, handler) +: signalHandlers)
}
object BehaviorBuilder {
def create[T]: BehaviorBuilder[T] = new BehaviorBuilder[T](Nil, Nil)
/** INTERNAL API */
@InternalApi
private[javadsl] final case class Case[BT, MT](`type`: Class[_ <: MT], test: Option[MT ⇒ Boolean], handler: (ActorContext[BT], MT) ⇒ Behavior[BT])
/**
* Start a new behavior chain starting with this case.
*
* @param type type of message to match
* @param handler action to apply if the type matches
* @tparam T type of behavior to create
* @tparam M type of message to match
* @return a new behavior with the specified handling appended
*/
def message[T, M <: T](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onMessage(`type`, handler)
/**
* Start a new behavior chain starting with this predicated case.
*
* @param type type of message to match
* @param test a predicate that will be evaluated on the argument if the type matches
* @param handler action to apply if the type matches and the predicate returns true
* @tparam T type of behavior to create
* @tparam M type of message to match
* @return a new behavior with the specified handling appended
*/
def message[T, M <: T](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onMessage(`type`, test, handler)
/**
* Start a new behavior chain starting with a handler without compile time type check.
*
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. List.class and (List<String> list) -> {...}
*
* @param type type of message to match
* @param handler action to apply when the type matches
* @return a new behavior with the specified handling appended
*/
def messageUnchecked[T, M <: T](`type`: Class[_ <: T], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onMessageUnchecked(`type`, handler)
/**
* Start a new behavior chain starting with a handler for equal messages.
*
* @param msg the message to compare to
* @param handler action to apply when the message matches
* @tparam T type of behavior to create
* @return a new behavior with the specified handling appended
*/
def messageEquals[T](msg: T, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onMessageEquals(msg, handler)
/**
* Start a new behavior chain starting with this signal case.
*
* @param type type of signal to match
* @param handler action to apply if the type matches
* @tparam T type of behavior to create
* @tparam M type of signal to match
* @return a new behavior with the specified handling appended
*/
def signal[T, M <: Signal](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onSignal(`type`, handler)
/**
* Start a new behavior chain starting with this predicated signal case.
*
* @param type type of signals to match
* @param test a predicate that will be evaluated on the argument if the type matches
* @param handler action to apply if the type matches and the predicate returns true
* @tparam T type of behavior to create
* @tparam M type of signal to match
* @return a new behavior with the specified handling appended
*/
def signal[T, M <: Signal](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onSignal(`type`, test, handler)
/**
* Start a new behavior chain starting with this unchecked signal case.
*
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. GenMsg.class and (ActorContext ctx, GenMsg<String> list) -> {...}
*
* @param type type of signal to match
* @param handler action to apply when the type matches
* @return a new behavior with the specified handling appended
*/
def signalUnchecked[T, M <: Signal](`type`: Class[_ <: Signal], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onSignalUnchecked(`type`, handler)
/**
* Start a new behavior chain starting with a handler for this specific signal.
*
* @param signal the signal to compare to
* @param handler action to apply when the message matches
* @tparam T type of behavior to create
* @return a new behavior with the specified handling appended
*/
def signalEquals[T](signal: Signal, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
BehaviorBuilder.create[T].onSignalEquals(signal, handler)
}
private class BuiltBehavior[T](
private val messageHandlers: List[Case[T, T]],
private val signalHandlers: List[Case[T, Signal]]
) extends ExtensibleBehavior[T] {
override def receive(ctx: typed.ActorContext[T], msg: T): Behavior[T] = receive[T](ctx.asJava, msg, messageHandlers)
override def receiveSignal(ctx: typed.ActorContext[T], msg: Signal): Behavior[T] = receive[Signal](ctx.asJava, msg, signalHandlers)
@tailrec
private def receive[M](ctx: ActorContext[T], msg: M, handlers: List[Case[T, M]]): Behavior[T] =
handlers match {
case Case(cls, predicate, handler) :: tail ⇒
if (cls.isAssignableFrom(msg.getClass) && (predicate.isEmpty || predicate.get.apply(msg))) handler(ctx, msg)
else receive[M](ctx, msg, tail)
case _ ⇒
unhandled[T]
}
}