Skip to content

Commit

Permalink
define templates in outer section
Browse files Browse the repository at this point in the history
  • Loading branch information
tromberg committed Feb 12, 2010
1 parent 2a7cbcd commit 5d224d6
Showing 1 changed file with 101 additions and 35 deletions.
136 changes: 101 additions & 35 deletions src/main/scala/be/romberg/liftweb/util/MBindHelper.scala
Expand Up @@ -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 <template:show> tag
*/
var defaultShowTemplate = <field:value/>
var defaultShowEmptyTemplate = NodeSeq.Empty
// IntelliJ gets confused by entity references emsp (u2003) and ensp(2002)
var defaultInputTemplate = <p><label><field:caption /></label>{"\u2003"}<span class="fieldhint"><field:hint /></span><br /><field:input />{"\u2002"}<field:message errorClass="errorBox" warningClass="warningBox" noticeClass="noticeBox" /></p>
var defaultCaptionTemplate = <th><field:caption /></th>

/**
* 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
Expand All @@ -68,20 +75,32 @@ 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)}) ||
(field.isInstanceOf[MappedDate[_]] && (field.asInstanceOf[MappedDate[A]].is == null)) ||
(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
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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" => {
Expand All @@ -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))
Expand All @@ -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
Expand Down Expand Up @@ -227,26 +288,31 @@ 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))
case n => n
}

if (data.isEmpty) chooseTemplate("list", "ifEmpty", template)
else in_bind(template)
else {
val res = in_bind(template)
resetTemplates
res
}

}
}
Expand Down

0 comments on commit 5d224d6

Please sign in to comment.