org.specs2.form.Cells.scala Maven / Gradle / Ivy
The newest version!
package org.specs2
package form
import scala.xml._
import xml.Nodex._
import text.NotNullStrings._
import main.Arguments
import execute._
import StandardResults._
import text.Markdown
/**
* A Cell is the Textual or Xml representation of a Form element: Field, Prop or Form.
* A more general XmlCell is also available to be able to input any kind of Xml inside a Form
*
* A Cell can be executed by executing the underlying element but also by setting the cell to a specific result (success or failure).
* This feature is used to display rows of values with were expected and found ok in Forms.
*
*/
trait Cell extends Text with Xml with Executable {
def setSuccess: Cell
def setFailure: Cell
/**
* execute the Cell and returns it
*/
def executeCell : Cell
}
/**
* Base type for anything returning some text
*/
trait Text {
/** @return a text representation */
def text: String
/** @return the width of the cell, without borders when it's a FormCell */
def width: Int = text.size
}
/**
* Base type for anything returning some xml
*/
trait Xml {
/** @return an xml representation */
def xml(implicit args: Arguments): NodeSeq
}
/**
* utility functions for creating xml for Cells
*/
object Xml {
/** @return the stacktraces of a Cell depending on its type and execution result */
def stacktraces(cell: Cell)(implicit args: Arguments): NodeSeq = cell match {
case FormCell(f: Form) => f.rows.map(stacktraces(_)).reduceNodes
case PropCell(_, Some(e @ Error(_, _))) => stacktraces(e)
case PropCell(_, Some(f @ Failure(_, _, _, _))) => stacktraces(f)
case FieldCell(_, Some(e @ Error(_, _))) => stacktraces(e)
case other => NodeSeq.Empty
}
private def stacktraces(row: Row)(implicit args: Arguments): NodeSeq = row.cells.map(stacktraces(_)).reduceNodes
private def stacktraces(e: Result with ResultStackTrace): NodeSeq =
{e.message.notNull+" ("+e.location+")"}
{e.stackTrace.map(st => {st})}
/** @return the number of columns for a given cell */
def colnumber(cell: Cell): Int = cell match {
case TextCell(_,_,_) => 1 // just the string
case FieldCell(_, _) => 3 // label + value + optional error
case PropCell(_, _) => 3 // label + value + optional error/failure
case EffectCell(_, _) => 2 // label + optional error
case FormCell(form) => if (form.rows.isEmpty) 1 else form.rows.map(_.cells.map(c => colnumber(c)).sum).max
case LazyCell(c) => colnumber(c)
case _ => 100 // not known by default, so a max value is chosen
}
}
/**
* Simple Cell embedding an arbitrary String
*/
case class TextCell(s: String, result: Option[Result] = None, decorator: Decorator = Decorator()) extends Cell with DecoratedProperty[TextCell] {
def text = s
def xml(implicit args: Arguments) = {decorateValue(Markdown.toXhtml(text))}
def execute = result.getOrElse(Skipped())
def setSuccess = TextCell(s, success)
def setFailure = TextCell(s, failure)
def executeCell = this
/** set a new Decorator */
def decoratorIs(d: Decorator) = copy(decorator = d)
override def equals(other: Any) = {
other match {
case TextCell(s1, result1, _) => s == s1 && result == result1
case _ => false
}
}
}
object TextCell {
def apply(s: String, result: Result): TextCell = new TextCell(s, Some(result))
}
/**
* Cell embedding a Field
*/
case class FieldCell(f: Field[_], result: Option[Result] = None) extends Cell {
def text = f.toString
def xml(implicit args: Arguments) = {
val executedValue = f.valueOrResult match {
case Left(e) => e
case Right(e) => e
}
val executedResult = execute
({f.decorateLabel(f.label)} unless f.label.isEmpty) ++
{f.decorateValue(executedValue)} ++
({executedResult.message} unless
!executedResult.isError)
}
private def statusName(r: Result) = r match {
case Skipped(_, _) => "info"
case _ => r.statusName
}
def execute = result.getOrElse(f.execute)
def setSuccess = setResult(success)
def setFailure = setResult(failure)
def setResult(r: Result) = FieldCell(f, Some(r))
def executeCell = FieldCell(f, result.orElse(Some(f.execute)))
}
/**
* Cell embedding a Eff
*/
case class EffectCell(e: Effect[_], result: Option[Result] = None) extends Cell {
def text = e.toString
def xml(implicit args: Arguments) = {
val executed = e.valueOrResult match {
case Left(r) => r
case Right(r) => r
}
val executedResult = execute
{e.decorateLabel(e.label)} ++
({executedResult.message} unless executedResult.isSuccess)
}
private def statusName(r: Result) = r match {
case Skipped(_, _) => "info"
case Success(_, _) => "info"
case _ => r.statusName
}
def execute = result.getOrElse(e.execute)
def setSuccess = EffectCell(e, Some(success))
def setFailure = EffectCell(e, Some(failure))
def executeCell = EffectCell(e, result.orElse(Some(e.execute)))
}
/**
* Cell embedding a Prop
*/
case class PropCell(p: Prop[_,_], result: Option[Result] = None) extends Cell {
def text = p.toString
def execute = result.getOrElse(p.execute)
def executeCell = PropCell(p, result.orElse(Some(p.execute)))
def setSuccess = PropCell(p, Some(success))
def setFailure = PropCell(p, Some(failure))
def xml(implicit args: Arguments): NodeSeq = {
val executed = result.getOrElse(skipped)
({p.decorateLabel(p.label)} unless p.label.isEmpty) ++
({p.decorateValue(p.expectedValue.right.toOption.getOrElse(""))} unless !p.expectedValue.right.toOption.isDefined) ++
({executed.message} unless (executed.isSuccess || executed.message.isEmpty))
}
}
/**
* Cell embedding a Form
*/
class FormCell(_form: =>Form, result: Option[Result] = None) extends Cell {
lazy val form = _form
def text: String = form.text
def xml(implicit args: Arguments) = form.toCellXml(args)
def execute = result.getOrElse(form.execute)
def executeCell = {
lazy val executed = result.map(r => form).getOrElse(form.executeForm)
new FormCell(executed, result.orElse(Some(executed.execute)))
}
def setSuccess = new FormCell(form.setSuccess, Some(success))
def setFailure = new FormCell(form.setFailure, Some(failure))
/**
* @return the width of a form when inlined.
* It is the width of its text size minus 4, which is the size of the borders "| " and " |"
*/
override def width = text.split("\n").map((_:String).size).max[Int] - 4
}
object FormCell {
def unapply(cell: FormCell): Option[Form] = Some(cell.form)
}
/** Proxy to a cell that's not evaluated right away when added to a row */
class LazyCell(_cell: =>Cell) extends Cell {
lazy val cell = _cell
def text: String = cell.text
def xml(implicit args: Arguments) = cell.xml(args)
def execute = cell.execute
def executeCell = cell.executeCell
def setSuccess = cell.setSuccess
def setFailure = cell.setFailure
}
object LazyCell {
def unapply(cell: LazyCell): Option[Cell] = Some(cell.cell)
}
/** This cell can contain any xml */
class XmlCell(_theXml: =>NodeSeq) extends Cell {
lazy val theXml = _theXml
def text: String = theXml.text
def xml(implicit args: Arguments) = theXml
def execute = success
def executeCell = this
def setSuccess = this
def setFailure = this
}
object XmlCell {
def unapply(cell: XmlCell): Option[NodeSeq] = Some(cell.theXml)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy