Skip to content

Commit

Permalink
Fix for Issue djspiewak#36
Browse files Browse the repository at this point in the history
  • Loading branch information
josharnold52 committed Jul 27, 2011
1 parent be63c9b commit ee7d813
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 46 deletions.
21 changes: 11 additions & 10 deletions src/main/scala/com/codecommit/antixml/Selectable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,20 @@ trait Selectable[+A <: Node] {

val catBuilder = new VectorBuilder[B]
val chunkBuilder = new VectorBuilder[Int]
val rebuildBuilder = new VectorBuilder[(Group[Node], Map[Int, Set[Int]]) => Node]
val childMapBuilder = new VectorBuilder[Map[Int, Set[Int]]]
val rebuildBuilder = new VectorBuilder[(Group[Node], Map[Int, Int]) => Node]
val childMapBuilder = new VectorBuilder[Map[Int, Int]]

for (node <- toGroup) {
node match {
case e @ Elem(_, _, _, _, children) if children.matches(selector) => {
var childMap = Map[Int, Set[Int]]()
var childMap = Map[Int, Int]()
var currentChunk = 0

var i = 0
for (child <- children) {
if (selector isDefinedAt child) {
catBuilder += selector(child)
childMap += (i -> Set(currentChunk))
childMap += (i -> 1)
currentChunk += 1
}
i += 1
Expand All @@ -128,12 +128,13 @@ trait Selectable[+A <: Node] {
chunkBuilder += currentChunk
childMapBuilder += childMap

def rebuild(children2: Group[Node], indexes: Map[Int, Set[Int]]) = {
val revisedChildren = children.zipWithIndex.foldLeft(Group[Node]()) {
case (acc, (_, i)) if indexes contains i =>
indexes(i).foldLeft(acc) { _ :+ children2(_) }

case (acc, (e, _)) => acc :+ e
def rebuild(children2: Group[Node], indexToSize: Map[Int, Int]) = {
val (revisedChildren,offset) = children.zipWithIndex.foldLeft((Group[Node](),0)) {
case ((acc,offset), (_, i)) if indexToSize contains i => {
val chIndices = Range(offset,offset+indexToSize(i))
(chIndices.foldLeft(acc) { _ :+ children2(_) } , chIndices.end)
}
case ((acc,offset), (e, _)) => (acc :+ e, offset)
}

e.copy(children=revisedChildren)
Expand Down
52 changes: 18 additions & 34 deletions src/main/scala/com/codecommit/antixml/Zipper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ trait Zipper[+A <: Node] extends Group[A] with IndexedSeqLike[A, Zipper[A]] with

val nodes2 = (map zip parent.toVectorCase).foldLeft(VectorCase[Node]()) {
case (acc, (Some((from, to, rebuild, childMap)), _: Elem)) if from == to =>
acc :+ rebuild(Group(), childMap mapValues Function.const(Set[Int]()))
acc :+ rebuild(Group(), childMap mapValues Function.const(0))

case (acc, (Some((from, to, rebuild, childMap)), _: Elem)) =>
acc :+ rebuild(superSlice(from, to), childMap)
Expand Down Expand Up @@ -101,51 +101,35 @@ trait Zipper[+A <: Node] extends Group[A] with IndexedSeqLike[A, Zipper[A]] with
val result = toVectorCase.toVector map f

val intermedMap = map map {
case Some((from, to, rebuild, childMap)) => {
// get the mapping from *our* indexes to source indexes
val inverseMap = {
val maps = for ((source, targets) <- childMap)
yield (Map[Int, Int]() /: targets) { (acc, t) => acc + (t -> source) }

(Map[Int, Int]() /: maps) { _ ++ _ }
}

val (_, aggregate, childMap2) = result.slice(from, to).zipWithIndex.foldLeft((0, Vector[B](), Map[Int, Set[Int]]())) {
case ((start, acc, childMap2), (chunk, i)) => {
val size = chunk.size
val source = inverseMap(i)

val contrib = Set(start until (start + size): _*)
val set2 = childMap2.getOrElse(source, contrib) ++ contrib

(start + size, acc ++ chunk, childMap2.updated(source, set2))
case Some((from, to, rebuild, childMap)) => {
val childMapSorted = (childMap.toSeq) sortWith { _._1 < _._1 } //TODO - Maybe just make childMap a SortedMap
val localResults = result.slice(from,to)


val (_, chunk, childMap2) = ((0, Vector[B](), Map[Int,Int]()) /: childMapSorted) {
case ((offset, acc, childMap2),(srcIndex, 0)) =>
(offset, acc, childMap2 + (srcIndex -> 0))

case ((offset, acc, childMap2),(srcIndex,destCount)) => {
val items = localResults.slice(offset, offset+destCount).flatMap {x => x}
(offset + destCount, acc ++ items, childMap2 + (srcIndex -> items.size))
}
}

val elided = if (childMap.size == childMap2.size)
Set()
else
childMap filterKeys { !childMap2.isDefinedAt(_) } mapValues { _ => Set.empty[Int] }

val length = aggregate.length
val delta = length - (to - from)
Some((from, to + delta, rebuild, childMap2 ++ elided, aggregate, delta))
Some(chunk, rebuild, childMap2)
}

case None => None
}

val (_, map2, chunks) = intermedMap.foldLeft((0, Vector[Option[ZContext]](), Vector[Vector[B]]())) {
case ((offset, map2, acc), Some((from, to, rebuild, childMap, aggregate, delta))) => {
val from2 = from + offset
val to2 = to + offset
val offset2 = offset + delta
(offset2, map2 :+ Some((from2, to2, rebuild, childMap)), acc :+ aggregate)
val (_, map2, chunks) = ((0, Vector[Option[ZContext]](), Vector[Vector[B]]()) /: intermedMap) {
case ((offset, map2, acc), Some((chunk,rebuild,childMap))) => {
(offset + chunk.size, map2 :+ Some((offset, offset+chunk.size, rebuild, childMap)), acc :+ chunk)
}

case ((offset, map2, acc), None) => (offset, map2 :+ None, acc)
}

val builder = cbfwz(parent.asInstanceOf[Zipper[A]], map2)
chunks foreach (builder ++=)
builder.result
Expand Down
19 changes: 17 additions & 2 deletions src/main/scala/com/codecommit/antixml/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,23 @@ package com.codecommit
* </ul>
*/
package object antixml {
// (from, to, rebuild, internal map)
private[antixml] type ZContext = (Int, Int, (Group[Node], Map[Int, Set[Int]]) => Node, Map[Int, Set[Int]])
/**
* `(from, to, rebuild, internal map)`.
*
* Represents the mapping from an element's child group to the corresponding Nodes in a Zipper.
*
* `from` and `to` denote the slice of the target Zipper corresponding to the child group.
*
* `internal map` yields the number of target Nodes for a given child node's index.
*
* We require the correspondence to be order preserving and to cover the target slice. Given this fact,
* the above items completely determine the correspondence.
*
* `rebuild` is called to reconstruct the element for the `unzip` operation. It is passed the slice
* of the target group along with the `internal map`.
*
*/
private[antixml] type ZContext = (Int, Int, (Group[Node], Map[Int, Int]) => Node, Map[Int, Int])

/**
* Pimps the `anti` method onto any object for which there exists a conversion
Expand Down
11 changes: 11 additions & 0 deletions src/test/scala/com/codecommit/antixml/ZipperSpecs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,18 @@ class ZipperSpecs extends Specification with ScalaCheck with XMLGenerators {
children must haveSize(1)
children \ 'title \ text mustEqual Vector("Programming Scala")
}
}
}

"preserve flatMap order" in {
val original = <top><a /></top>.convert
val expanded = original \ 'a flatMap {
case e: Elem => for(i <- 0 until 10) yield e.copy(name=e.name+i)
}
val modified = expanded.unselect

modified must haveSize(1)
modified(0) mustEqual <top><a0 /><a1 /><a2 /><a3 /><a4 /><a5 /><a6 /><a7 /><a8 /><a9 /></top>.convert
}
}

Expand Down

0 comments on commit ee7d813

Please sign in to comment.