Permalink
Browse files

Merge remote-tracking branch 'josharnold52/minorfixes'

  • Loading branch information...
2 parents 543739a + 28e9615 commit 61ba7ac1866568eb0f8fe7b01d2a6a2422a13dcf @djspiewak committed Oct 12, 2011
View
51 src/main/scala/com/codecommit/antixml/Attributes.scala
@@ -29,7 +29,7 @@
package com.codecommit.antixml
import scala.collection.generic.CanBuildFrom
-import scala.collection.immutable.{HashMap, Map, MapLike}
+import scala.collection.immutable.{Map, MapLike}
import scala.collection.mutable.Builder
import com.codecommit.antixml.util.OrderPreservingMap
@@ -85,18 +85,21 @@ import com.codecommit.antixml.util.OrderPreservingMap
*
* @see [[com.codecommit.antixml.QName]]
*/
-class Attributes(delegate: Map[QName, String]) extends Map[QName, String] with MapLike[QName, String, Attributes] {
+class Attributes private (delegate: OrderPreservingMap[QName, String]) extends Map[QName, String] with MapLike[QName, String, Attributes] {
import Node.CharRegex
for ((name, value) <- delegate) {
if (CharRegex.unapplySeq(value).isEmpty)
throw new IllegalArgumentException("Illegal character in attribute value '" + value + "'")
}
- override def empty = new Attributes(Map())
+ @deprecated("Use the factory methods on the Attributes companion instead","0.4")
+ def this(entries: Map[QName, String]) = this(OrderPreservingMap(entries.toSeq:_*))
+
+ override def empty = Attributes.empty
// totally shadowed by the overload; compiler won't even touch it without ascribing a supertype
- def +[B >: String](kv: (QName, B)) = delegate + kv
+ def +[B >: String](kv: (QName, B)): Map[QName, B] = delegate + kv
/**
* Special overload of the `+` method to return `Attributes` rather than
@@ -121,11 +124,47 @@ class Attributes(delegate: Map[QName, String]) extends Map[QName, String] with M
*/
def +(kv: (QName, String))(implicit d: DummyImplicit) = new Attributes(delegate + kv)
+ // totally shadowed by the overload; compiler won't even touch it without ascribing a supertype
+ override def + [B >: String] (kv1: (QName, B), kv2: (QName, B), kvs: (QName, B) *): Map[QName, B] = delegate + (kv1, kv2, kvs:_*)
+
+ /**
+ * Special overload of the multiple-argument `+` method to return `Attributes` rather than
+ * `Map[QName, String]`.
+ *
+ * See the corresponding overload of single-argument `+` for more details.
+ *
+ * @usecase def +(kv1: (QName, String), kv2: (QName, String), kvs: (QName, String) *): Attributes
+ */
+ def +(kv1: (QName, String), kv2: (QName, String), kvs: (QName, String) *)(implicit d: DummyImplicit) = new Attributes(delegate + (kv1,kv2,kvs:_*))
+
+
+ // totally shadowed by the overload; compiler won't even touch it without ascribing a supertype
+ override def updated [B >: String](key: QName, value: B): Map[QName, B] = this + ((key, value))
+
+ /**
+ * Special overload of the `updated` method to return `Attributes` rather than
+ * `Map[QName, String]`.
+ *
+ * See the corresponding overload of single-argument `+` for more details.
+ *
+ * @usecase def updated(key: QName, value: String): Attributes
+ */
+ def updated(key: QName, value: String)(implicit d: DummyImplicit) = new Attributes(delegate.updated(key,value))
+
+
def -(key: QName) = new Attributes(delegate - key)
def iterator = delegate.iterator
def get(key: QName) = delegate get key
+
+ //The following two overrides are recommended by MapLike for efficiency:
+ override def size: Int = delegate.size
+
+ override def foreach[U] (f:((QName,String)) => U) {
+ delegate.foreach(f)
+ }
+
}
/**
@@ -162,9 +201,9 @@ object Attributes {
def apply(from: Attributes) = apply()
}
- def newBuilder = OrderPreservingMap.newBuilder[QName, String] mapResult { m: Map[QName, String] => new Attributes(m) }
+ def newBuilder = OrderPreservingMap.newBuilder[QName, String] mapResult { m: OrderPreservingMap[QName, String] => new Attributes(m) }
- val empty = new Attributes(OrderPreservingMap())
+ val empty = new Attributes(OrderPreservingMap.empty[QName,String])
def apply(attrs: (QName, String)*) = if (attrs.isEmpty) empty else new Attributes(OrderPreservingMap(attrs: _*))
}
View
21 src/main/scala/com/codecommit/antixml/Zipper.scala
@@ -28,11 +28,10 @@
package com.codecommit.antixml
-import scala.collection.generic.CanBuildFrom
import com.codecommit.antixml.util.VectorCase
+import scala.collection.{IndexedSeqLike, GenTraversableOnce}
+import scala.collection.generic.{CanBuildFrom, FilterMonadic}
import scala.collection.immutable.{SortedMap, IndexedSeq, Seq}
-import scala.collection.IndexedSeqLike
-import scala.collection.GenTraversableOnce
import scala.collection.mutable.Builder
import Zipper._
@@ -204,6 +203,22 @@ trait Zipper[+A <: Node] extends Group[A] with IndexedSeqLike[A, Zipper[A]] { se
case _ => super.flatMap(f)(cbf)
}
}
+
+ override def withFilter(f: A => Boolean) = new WithFilter(List(f))
+
+ class WithFilter(filters: List[A => Boolean]) extends FilterMonadic[A, Zipper[A]] {
+ private[this] def sat(a: A) = filters forall { _(a) }
+
+ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Zipper[A], B, That]) =
+ self flatMap {a => if (sat(a)) Seq(f(a)) else Nil }
+
+ def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Zipper[A], B, That]) =
+ self flatMap {a => if (sat(a)) f(a) else Nil}
+
+ def foreach[B](f: A => B) = self foreach {a => if (sat(a)) f(a)}
+
+ def withFilter(p: A => Boolean) = new WithFilter(p :: filters)
+ }
override def toZipper = this
View
4 src/main/scala/com/codecommit/antixml/ZipperMergeStrategy.scala
@@ -119,7 +119,7 @@ object ZipperMergeStrategy {
object RequireLocal extends ZipperMergeStrategy {
override def apply(context: ZipperMergeContext) = context.directUpdate map {
case (e:Elem, _) if (e.children==context.original.children) => e.copy(children=context.indirectUpdate._1.children)
- case n => error("A conflict has been detected in the following node that cannot be resolved by the RequireLocal merge strategy:\n" + n)
+ case n => sys.error("A conflict has been detected in the following node that cannot be resolved by the RequireLocal merge strategy:\n" + n)
}
}
@@ -130,7 +130,7 @@ object ZipperMergeStrategy {
*/
object RequireConflictFree extends ZipperMergeStrategy {
override def apply(context: ZipperMergeContext): Nothing =
- error("A node and one or more of its descendents were contained in the same zipper.\n" +
+ sys.error("A node and one or more of its descendents were contained in the same zipper.\n" +
"Possible fixes include either using a different merge strategy or using a different selection\n" +
"operator.\n"+
context.original)
View
2 src/main/scala/com/codecommit/antixml/conversion.scala
@@ -145,7 +145,7 @@ private[antixml] sealed trait SecondPrecedenceConvertables extends ThirdPreceden
case e: xml.Elem => ElemConvertable(e)
case a: xml.Atom[String] => TextConvertable(a)
case r: xml.EntityRef => EntityRefConvertable(r)
- case g: xml.Group => error("xml.Group should never have been a Node; there is no sane conversion")
+ case g: xml.Group => sys.error("xml.Group should never have been a Node; there is no sane conversion")
}
}
}
View
2 src/main/scala/com/codecommit/antixml/node.scala
@@ -166,8 +166,6 @@ case class Elem(prefix: Option[String], name: String, attrs: Attributes, scope:
sw.toString
}
- override val hashCode = runtime.ScalaRunTime._hashCode(this)
-
def toGroup = Group(this)
}
View
6 src/main/scala/com/codecommit/antixml/util/OrderPreservingMap.scala
@@ -54,6 +54,12 @@ private[antixml] trait OrderPreservingMap[A,+B] extends Map[A,B] with MapLike[A,
override def empty:OrderPreservingMap[A,B] = OrderPreservingMap.empty
override def + [B1 >: B] (kv: (A, B1)): OrderPreservingMap[A, B1]
+
+ override def + [B1 >: B] (kv1: (A, B1), kv2: (A, B1), kvs: (A, B1) *): OrderPreservingMap[A, B1] =
+ self + kv1 + kv2 ++ kvs
+
+ override def updated [B1 >: B] (key: A, value: B1): OrderPreservingMap[A, B1] = self + ((key,value))
+
}
/**
View
4 src/main/scala/com/codecommit/antixml/util/vectorCases.scala
@@ -125,8 +125,8 @@ private[antixml] case object Vector0 extends VectorCase[Nothing] {
def +:[B](b: B) = Vector1(b)
def :+[B](b: B) = Vector1(b)
- def apply(index: Int) = error("Apply on empty vector")
- def updated[B](index: Int, b: B) = error("Updated on empty vector")
+ def apply(index: Int) = sys.error("Apply on empty vector")
+ def updated[B](index: Int, b: B) = sys.error("Updated on empty vector")
def ++[B](that: VectorCase[B]) = that
View
41 src/test/scala/com/codecommit/antixml/AttributesSpecs.scala
@@ -50,18 +50,51 @@ class AttributesSpecs extends Specification with ScalaCheck with XMLGenerators {
"support addition of qname attrs" in check { (attrs: Attributes, name: QName, value: String) =>
val attrsSafe = attrs - name
- val attrs2 = attrsSafe + (name -> value)
+ val attrs2:Attributes = attrsSafe + (name -> value)
attrs2 must havePairs(attrsSafe.toSeq: _*)
attrs2 must havePair(name -> value)
}
-
- "support addition of string attrs" in check { (attrs: Attributes, name: String, value: String) =>
+
+ "support addition of string attrs" in check { (attrs: Attributes, x: QName, value: String) =>
+ val name = x.name
+ val attrsSafe = attrs - name
+ val attrs2:Attributes = attrsSafe + (name -> value)
+ attrs2 must havePairs(attrsSafe.toSeq: _*)
+ attrs2 must havePair(QName(None, name) -> value)
+ }
+
+ "support multiple addition of qname attrs" in check { (attrs: Attributes, n1: QName, v1: String, n2: QName, v2: String, n3: QName, v3: String) =>
+ val attrsSafe = attrs - n1 - n2 - n3
+ val attrs2:Attributes = attrsSafe + (n1 -> v1, n2 -> v2, n3 -> v3)
+ val baseline = Map(attrs.toSeq:_*) + (n1 -> v1, n2 -> v2, n3 -> v3)
+ attrs2 must havePairs(baseline.toSeq: _*)
+ }
+
+ "support multiple addition of qname attrs" in check { (attrs: Attributes, x1: QName, v1: String, x2: QName, v2: String, x3: QName, v3: String) =>
+ val (n1,n2,n3) = (x1.name,x2.name,x2.name)
+ val attrsSafe = attrs - n1 - n2 - n3
+ val attrs2:Attributes = attrsSafe + (n1 -> v1, n2 -> v2, n3 -> v3)
+ val baseline = Map(attrs.toSeq:_*) + (QName(None,n1) -> v1, QName(None,n2) -> v2, QName(None,n3) -> v3)
+ attrs2 must havePairs(baseline.toSeq: _*)
+ }
+
+ "support update of qname attrs" in check { (attrs: Attributes, name: QName, value: String) =>
val attrsSafe = attrs - name
- val attrs2 = attrsSafe + (name -> value)
+ val attrs2:Attributes = attrsSafe.updated(name,value)
+ attrs2 must havePairs(attrsSafe.toSeq: _*)
+ attrs2 must havePair(name -> value)
+ }
+
+ "support update of string attrs" in check { (attrs: Attributes, x: QName, value: String) =>
+ val name = x.name
+ val attrsSafe = attrs - name
+ val attrs2:Attributes = attrsSafe.updated(name,value)
attrs2 must havePairs(attrsSafe.toSeq: _*)
attrs2 must havePair(QName(None, name) -> value)
}
+
+
"produce most specific Map with non-String value" in check { attrs: Attributes =>
val value = new AnyRef
val attrsSafe = attrs - "foo"
View
85 src/test/scala/com/codecommit/antixml/ZipperSpecs.scala
@@ -586,7 +586,90 @@ class ZipperSpecs extends SpecificationWithJUnit with ScalaCheck with XMLGenera
}
}
-
+
+ "withFilter" should {
+ val anyElem = Selector[Elem]( {case e: Elem => e} )
+
+ "support forEach" in {
+ val elems = (bookstore \\ anyElem).withFilter(e => e.name != "book")
+ var count = 0
+ elems.foreach { e =>
+ count = count + 1
+ e.name must beOneOf("title", "author")
+ }
+ count mustEqual 7
+ }
+
+ "support forEach with multiple filters" in {
+ val elems = (bookstore \\ anyElem).withFilter(e => e.name != "book").withFilter(e => e.name != "title")
+ var count = 0
+ elems.foreach { e =>
+ count = count + 1
+ e.name mustEqual("author")
+ }
+ count mustEqual 4
+ }
+
+ "support map" in {
+ val elems = (bookstore \\ anyElem).withFilter(e => e.name != "book").map({e => e.copy(name=e.name.toUpperCase)})
+ elems.foreach { e =>
+ e.name must beOneOf("TITLE", "AUTHOR")
+ }
+ elems.length mustEqual 7
+ }
+
+ "support map with multiple filters" in {
+ val elems = (bookstore \\ anyElem).withFilter(e => e.name != "book")
+ .withFilter(e => e.name != "title")
+ .map({e => e.copy(name=e.name.toUpperCase)})
+ elems.foreach { e =>
+ e.name mustEqual("AUTHOR")
+ }
+ elems.length mustEqual 4
+ }
+
+ "support flatMap" in {
+ val elems = (bookstore \\ anyElem)
+ .withFilter(e => e.name != "book")
+ .flatMap(e => e :: e :: Nil)
+ elems.foreach { e =>
+ e.name must beOneOf("title", "author")
+ }
+ elems.length mustEqual 14
+ }
+
+ "support flatMap with multiple filters" in {
+ val elems = (bookstore \\ anyElem)
+ .withFilter(e => e.name != "book")
+ .withFilter(e => e.name != "title")
+ .flatMap(e => e :: e :: Nil)
+ elems.foreach { e =>
+ e.name mustEqual("author")
+ }
+ elems.length mustEqual 8
+ }
+
+ "preserve zipper context after map" in {
+ val elems = (bookstore \\ anyElem).withFilter(e => e.name != "author").map({e => e.copy(name=e.name.toUpperCase)})
+ val result = elems.unselect
+ (result \ 'BOOK).length mustEqual 3
+ (result \ 'book).length mustEqual 0
+ (result \ 'BOOK \ 'TITLE).length mustEqual 3
+ (result \ 'BOOK \ 'title).length mustEqual 0
+ (result \ 'BOOK \ 'AUTHOR).length mustEqual 0
+ (result \ 'BOOK \ 'author).length mustEqual 0
+ }
+
+ "preserve zipper context after flatMap" in {
+ val elems = (bookstore \ anyElem \ anyElem).withFilter(e => e.name != "author").flatMap(e => e :: e :: Nil)
+ val result = elems.unselect.unselect
+ (result \ 'book).length mustEqual 3
+ (result \ 'book \ 'title).length mustEqual 6
+ (result \ 'book \ 'author).length mustEqual 0
+ }
+
+ }
+
def validate[Expected] = new {
def apply[A](a: A)(implicit evidence: A =:= Expected) = evidence must not beNull
View
2 src/test/scala/com/codecommit/antixml/util/OrderPreservingMapSpecs.scala
@@ -86,7 +86,7 @@ trait OrderPreservingMapSpecs extends Specification with ScalaCheck {
val (map, expect) = ((buildMap(e),e.entries) /: ops) { (itm, op) =>
val (m,e) = itm
op match {
- case Add(k,v) => (m + (k,v), updateEntry(e,k,v))
+ case Add(k,v) => (m + ((k,v)), updateEntry(e,k,v))
case Remove(k) => (m - k, removeEntry(e, k))
}
}

0 comments on commit 61ba7ac

Please sign in to comment.