From 5d224d6198a78fbf0c666f11ceddbd3a67a37b96 Mon Sep 17 00:00:00 2001 From: Tim Romberg Date: Fri, 12 Feb 2010 11:05:03 +0100 Subject: [PATCH] define templates in outer section --- .../be/romberg/liftweb/util/MBindHelper.scala | 136 +++++++++++++----- 1 file changed, 101 insertions(+), 35 deletions(-) diff --git a/src/main/scala/be/romberg/liftweb/util/MBindHelper.scala b/src/main/scala/be/romberg/liftweb/util/MBindHelper.scala index 0d58a58..1c3fe1f 100644 --- a/src/main/scala/be/romberg/liftweb/util/MBindHelper.scala +++ b/src/main/scala/be/romberg/liftweb/util/MBindHelper.scala @@ -41,13 +41,20 @@ trait MBindHelper { private val _currentInputTemplate = new ThreadGlobal[NodeSeq] private val _currentCaptionTemplate = new ThreadGlobal[NodeSeq] + /** + * The default per-field show template to be used when no such template is defined with the tag + */ var defaultShowTemplate = var defaultShowEmptyTemplate = NodeSeq.Empty // IntelliJ gets confused by entity references emsp (u2003) and ensp(2002) var defaultInputTemplate =

{"\u2003"}
{"\u2002"}

var defaultCaptionTemplate = + /** + * The current Node, e.g. accessible by special bindings. + */ def currentNode = _currentNode.value + def currentShowTemplate = { val res = _currentShowTemplate.value if (res == null) defaultShowTemplate else res @@ -68,8 +75,13 @@ trait MBindHelper { if (res == null) defaultCaptionTemplate else res } + /** + * Simple check for emptiness of a given field. Current implementation only treats Mapped{Long, Int, String, Binary, Text, Date, EnumList}. + * Override to add more field classes. Note that a field is also considered empty if asHtml delivers XHTML without any text content + */ def isFieldEmpty[A <: Mapper[A]](field:MappedField[_,A]): Boolean = { (field.isInstanceOf[MappedLong[_]] && (field.asInstanceOf[MappedLong[A]].is == 0L)) || + (field.isInstanceOf[MappedInt[_]] && (field.asInstanceOf[MappedInt[A]].is == 0)) || (field.isInstanceOf[MappedString[_]] && {val s=field.asInstanceOf[MappedString[A]].is; (s==null) || (s.length == 0)}) || (field.isInstanceOf[MappedBinary[_]] && {val b=field.asInstanceOf[MappedBinary[A]].is; (b==null) || (b.length == 0)}) || (field.isInstanceOf[MappedText[_]] && {val b=field.asInstanceOf[MappedText[A]].is; (b==null) || (b.length == 0)}) || @@ -77,11 +89,18 @@ trait MBindHelper { (field.isInstanceOf[MappedEnumList[_,_]] && (field.asInstanceOf[MappedEnumList[A,_]].is.length == 0)) } + /** + * The default production executed when a show tag name matches a field name. + * Special bindings can use this to display fields of a lookup entity, for example. + */ def defaultShowField[A <: Mapper[A]](field:MappedField[_,A]): NodeSeq = { if (isFieldEmpty(field)) showEmpty(field.displayName) else showValueIfNotEmpty(field.displayName, field.asHtml) } + /** + * Apply the current Show template to the given parameters. Don't check for emptiness of the value. + */ def showValue(caption:NodeSeq, value:NodeSeq):NodeSeq = BindHelpers.bind("field", currentShowTemplate, "caption" -> caption, "value" -> value @@ -90,13 +109,23 @@ trait MBindHelper { def showValue(caption:String, value:NodeSeq):NodeSeq = showValue(Text(caption), value) def showValue(caption:String, value:String):NodeSeq = showValue(Text(caption), Text(value)) - def showValueIfNotEmpty(caption:String, value:NodeSeq):NodeSeq = if (value.text.length > 0) showValue(caption, value) else Nil - def showValueIfNotEmpty(caption:String, value:String):NodeSeq = if (value.length > 0) showValue(caption, value) else Nil - + /** + * Check if value has text content. If yes, execute showValue, otherwise showEmpty + */ + def showValueIfNotEmpty(caption:String, value:NodeSeq):NodeSeq = if (value.text.length > 0) showValue(caption, value) else showEmpty(caption) + def showValueIfNotEmpty(caption:String, value:String):NodeSeq = if (value.length > 0) showValue(caption, value) else showEmpty(caption) + + /** + * Apply current showEmpty template to caption parameter + */ def showEmpty(caption:String):NodeSeq = BindHelpers.bind("field", currentShowEmptyTemplate, "caption" -> caption ) + /** + * The default production executed when an input tag name matches a field name. + * For a field hint to be displayed, the field must extend HintedField + */ def defaultInputField[A <: Mapper[A]](field:MappedField[_,A]): NodeSeq = { val hint:String = if (field.isInstanceOf[HintedField[_, _]]) field.asInstanceOf[HintedField[Any,A]].fieldHintWithRequired @@ -110,6 +139,9 @@ trait MBindHelper { ) } + /** + * Apply the current input template to the given parameters. This method is not called by defaultInputField. + */ def makeInputField(caption:NodeSeq, input:NodeSeq, hint:NodeSeq, message:NodeSeq):NodeSeq = { BindHelpers.bind("field", currentInputTemplate, "caption" -> caption, @@ -119,15 +151,66 @@ trait MBindHelper { ) } + /** + * Default production for caption tags that match a field name (bindMappers) + */ def defaultFieldCaption[A <: Mapper[A]](field:MappedField[_,A]): NodeSeq = { makeCaption(Text(field.displayName)) } + /** + * Apply current caption template to given parameter + */ def makeCaption(caption:NodeSeq) = BindHelpers.bind("field", currentCaptionTemplate, "caption" -> caption) + private def setTemplate(s:Elem) { + s.label match { + case "show" => { + if (s.child.length > 0) + _currentShowTemplate(s.child) + else + _currentShowTemplate(null) + } + case "showEmpty" => { + if (s.child.length > 0) + _currentShowEmptyTemplate(s.child) + else + _currentShowEmptyTemplate(null) + } + case "input" => { + if (s.child.length > 0) + _currentInputTemplate(s.child) + else + _currentInputTemplate(null) + } + case "caption" => { + if (s.child.length > 0) + _currentCaptionTemplate(s.child) + else + _currentCaptionTemplate(null) + } + case _ => Text("ERROR: invalid template tag") + } + } + + private def resetTemplates { + _currentShowTemplate(null) + _currentShowEmptyTemplate(null) + _currentInputTemplate(null) + _currentCaptionTemplate(null) + } + /** + * Bind a single mapper instance to the given template. Use NoSpecialBinding if you only want to automatically bind to the fields of the Mapper + */ def bindMapper[A <: Mapper[A]](template: NodeSeq, data: Mapper[A], specialBinding: PartialFunction[String, NodeSeq]): NodeSeq = { + val res = doBindMapper(template, data, specialBinding) + resetTemplates + res + } + + private def doBindMapper[A <: Mapper[A]](template: NodeSeq, data: Mapper[A], specialBinding: PartialFunction[String, NodeSeq]): NodeSeq = { def in_bind(xml:NodeSeq):NodeSeq = xml.flatMap { case s:Elem => s.prefix match { case "show" => { @@ -150,35 +233,7 @@ trait MBindHelper { if (s.child.length > 0) _currentInputTemplate(saveTemplate) res } - case "template" => { - s.label match { - case "show" => { - if (s.child.length > 0) - _currentShowTemplate(s.child) - else - _currentShowTemplate(null) - - NodeSeq.Empty - } - case "showEmpty" => { - if (s.child.length > 0) - _currentShowEmptyTemplate(s.child) - else - _currentShowEmptyTemplate(null) - - NodeSeq.Empty - } - case "input" => { - if (s.child.length > 0) - _currentInputTemplate(s.child) - else - _currentInputTemplate(null) - - NodeSeq.Empty - } - case _ => Text("ERROR: invalid template tag") - } - } + case "template" => setTemplate(s); NodeSeq.Empty case _ => Elem(s.prefix, s.label, s.attributes, s.scope, in_bind(s.child) : _*) } case Group(nodes) => Group(in_bind(nodes)) @@ -188,11 +243,17 @@ trait MBindHelper { in_bind(template) } + /** + * Empty binding to be used with bindMapper + */ object NoSpecialBinding extends PartialFunction[String, NodeSeq] { override def isDefinedAt(x:String) = false override def apply(v1:String) = NodeSeq.Empty } + /** + * Empty binding to be used with bindMappers + */ object NoSpecialListBinding extends PartialFunction[(Option[_],Int,String), NodeSeq] { override def isDefinedAt(x:(Option[_],Int,String)) = false override def apply(v1:(Option[_],Int,String)) = NodeSeq.Empty @@ -227,18 +288,19 @@ trait MBindHelper { if (s.child.length > 0) _currentCaptionTemplate(saveTemplate) res case "list" => s.label match { - case"count" => Text(count.toString) + case "count" => Text(count.toString) case "each" => { val oddClass = new UnprefixedAttribute("class", s.attribute("oddClass"), Null) val evenClass = new UnprefixedAttribute("class", s.attribute("evenClass"), Null) var index = 0 data.flatMap(mapper => { index += 1 - bindMapper(s.child, mapper, new MappedPF(mapper, index, specialBinding)) + doBindMapper(s.child, mapper, new MappedPF(mapper, index, specialBinding)) .flatMap(applyClass(if ((index % 2) == 0) evenClass else oddClass))}) } case _ => NodeSeq.Empty } + case "template" => setTemplate(s); NodeSeq.Empty case _ => Elem(s.prefix, s.label, s.attributes, s.scope, in_bind(s.child) : _*) } case Group(nodes) => Group(in_bind(nodes)) @@ -246,7 +308,11 @@ trait MBindHelper { } if (data.isEmpty) chooseTemplate("list", "ifEmpty", template) - else in_bind(template) + else { + val res = in_bind(template) + resetTemplates + res + } } }