From 8a4fca72f786ffd0d389bc5f0af59063b9b03ff4 Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 31 Dec 2012 18:38:06 +0100 Subject: [PATCH] add selfChannel and createChildren --- .../scala/akka/channels/ChannelSpec.scala | 54 +++++++++++++++++-- .../main/scala/akka/channels/Channels.scala | 45 +++++++++++++++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/akka-macro-tests/src/test/scala/akka/channels/ChannelSpec.scala b/akka-macro-tests/src/test/scala/akka/channels/ChannelSpec.scala index 056ee6ec34..7263ebc3c9 100644 --- a/akka-macro-tests/src/test/scala/akka/channels/ChannelSpec.scala +++ b/akka-macro-tests/src/test/scala/akka/channels/ChannelSpec.scala @@ -13,7 +13,7 @@ import scala.tools.reflect.ToolBoxError object ChannelSpec { trait Msg - + trait A extends Msg object A extends A object A1 extends A @@ -21,10 +21,10 @@ object ChannelSpec { trait B extends Msg object B extends B - + trait C extends Msg object C extends C - + trait D extends Msg object D extends D @@ -49,6 +49,24 @@ object ChannelSpec { x ! C } } + + // pos compile test for children + class Children extends Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] { + val c = createChild(new Channels[Parent[A :-: TNil], Channel[B, C]:=: TNil] { + channel[B] { case (B, s) ⇒ s ! C } + }) + + var client: ActorRef = _ + channel[A] { + case (A, s) ⇒ c ! B; client = sender + } + channel[C] { + case (C, _) ⇒ client ! C + } + + createChild(new Channels[Parent[C :-: TNil], TNil]) + createChild(new Channels[Parent[A :-: C :-: TNil], TNil]) + } } class ChannelSpec extends AkkaSpec with ImplicitSender { @@ -151,6 +169,36 @@ class ChannelSpec extends AkkaSpec with ImplicitSender { }.message must include("This ChannelRef does not support messages of type akka.channels.ChannelSpec.C.type") } + "not permit Nothing children" in { + intercept[ToolBoxError] { + eval(""" + |import akka.channels._ + |import ChannelSpec._ + |new Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] { + | createChild(new Channels) + |} + """.stripMargin) + }.message must include("Parent argument must not be Nothing") + } + + "not permit too demanding children" in { + intercept[ToolBoxError] { + eval(""" + |import akka.channels._ + |import ChannelSpec._ + |new Channels[Parent[TNil], Channel[A, B] :=: Channel[C, D] :=: TNil] { + | createChild(new Channels[Parent[B :-: TNil], TNil]) + |} + """.stripMargin) + }.message must include("This actor cannot support a child requiring channels akka.channels.ChannelSpec.B") + } + + "have a working selfChannel" in { + val ref = ChannelExt(system).actorOf(new Children) + ref ! A + expectMsg(C) + } + } } \ No newline at end of file diff --git a/akka-macros/src/main/scala/akka/channels/Channels.scala b/akka-macros/src/main/scala/akka/channels/Channels.scala index 4f9b037a0a..19e0bac74b 100644 --- a/akka-macros/src/main/scala/akka/channels/Channels.scala +++ b/akka-macros/src/main/scala/akka/channels/Channels.scala @@ -10,11 +10,16 @@ import scala.reflect.macros.Context import scala.reflect.runtime.universe.TypeTag import scala.reflect.macros.Universe import scala.runtime.AbstractPartialFunction +import akka.actor.Props -trait Channels[P <: Parent[_], C <: ChannelList] extends Actor { +class Channels[P <: Parent[_], C <: ChannelList: TypeTag] extends Actor { import Channels._ + def createChild[Pa <: Parent[_], Ch <: ChannelList](factory: Channels[Pa, Ch]): ChannelRef[Ch] = macro createChildImpl[C, Pa, Ch] + + implicit val selfChannel = new ChannelRef[C](self) + /* * Warning Ugly Hack Ahead * @@ -27,7 +32,7 @@ trait Channels[P <: Parent[_], C <: ChannelList] extends Actor { */ private var behavior = List.empty[Recv[Any, ChannelList]] - def channel[T]: Channels[P, C]#Behaviorist[T, _ <: ChannelList] = macro Channels.channelImpl[T, C, P, ChannelList] + def channel[T]: Channels[P, C]#Behaviorist[T, _ <: ChannelList] = macro channelImpl[T, C, P, ChannelList] protected def _channel[T, Ch <: ChannelList] = new Behaviorist[T, Ch] protected class Behaviorist[T, Ch <: ChannelList] { @@ -87,6 +92,42 @@ object Channels { } } + def createChildImpl[C <: ChannelList: c.WeakTypeTag, Pa <: Parent[_]: c.WeakTypeTag, Ch <: ChannelList: c.WeakTypeTag]( + c: Context { + type PrefixType = Channels[_, C] + })(factory: c.Expr[Channels[Pa, Ch]]): c.Expr[ChannelRef[Ch]] = { + + import c.universe._ + if (weakTypeOf[Pa] =:= weakTypeOf[Nothing]) { + c.abort(c.enclosingPosition, "Parent argument must not be Nothing") + } + if (weakTypeOf[Ch] =:= weakTypeOf[Nothing]) { + c.abort(c.enclosingPosition, "channel list must not be Nothing") + } + val missing = missingChannels(c.universe)(weakTypeOf[C], parentChannels(c.universe)(weakTypeOf[Pa])) + if (missing.isEmpty) { + implicit val t = c.TypeTag[Ch](c.weakTypeOf[Ch]) + reify(new ChannelRef[Ch](c.prefix.splice.context.actorOf(Props(factory.splice)))) + } else { + c.error(c.enclosingPosition, s"This actor cannot support a child requiring channels ${missing mkString ", "}") + reify(null) + } + } + + /** + * get all required channels from a Parent[_] + */ + final def parentChannels(u: Universe)(list: u.Type): List[u.Type] = { + import u._ + def rec(l: u.Type, acc: List[u.Type]): List[u.Type] = l match { + case TypeRef(_, _, ch :: tail :: Nil) ⇒ rec(tail, ch :: acc) + case _ ⇒ acc.reverse + } + list match { + case TypeRef(_, _, ch :: Nil) ⇒ rec(ch, Nil) + } + } + /** * find all input channels matching the given message type and return a * list of their respective reply channels