/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: NodeSeq.scala 17988 2009-06-03 12:31:59Z extempore $ package scala.xml import collection.immutable import collection.generic._ import collection.mutable.ListBuffer /** This object ... * * @author Burak Emir * @version 1.0 */ object NodeSeq { final val Empty = fromSeq(Nil) def fromSeq(s: Seq[Node]): NodeSeq = new NodeSeq { def theSeq = s } type Coll = NodeSeq implicit def builderFactory: BuilderFactory[Node, NodeSeq, Coll] = new BuilderFactory[Node, NodeSeq, Coll] { def apply(from: Coll) = newBuilder } def newBuilder: Builder[Node, NodeSeq] = new ListBuffer[Node] mapResult fromSeq implicit def seqToNodeSeq(s: Seq[Node]): NodeSeq = fromSeq(s) } /** This class implements a wrapper around <code>Seq[Node]</code> that * adds XPath and comprehension methods. * * @author Burak Emir * @version 1.0 */ abstract class NodeSeq extends immutable.Sequence[Node] with SequenceTemplate[Node, NodeSeq] { import NodeSeq.seqToNodeSeq // import view magic for NodeSeq wrappers /** Creates a list buffer as builder for this class */ override protected[this] def newBuilder = NodeSeq.newBuilder def theSeq: Seq[Node] def length = theSeq.length override def iterator = theSeq.iterator def apply(i: Int): Node = theSeq(i) def apply(f: Node => Boolean): NodeSeq = filter(f) /** structural equality */ override def equals(x: Any): Boolean = x match { case z:Node => (length == 1) && z == apply(0) case z:Seq[_] => sameElements(z) case z:String => text == z case _ => false } /** Projection function. Similar to XPath, use <code>this \ "foo"</code> * to get a list of all elements of this sequence that are labelled with * <code>"foo"</code>. Use <code>\ "_"</code> as a wildcard. Use * <code>ns \ "@foo"</code> to get the unprefixed attribute "foo". * Use <code>ns \ "@{uri}foo"</code> to get the prefixed attribute * "pre:foo" whose prefix "pre" is resolved to the namespace "uri". * For attribute projections, the resulting NodeSeq attribute values are * wrapped in a Group. * There is no support for searching a prefixed attribute by * its literal prefix. * The document order is preserved. * * @param that ... * @return ... */ def \(that: String): NodeSeq = { def atResult = { def fail = throw new IllegalArgumentException(that) lazy val y = this(0) val attr = if (that.length == 1) fail else if (that(1) == '{') { val i = that indexOf '}' if (i == -1) fail val (uri, key) = (that.substring(2,i), that.substring(i+1, that.length())) if (uri == "" || key == "") fail else y.attribute(uri, key) } else y.attribute(that.substring(1)) attr match { case Some(x) => Group(x) case _ => NodeSeq.Empty } } def makeSeq(cond: (Node) => Boolean) = NodeSeq fromSeq (this flatMap (_.child) filter cond) that match { case "_" => makeSeq(!_.isAtom) case _ if (that(0) == '@' && this.length == 1) => atResult case _ => makeSeq(_.label == that) } } /** projection function. Similar to XPath, use <code>this \\ 'foo</code> * to get a list of all elements of this sequence that are labelled with * <code>"foo"</code>. Use <code>\\ "_"</code> as a wildcard. Use * <code>ns \\ "@foo"</code> to get the unprefixed attribute "foo". * Use <code>ns \\ "@{uri}foo"</code> to get each prefixed attribute * "pre:foo" whose prefix "pre" is resolved to the namespace "uri". * For attribute projections, the resulting NodeSeq attribute values are * wrapped in a Group. * There is no support for searching a prefixed attribute by * its literal prefix. * The document order is preserved. * * @param that ... * @return ... */ def \\ (that: String): NodeSeq = { def filt(cond: (Node) => Boolean) = this flatMap (_.descendant_or_self) filter cond that match { case "_" => filt(!_.isAtom) case _ if that(0) == '@' => filt(!_.isAtom) flatMap (_ \ that) case _ => filt(x => !x.isAtom && x.label == that) } } override def toString(): String = theSeq.mkString def text: String = this map (_.text) mkString }