Permalink
Browse files

Pulled Zipper unselection support into a separate trait.

  • Loading branch information...
ncreep committed Dec 13, 2011
1 parent d3773a5 commit bddf6e91cb087d794994d1b391ba5fd8b4e8cad5
@@ -0,0 +1,81 @@
+package com.codecommit.antixml
+
+import Zipper._
+import util.VectorCase
+import collection.immutable.IndexedSeq
+
+/** Provides unselection support for zipper. */
+private[antixml] trait ZipperUnselection { self: Zipper[Node] =>
+
+ /** Applies the node updates to the context parent and returns the result. */
+ private def unselect(context: Context, mergeStrategy: ZipperMergeStrategy) = {
+ val topLevelHoleInfo = toHoleInfo(context)
+ pullBackGroup(context.parent, topLevelHoleInfo, mergeStrategy)._1.asInstanceOf[Zipper[Node]]
+ }
+
+ /** Returns the pullback of the nodes in the specified group.
+ * @param nodes the group containing the nodes to pull back.
+ * @param holeInfo the HoleInfo corresponding to the group.
+ * @param zms The strategy to be used for conflict resolution.
+ * @return the pullBacks of the groups children, concatenated together, along with the latest update
+ * time.
+ */
+ private def pullBackGroup(nodes: Group[Node], holeInfo: HoleInfo, zms: ZipperMergeStrategy): (Group[Node], Time) = {
+ var maxTime: Int = 0 //mutable for performance and to avoid further complicating`conditionalFlatMapWithIndex`.
+ val updatedGroup = nodes.conditionalFlatMapWithIndex[Node] { (node, index) =>
+ node match {
+ case elem: Elem if (holeInfo.hasChildrenAt(index)) => {
+ val (newNodes, time) = pullUp(elem, index, holeInfo, zms)
+ maxTime = math.max(maxTime, time)
+ Some(newNodes)
+ }
+ case _ if holeInfo.contains(index) => {
+ val (newNodes, time) = holeInfo(index)
+ maxTime = math.max(maxTime, time)
+ Some(newNodes.map { _._1 })
+ }
+ case _ => None
+ }
+ }
+ (updatedGroup, maxTime)
+ }
+
+ /** Returns the pullback of an element that is known to be above a hole (and thus has
+ * child updates that need to be pulled up).
+ *
+ * @param elem the element
+ * @param indexInParent the index of the element in its parent
+ * @param holeInfo the HoleInfo corresponding to the parent group
+ * @param zms The strategy to be used for conflict resolution.
+ * @return the pulled back nodes and their combined update time
+ *
+ * @note assumes `holeInfo.hasChildrenAt(indexInParent) == true`
+ */
+ private def pullUp(elem: Elem, indexInParent: Int, holeInfo: HoleInfo, zms: ZipperMergeStrategy): (VectorCase[Node], Time) = {
+ //Recursively pull back children
+ val (childGrp, childTime) = pullBackGroup(elem.children, holeInfo.children(indexInParent), zms)
+ val indirectUpdate = elem.copy(children = childGrp)
+ if (holeInfo.contains(indexInParent)) {
+ //This is a conflicted hole, so merge.
+ mergeConflicts(elem, holeInfo(indexInParent), (indirectUpdate, childTime), zms)
+ } else {
+ //No conflicts, just let the child updates bubble up
+ (VectorCase(indirectUpdate), childTime)
+ }
+ }
+
+ /** Merges updates at a conflicted node in the tree. See the unselection algorithm, above, for more information.
+ * @param node the conflicted node
+ * @param directUpdates the direct updates to `node`.
+ * @param indirectUpdate the indirectUpdate to `node`.
+ * @param mergeStrategy The strategy to be used for conflict resolution.
+ * @return the sequence of nodes to replace `node`, along with an overall update time for `node`.
+ */
+ private def mergeConflicts(node: Elem, directUpdates: (IndexedSeq[(Node, Time)], Time), indirectUpdate: (Node, Time), mergeStrategy: ZipperMergeStrategy): (VectorCase[Node], Time) = {
+ val mergeContext = ZipperMergeContext(original = node, lastDirectUpdate = directUpdates._2, directUpdate = directUpdates._1,
+ indirectUpdate = indirectUpdate)
+
+ val result = mergeStrategy(mergeContext)
+ (VectorCase.fromSeq(result), math.max(directUpdates._2, indirectUpdate._2))
+ }
+}
@@ -124,7 +124,8 @@ trait Zipper[+A <: Node] extends Group[A]
with IndexedSeqLike[A, Zipper[A]]
with ZipperGroupOverrides[A]
with ZipperHoleShifting
- with ZipperAxes { self =>
+ with ZipperAxes
+ with ZipperUnselection { self =>
/**
* Returns the original group that was selected upon when the Zipper was created. A value of `None` indicates that
@@ -150,7 +151,7 @@ trait Zipper[+A <: Node] extends Group[A]
/** Applies the node updates to the parent and returns the result. */
def unselect(implicit zms: ZipperMergeStrategy): Zipper[Node] = {
val ctx = context.getOrElse(sys.error("Zipper does not have a valid context"))
- new Unselector(ctx, zms).unselect
+ unselect(ctx, zms)
}
/** Each hole is associated with a list of node/time pairs as well as a master update time */
@@ -207,80 +208,6 @@ trait Zipper[+A <: Node] extends Group[A]
}
}
}
-
- /** Utility class to perform unselect. */
- private[this] class Unselector(context: Context, mergeStrategy: ZipperMergeStrategy) {
-
- private val topLevelHoleInfo = toHoleInfo(context)
-
- /** Applies the node updates to the parent and returns the result. */
- def unselect: Zipper[Node] =
- pullBackGroup(context.parent, topLevelHoleInfo)._1.asInstanceOf[Zipper[Node]]
-
- /**
- * Returns the pullback of the nodes in the specified group.
- * @param nodes the group containing the nodes to pull back.
- * @param holeInfo the HoleInfo corresponding to the group.
- * @return the pullBacks of the groups children, concatenated together, along with the latest update
- * time.
- */
- private[this] def pullBackGroup(nodes: Group[Node], holeInfo: HoleInfo): (Group[Node], Time) = {
- var maxTime: Int = 0 //mutable for performance and to avoid further complicating`conditionalFlatMapWithIndex`.
- val updatedGroup = nodes.conditionalFlatMapWithIndex[Node] { (node,index) => node match {
- case elem:Elem if (holeInfo.hasChildrenAt(index)) => {
- val (newNodes, time) = pullUp(elem, index, holeInfo)
- maxTime = math.max(maxTime,time)
- Some(newNodes)
- }
- case _ if holeInfo.contains(index) => {
- val (newNodes, time) = holeInfo(index)
- maxTime = math.max(maxTime, time)
- Some(newNodes.map {_._1})
- }
- case _ => None
- }}
- (updatedGroup,maxTime)
- }
-
- /**
- * Returns the pullback of an element that is known to be above a hole (and thus has
- * child updates that need to be pulled up).
- *
- * @param elem the element
- * @param indexInParent the index of the element in its parent
- * @param holeInfo the HoleInfo corresponding to the parent group
- * @return the pulled back nodes and their combined update time
- *
- * @note assumes `holeInfo.hasChildrenAt(indexInParent) == true`
- */
- private[this] def pullUp(elem: Elem, indexInParent: Int, holeInfo: HoleInfo): (VectorCase[Node], Time) = {
- //Recursively pull back children
- val (childGrp, childTime) = pullBackGroup(elem.children, holeInfo.children(indexInParent))
- val indirectUpdate = elem.copy(children = childGrp)
- if (holeInfo.contains(indexInParent)) {
- //This is a conflicted hole, so merge.
- mergeConflicts(elem, holeInfo(indexInParent), (indirectUpdate, childTime))
- } else {
- //No conflicts, just let the child updates bubble up
- (VectorCase(indirectUpdate), childTime)
- }
- }
-
- /**
- * Merges updates at a conflicted node in the tree. See the unselection algorithm, above, for more information.
- * @param node the conflicted node
- * @param directUpdates the direct updates to `node`.
- * @param indirectUpdate the indirectUpdate to `node`.
- * @return the sequence of nodes to replace `node`, along with an overall update time for `node`.
- */
- private def mergeConflicts(node: Elem, directUpdates: (IndexedSeq[(Node,Time)], Time) , indirectUpdate: (Node, Time)): (VectorCase[Node], Time) = {
- val mergeContext = ZipperMergeContext(original=node, lastDirectUpdate = directUpdates._2, directUpdate = directUpdates._1,
- indirectUpdate = indirectUpdate)
-
- val result = mergeStrategy(mergeContext)
- (VectorCase.fromSeq(result), math.max(directUpdates._2, indirectUpdate._2))
- }
- }
}
object Zipper {
@@ -307,7 +234,7 @@ object Zipper {
hiddenNodes: immutable.Seq[ElemsWithContextHidden])
/** The units in which time is measured in the zipper. Assumed non negative. */
- private type Time = Int
+ private[antixml] type Time = Int
implicit def canBuildFromWithZipper[A <: Node] = {
new CanBuildFromWithZipper[Traversable[_], A, Zipper[A]] {

0 comments on commit bddf6e9

Please sign in to comment.