Skip to content

Instantly share code, notes, and snippets.

@Rogach
Created September 12, 2013 06:41
Show Gist options
  • Save Rogach/6533720 to your computer and use it in GitHub Desktop.
Save Rogach/6533720 to your computer and use it in GitHub Desktop.
Macro to display case class hierarchy as Swing JTree. (now uses quasiquotes)
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 children = caseClassAccessors(c)(tpe).map { acc =>
val fieldName = c.literal(acc.name.toString)
val fieldValue = c.Expr(q"$obj.${acc.name}")
if (acc.returnType <:< typeOf[Iterable[Any]]) {
val TypeRef(_, _, typeParam :: Nil) = acc.returnType
val nodesCall = nodes(c)(c.Expr(Ident(newTermName("v"))))(typeParam)
q"""
val listNode = new javax.swing.tree.DefaultMutableTreeNode($fieldName)
$fieldValue.foreach { v =>
listNode.add($nodesCall)
}
$node.add(listNode)
"""
} else if (acc.returnType.typeSymbol.isClass &&
acc.returnType.typeSymbol.asClass.isCaseClass ||
acc.returnType.typeSymbol.asClass.isTrait) {
val subNodes = nodes(c)(fieldValue)(acc.returnType)
q"$node.add($subNodes)"
} else {
// handle all other types
q"""$node.add(new javax.swing.tree.DefaultMutableTreeNode($fieldName+" : "+$fieldValue))"""
}
}
c.Expr(q"""
val $node = new javax.swing.tree.DefaultMutableTreeNode(${tpe.toString.split("\\.").last})
..$children
$node
""")
} 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(Match(
obj.tree.duplicate,
tpe.normalize.typeSymbol.asClass.knownDirectSubclasses.map(_.asType.toType).toList.map {tp =>
val name = newTermName(c.fresh("cl$"))
val body = nodes(c)(c.Expr[Any](Ident(name)))(tp).tree
cq"$name : $tp => $body"
}
))
}
} else {
c.Expr(q"""new javax.swing.tree.DefaultMutableTreeNode($obj+" :: "+${tpe.toString})""")
}
}
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)
val res = 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
}
println(res.tree)
res
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment