Skip to content

Instantly share code, notes, and snippets.

@Rogach
Created September 10, 2013 04:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rogach/6505001 to your computer and use it in GitHub Desktop.
Save Rogach/6505001 to your computer and use it in GitHub Desktop.
Macro to display case class hierarchy as Swing JTree.
package dxf.macros
import scala.language.experimental.macros
import scala.reflect.macros.Context
import scala.swing._
import scala.swing.Swing._
import javax.swing._
import javax.swing.tree.DefaultMutableTreeNode
object ShowTree {
def showTree[T](obj: T) = macro _showTree[T]
def caseClassAccessors(c: Context)(tpe: c.universe.Type): List[c.universe.MethodSymbol] = {
import c.universe._
tpe.declarations.collect {
case acc: MethodSymbol if acc.isCaseAccessor => acc
}.toList
}
def nodes(c: Context)(obj: c.Expr[Any])(tpe: c.universe.Type): c.Expr[DefaultMutableTreeNode] = {
import c.universe._
if (tpe.normalize.typeSymbol.asClass.isCaseClass) {
val node = newTermName(c.fresh("node$"))
val nodeVal = ValDef(Modifiers(), node, TypeTree(), reify {
new DefaultMutableTreeNode(c.literal(tpe.toString.split("\\.").last).splice)
}.tree)
val nodeExpr = c.Expr[DefaultMutableTreeNode](Ident(node))
val children = caseClassAccessors(c)(tpe).map { acc =>
val fieldName = c.literal(acc.name.toString)
val fieldValue = c.Expr(Select(obj.tree.duplicate, acc.name))
if (acc.returnType <:< typeOf[Iterable[Any]]) {
val TypeRef(_, _, typeParam :: Nil) = acc.returnType
val nodesCall = nodes(c)(c.Expr(Ident(newTermName("v"))))(typeParam)
reify {
val listNode = new DefaultMutableTreeNode(fieldName.splice)
fieldValue.asInstanceOf[c.Expr[Iterable[Any]]].splice.foreach { v =>
listNode.add(nodesCall.splice)
}
nodeExpr.splice.add(listNode)
}
} else if (acc.returnType.typeSymbol.isClass &&
acc.returnType.typeSymbol.asClass.isCaseClass) {
val subNodes = nodes(c)(fieldValue)(acc.returnType)
reify {
nodeExpr.splice.add(subNodes.splice)
}
} else {
// handle all other types
reify {
nodeExpr.splice.add(new DefaultMutableTreeNode(
s"${fieldName.splice} : ${fieldValue.splice.toString}"))
}
}
}
val addNodes = c.Expr[Unit](Block(children.map(_.tree), Literal(Constant(()))))
reify {
c.Expr[Unit](nodeVal).splice
addNodes.splice
nodeExpr.splice
}
} else if (tpe.typeSymbol.asClass.isTrait) {
if (!tpe.typeSymbol.asClass.isSealed)
c.abort(c.enclosingPosition, s"Can't handle non-sealed trait ${tpe}")
else {
c.Expr[DefaultMutableTreeNode](Match(
obj.tree.duplicate,
tpe.normalize.typeSymbol.asClass.knownDirectSubclasses.map(_.asType.toType).toList.map { tp =>
val name = newTermName(c.fresh("cl$"))
CaseDef(
Bind(name, Typed(Ident(nme.WILDCARD), TypeTree(tp))),
EmptyTree,
nodes(c)(c.Expr[Any](Ident(name)))(tp).tree)
}
))
}
} else {
reify {
new DefaultMutableTreeNode(
s"${obj.splice.toString} :: ${c.literal(tpe.toString).splice}")
}
}
}
def _showTree[T](c: Context)(obj: c.Expr[T])(implicit wtt: c.WeakTypeTag[T]): c.Expr[Unit] = {
import c.universe._
val root = nodes(c)(obj)(wtt.tpe)
reify {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel")
val frame = new MainFrame {
contents = new ScrollPane {
contents = Component.wrap(new JTree(root.splice))
}
}
frame.visible = true
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment