Permalink
Browse files

Merged the type-class based CSS Selector transforms in

  • Loading branch information...
1 parent 27bd39e commit e18d32fc36d21d8501bb8922dd51e20051a2cda2 @dpp dpp committed Aug 27, 2012

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -17,10 +17,10 @@
package net.liftweb
package util
-import scala.util.parsing.combinator.{Parsers, ImplicitConversions}
+
+import scala.util.parsing.combinator.{PackratParsers, Parsers, ImplicitConversions}
import scala.xml.NodeSeq
import net.liftweb.common._
-import util.EnclosedSelector
sealed trait CssSelector {
def subNodes: Box[SubNode]
@@ -81,13 +81,17 @@ final case class PrependKidsSubNode() extends SubNode with WithKids {
}
final case object DontMergeAttributes extends SubNode {
+ }
+final case class SurroundKids() extends SubNode with WithKids {
+ def transform(original: NodeSeq, newNs: NodeSeq): NodeSeq = newNs ++ original // FIXME
}
final case class AppendKidsSubNode() extends SubNode with WithKids {
def transform(original: NodeSeq, newNs: NodeSeq): NodeSeq = original ++ newNs
}
+
sealed trait AttributeRule
final case class AttrSubNode(attr: String) extends SubNode with AttributeRule
@@ -99,7 +103,7 @@ final case class SelectThisNode(kids: Boolean) extends SubNode
/**
* Parse a subset of CSS into the appropriate selector objects
*/
-object CssSelectorParser extends Parsers with ImplicitConversions {
+object CssSelectorParser extends PackratParsers with ImplicitConversions {
private val cache = new LRUMap[String, CssSelector](25000)
/**
@@ -150,16 +154,18 @@ object CssSelectorParser extends Parsers with ImplicitConversions {
colonMatch)
}
- private def fixAll(all: List[CssSelector], sn: Option[SubNode]): CssSelector = (all, sn) match {
- case (r :: Nil, None) => r
- case (r :: Nil, Some(sn)) => r.withSubnode(sn)
- case (lst, None) => lst.reduceRight((b, a) => EnclosedSelector(a, b))
- case (lst, Some(sn)) => (lst.dropRight(1) ::: lst.takeRight(1).map(_.withSubnode(sn))).reduceRight((b, a) => EnclosedSelector(a, b))
+ private def fixAll(all: List[CssSelector], sn: Option[SubNode]): CssSelector = {
+ (all, sn) match {
+ case (r :: Nil, None) => r
+ case (r :: Nil, Some(sn)) => r.withSubnode(sn)
+ case (lst, None) => lst.reduceRight((b, a) => EnclosedSelector(b, a))
+ case (lst, Some(sn)) => (lst.dropRight(1) ::: lst.takeRight(1).map(_.withSubnode(sn))).reduceRight((b, a) => EnclosedSelector(b, a))
+ }
}
private lazy val topParser: Parser[CssSelector] =
- phrase(rep1(_idMatch | _nameMatch | _classMatch | _attrMatch | _elemMatch |
- _colonMatch | _starMatch) ~ opt(subNode)) ^^ {
+ phrase(rep1((_idMatch | _nameMatch | _classMatch | _attrMatch | _elemMatch |
+ _colonMatch | _starMatch) <~ (rep1(' ') | 26.toChar)) ~ opt(subNode)) ^^ {
case all ~ None if all.takeRight(1).head == StarSelector(Empty) =>
fixAll(all.dropRight(1), Some(KidsSubNode()))
case all ~ sn => fixAll(all, sn)
@@ -261,7 +267,7 @@ object CssSelectorParser extends Parsers with ImplicitConversions {
private lazy val number: Parser[Char] = elem("number", isNumber)
- private lazy val subNode: Parser[SubNode] = rep1(' ') ~>
+ private lazy val subNode: Parser[SubNode] = rep(' ') ~>
((opt('*') ~ '[' ~> attrName <~ '+' ~ ']' ^^ {
name => AttrAppendSubNode(name)
}) |
@@ -270,7 +276,9 @@ object CssSelectorParser extends Parsers with ImplicitConversions {
}) | (opt('*') ~ '[' ~> attrName <~ ']' ^^ {
name => AttrSubNode(name)
}) |
+
('!' ~ '!' ^^ (a => DontMergeAttributes)) |
+ ('<' ~ '*' ~ '>') ^^ (a => SurroundKids()) |
('-' ~ '*' ^^ (a => PrependKidsSubNode())) |
('>' ~ '*' ^^ (a => PrependKidsSubNode())) |
('*' ~ '+' ^^ (a => AppendKidsSubNode())) |
@@ -0,0 +1,143 @@
+package net.liftweb
+package util
+
+import common._
+import xml._
+import java.util.{List => JavaList}
+// import scala.collection.JavaConverters._
+
+/**
+ * A trait that has some helpful implicit conversions from
+ * Iterable[NodeSeq], Seq[String], Box[String], and Option[String]
+ */
+trait IterableConst {
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq]
+}
+
+import scala.collection.JavaConversions._
+
+/**
+ * The implementation for a NodeSeq Iterable Const
+ */
+final case class NodeSeqIterableConst(it: Iterable[NodeSeq]) extends IterableConst {
+ def this(it: JavaList[NodeSeq]) = this(it: Iterable[NodeSeq])
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = it.toSeq
+}
+
+/**
+ * The implementation for a NodeSeq => NodeSeq Iterable Const
+ */
+final case class NodeSeqFuncIterableConst(it: Iterable[NodeSeq => NodeSeq]) extends IterableConst {
+ def this(it: JavaList[NodeSeq => NodeSeq]) = this(it: Iterable[NodeSeq => NodeSeq])
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = Helpers.ensureUniqueId(it.map(_(nodeSeq)).toSeq)
+}
+
+/**
+ * The implementation for a Box[NodeSeq => Node] Iterable Const
+ */
+final case class BoxNodeSeqFuncIterableConst(it: Box[NodeSeq => NodeSeq]) extends IterableConst {
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = it.toList.map(_(nodeSeq))
+}
+
+/**
+ * The implementation for a Option[NodeSeq => Node] Iterable Const
+ */
+final case class OptionNodeSeqFuncIterableConst(it: Option[NodeSeq => NodeSeq]) extends IterableConst {
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = it.toList.map(_(nodeSeq))
+}
+
+/**
+ * Sequence of String iterable const
+ */
+final case class SeqStringIterableConst(it: Iterable[String]) extends IterableConst {
+ def this(it: JavaList[String]) = this(it: Iterable[String])
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = it.map(a => Text(a)).toSeq
+}
+
+/**
+ * Sequence of Bindable iterable const
+ */
+final case class SeqBindableIterableConst(it: Iterable[Bindable]) extends IterableConst {
+ def this(it: JavaList[Bindable]) = this(it: Iterable[Bindable])
+
+ def constList(nodeSeq: NodeSeq): Seq[NodeSeq] = it.map(_.asHtml).toSeq
+}
+
+/**
+ * The companion object that does the helpful promotion of common
+ * collection types into an IterableConst,
+ * e.g. Iterable[NodeSeq], Seq[String], Box[String], and Option[String]
+ */
+object IterableConst {
+ /**
+ * Converts anything that can be converted into an Iterable[NodeSeq]
+ * into an IterableConst. This includes Seq[NodeSeq]
+ */
+ implicit def itNodeSeq(it: Iterable[NodeSeq]): IterableConst =
+ NodeSeqIterableConst(it)
+
+ /**
+ * Converts anything that can be converted into an Box[NodeSeq]
+ */
+ implicit def boxNodeSeq(it: Box[NodeSeq]): IterableConst =
+ NodeSeqIterableConst(it.toList)
+
+ /**
+ * Converts anything that can be converted into an Box[NodeSeq]
+ */
+ implicit def optionNodeSeq(it: Option[NodeSeq]): IterableConst =
+ NodeSeqIterableConst(it.toList)
+
+ /**
+ * Converts anything that can be converted into an Iterable[NodeSeq]
+ * into an IterableConst. This includes Seq[NodeSeq], Option[NodeSeq],
+ * and Box[NodeSeq]
+ */
+ implicit def itNodeSeq(it: JavaList[NodeSeq]): IterableConst =
+ new NodeSeqIterableConst(it)
+
+ implicit def itNodeSeqFunc(it: Iterable[NodeSeq => NodeSeq]): IterableConst =
+ NodeSeqFuncIterableConst(it)
+
+ implicit def itNodeSeqFunc(it: JavaList[NodeSeq => NodeSeq]): IterableConst =
+ new NodeSeqFuncIterableConst(it)
+
+ implicit def boxNodeSeqFunc(it: Box[NodeSeq => NodeSeq]): IterableConst =
+ BoxNodeSeqFuncIterableConst(it)
+
+ implicit def optionNodeSeqFunc(it: Option[NodeSeq => NodeSeq]): IterableConst =
+ OptionNodeSeqFuncIterableConst(it)
+
+ implicit def itStringPromotable(it: Iterable[String]): IterableConst =
+ SeqStringIterableConst(it)
+
+ implicit def javaListStringPromotable(it: JavaList[String]): IterableConst =
+ new SeqStringIterableConst(it)
+
+ implicit def boxString(it: Box[String]): IterableConst =
+ SeqStringIterableConst(it.toList)
+
+ implicit def optionString(it: Option[String]): IterableConst =
+ SeqStringIterableConst(it.toList)
+
+ implicit def itBindable(it: Iterable[Bindable]): IterableConst =
+ SeqBindableIterableConst(it)
+
+ implicit def itBindable(it: JavaList[Bindable]): IterableConst =
+ new SeqBindableIterableConst(it)
+
+
+ implicit def boxBindablePromotable(it: Box[Bindable]): IterableConst =
+ SeqBindableIterableConst(it.toList)
+
+ implicit def optionBindablePromotable(it: Option[Bindable]): IterableConst =
+ SeqBindableIterableConst(it.toList)
+
+ implicit def optionStringPromotable[T](o: Option[T])(implicit view:T=>StringPromotable) = optionString(o.map(view(_).toString))
+}
+
@@ -0,0 +1,47 @@
+package net.liftweb
+package util
+
+import common._
+import xml.{Text, NodeSeq}
+
+sealed trait IterableFunc extends Function1[NodeSeq, Seq[NodeSeq]] {
+ def apply(ns: NodeSeq): Seq[NodeSeq]
+}
+
+object IterableFunc {
+ implicit def itNodeSeq[C <% Iterable[NodeSeq]](it: NodeSeq => C): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = it(in).toSeq
+ }
+
+ implicit def itNodeSeqPromotable(it: NodeSeq => NodeSeq): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = List(it(in))
+ }
+
+
+ implicit def itStringFuncPromotable(it: NodeSeq => String): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = it(in) match {
+ case null => List(NodeSeq.Empty)
+ case str => List(Text(str))}
+ }
+
+
+ implicit def itStringPromotable(it: NodeSeq => Seq[String]): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = it(in).filter(_ ne null).map(a => Text(a))
+ }
+
+ implicit def boxStringPromotable(it: NodeSeq => Box[String]): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = it(in).filter(_ ne null).toList.map(a => Text(a))
+ }
+
+
+ implicit def optionStringPromotable(it: NodeSeq => Option[String]): IterableFunc =
+ new IterableFunc {
+ def apply(in: NodeSeq): Seq[NodeSeq] = it(in).filter(_ ne null).toList.map(a => Text(a))
+ }
+}
+
@@ -0,0 +1,41 @@
+package net.liftweb.util
+
+/**
+ * This trait marks something that can be promoted into a String.
+ * The companion object has helpful conversions from Int,
+ * Symbol, Long, and Boolean
+ */
+trait StringPromotable
+
+object StringPromotable {
+ implicit def jsCmdToStrPromo(in: ToJsCmd): StringPromotable =
+ new StringPromotable {
+ override val toString = in.toJsCmd
+ }
+
+ implicit def jsCmdToStrPromo(in: (_, ToJsCmd)): StringPromotable =
+ new StringPromotable {
+ override val toString = in._2.toJsCmd
+ }
+
+
+ implicit def intToStrPromo(in: Int): StringPromotable =
+ new StringPromotable {
+ override val toString = in.toString
+ }
+
+ implicit def symbolToStrPromo(in: Symbol): StringPromotable =
+ new StringPromotable {
+ override val toString = in.name
+ }
+
+ implicit def longToStrPromo(in: Long): StringPromotable =
+ new StringPromotable {
+ override val toString = in.toString
+ }
+
+ implicit def booleanToStrPromo(in: Boolean): StringPromotable =
+ new StringPromotable {
+ override val toString = in.toString
+ }
+}
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-package net.liftweb
+package net.liftweb
+
+import common.{Empty, Full}
+import xml.NodeSeq
/**
@@ -28,4 +31,35 @@ package object util {
*/
@deprecated("Use Schedule", "2.3")
val ActorPing = Schedule
+
+ /**
+ * promote a String to a ToCssBindPromotor
+ */
+ implicit def strToCssBindPromoter(str: String): ToCssBindPromoter =
+ new ToCssBindPromoter(Full(str), CssSelectorParser.parse(str))
+
+ /**
+ * promote a String to a ToCssBindPromotor
+ */
+ implicit def cssSelectorToCssBindPromoter(sel: CssSelector): ToCssBindPromoter =
+ new ToCssBindPromoter(Empty, Full(sel))
+
+ /**
+ * Wrap a function and make sure it's a NodeSeq => NodeSeq. Much easier
+ * than explicitly casting the first parameter
+ *
+ * @param f the function
+ * @return a NodeSeq => NodeSeq
+ */
+ def nsFunc(f: NodeSeq => NodeSeq): NodeSeq => NodeSeq = f
+
+ /**
+ * Promote to an IterableConst when implicits won't do it for you
+ *
+ * @param ic the thing that can be promoted to an IterableConst
+ * @param f the implicit function that takes T and makes it an IterableConst
+ * @tparam T the type of the parameter
+ * @return an IterableConst
+ */
+ def itConst[T](ic: T)(implicit f: T => IterableConst): IterableConst = f(ic)
}
Oops, something went wrong.

0 comments on commit e18d32f

Please sign in to comment.