Context information corresponding to each node in the zipper.
Context information corresponding to each node in the zipper.
Returns the original group that was selected upon when the Zipper was created.
Returns the original group that was selected upon when the Zipper was created. A value of None indicates that
the Zipper has no parent and unselect cannot be called. This possibility is an unfortunate consequence of the
fact that some operations must return the static type of Zipper even though they yield no usable context.
In practice, this situation is usually caused by one of the following operations:
++, is used to "add" nodes to a zipper without replacing existing nodes.Information corresponding to each path in the zipper.
Information corresponding to each path in the zipper. The map's values consist of the indices of the corresponding nodes,
along with a master update time for the path. An empty sequence indicates the path has been elided and should be
removed upon unselect. In this case, the update time indicates the time of elision.
The map is sorted lexicographically by the path primarily to facilitate the isBeneathPath method.
A value that is greater than the update time of any node or path in the zipper.
A value that is greater than the update time of any node or path in the zipper. Subsequent updates must be tagged with a larger time.
Efficient (and slightly tricky) overload of ++ on parameters which are
specifically of type Group[_].
Efficient (and slightly tricky) overload of +: on parameters which are
specifically of type Node.
Efficient (and slightly tricky) overload of :+ on parameters which are
specifically of type Node.
[use case] Performs a shallow-select on the XML tree according to the specified selector function.
Performs a shallow-select on the XML tree according to the specified selector function. Shallow selection is defined according to the following expression:
Performs a shallow-select on the XML tree according to the specified selector function.
Performs a shallow-select on the XML tree according to the specified selector function. Shallow selection is defined according to the following expression:
nodes flatMap {
case Elem(_, _, children) => children collect selector
case _ => Group()
}
In English, this means that a shallow selection works by first selecting only the Elem(s) at the top level and then filtering their children according to the selector. The results of these filtrations are concatenated together, producing a single flattened result.
Very important: This is not the same as the XPath / operator!
(nor does it strive to be) XPath is inconsistent in its selection semantics,
varying them slightly depending on whether or not you are selecting from
the top level or in the middle of a compound expression. As a result, it
is categorically impossible to implement XPath in a combinatorial
fashion. Rather than give up on combinators, we chose to give up on XPath.
In practice, this "select child Elem(s), then filter their children" behavior
tends to be the most useful variant of the XPath selection. The "missed"
case here is applying a filter to the top-level set of nodes. This is
currently not handled by the API (perhaps in future). For now, if you need
this functionality, it's pretty easy to get it using the filter method.
The results of this function will be a collection of some variety, but the exact type is determined by the selector itself. For example:
val ns: Group[Node] = ... ns \ "name" ns \ * ns \ text
The three selection expressions here produce very different results. The
first will produce a collection of type Zipper[Elem],
the second will produce Zipper[Node],
while the third will produce scala.collection.Traversable[scala.String].
This reflects the fact that the selector produced (by implicit conversion)
from a String will only filter for nodes of type Elem.
However, the * selector will filter for all nodes (as the wildcard
symbol would suggest) and thus it must return a collection containing the
fully-generic Node. Finally, the text selector
specifically pulls out the textual contents of nodes, and thus its results
will not be nodes at all, but raw String(s).
Whenever supported by the resulting collection type, the selection operation will preserve a "backtrace", or an "undo log" of sorts. This backtrace is known as a zipper context. This context makes it possible to operate on the collection resulting from performing a selection, then unselect to rebuild the original collection around it (modulo the changes made). This provides a very clean and powerful way of drilling down into an XML tree, making some changes and then realizing those changes within the context of the full tree. In a sense, it solves the major inconvenience associated with immutable tree structures: the need to manually rebuild the entire ancestry of the tree after making a change somewhere within.
[use case] Performs a deep-select on the XML tree according to the specified selector function.
Performs a deep-select on the XML tree according to the specified selector function. Deep selection is defined according to the following recursion:
Performs a deep-select on the XML tree according to the specified selector function.
Performs a deep-select on the XML tree according to the specified selector function. Deep selection is defined according to the following recursion:
def deep(g: Group[Node]):That =
g flatMap {n => Group(n).collect(selector) ++ deep(n.children)}
nodes flatMap {n => deep(n.children)}
In English, this means that deep selection is defined simply as a depth-first search through the tree, all the way from the root down to the leaves. Note that the recursion does not short circuit when a result is found. Thus, if a parent node matches the selector as well as one of its children, then both the parent and the child will be returned, with the parent preceeding the child in the results.
Just as with shallow selection, the very outermost level of the group is not
considered in the selection. Thus, deep selection is not exactly the
same as the XPath // operator, since // will consider the outermost level,
while Anti-XML's deep selection \\ will not.
[use case] Performs a short-circuiting deep-select on the XML tree according to the specified selector.
Performs a short-circuiting deep-select on the XML tree according to the specified selector. Short-circuit deep selection is defined according to the following recursion:
Performs a short-circuiting deep-select on the XML tree according to the specified selector.
Performs a short-circuiting deep-select on the XML tree according to the specified selector. Short-circuit deep selection is defined according to the following recursion:
def deep(g: Group[Node]):That =
g flatMap {n =>
if (selector.isDefinedAt(n)) Group(n).collect(selector)
else deep(n.children)
}
nodes flatMap {n => deep(n.children)}
Like \\, this performs a depth-first search through the tree. However, any time
the selector matches a node, it's children are skipped over rather than being searched.
Thus, the result is guaranteed to never contain both a node and one of its descendants.
Just as with shallow selection, the very outermost level of the group is not considered in the selection.
Merges adjacent Text as well as adjacent
CDATA nodes to produce a Group which represents
an identical XML fragment but with a minimized structure.
Merges adjacent Text as well as adjacent
CDATA nodes to produce a Group which represents
an identical XML fragment but with a minimized structure. Slightly more
formally, for any XML fragment with n characters of textual data, there
are 2n possible ways of representing that fragment as a Group. All
of these representations are semantically distinct (i.e. structurally different
in memory) but logically equivalent in that they will all generate the same
XML fragment if serialized. Of these 2n distinct representations,
there will be exactly one representation which is minimal, in that the
smallest possible number
of Text and CDATA nodes
are used to represent the textual data. This form may be considered
"canonical". This method converts an arbitrary Group into its canonical
form, a logically equivalent Group which represents the same XML fragment
in its structurally minimized form.
This method is perhaps best explained by an example:
val xml = Group(Text("Daniel "), Text("Spiewak"))
xml.canonicalize // => Group(Text("Daniel Spiewak"))
The Group resulting from the canonicalize invocation will produce exactly
the same result as would xml were we to invoke the toString method on
each of them. However, the canonicalized result has only one text node for
the entire character block, while xml (the original Group) has two.
This is actually a very common gotcha in scala.xml. The issue comes up
most frequently in the area of equality. As you can see in the example above,
xml clearly will not be equivalent (according to the equals method)
to xml.canonicalize. However, it is very natural to assume that these
two structures are in fact equal due to their logical equivalence in
that they represent the same textual XML fragment. Oftentimes, people will
get around this issue in scala.xml by converting all NodeSeq(s) into
strings prior to comparison. In Anti-XML, all that is necessary to handle
potential semantic divergence in cases of logical equality is to simply
invoke the canonicalize method on each of the two equality operands.
If true this group may contain an element with the given name as one of its children (recursively).
If true this group may contain an element with the given name as one of its children (recursively).
[use case] Performs a selection on the top-level nodes of this group.
Performs a selection on the top-level nodes of this group. The nodes returned by this method are exactly the same as the nodes returned by:
Performs a selection on the top-level nodes of this group.
Performs a selection on the top-level nodes of this group. The nodes returned by this method are exactly the same as the nodes returned by:
nodes.collect(selector)
However, this method differs from collect in that it can return a Zipper
with full unselect functionality. Thus, it is possible to select a subset of a group,
operate on that subset, and then call unselect to pull those operations back to the original group.
Returns a Group containing the same nodes as this Zipper, but without any Zipper context, and in particular,
without any implict references to the zipper's parent.
Serializes the nodes in this Group into their most compact XML representation
and concatenates the result.
Serializes the nodes in this Group into their most compact XML representation
and concatenates the result. Note that the result of this method may not
be well-formed XML, since well-formed XML is required to have only a single
parent element (whereas a Group may contain multiple nodes at its top level).
This is not a pretty-print. The resulting XML will not be formatted in any
way. It will be precisely the XML fragment represented by this Group,
no more and no less.
The XML fragment represented by this Group in String form
Produces a scala.collection.immutable.Vector which contains all of the
nodes in this Group.
Produces a scala.collection.immutable.Vector which contains all of the
nodes in this Group. This function is guaranteed to run in constant time.
Applies the node updates to the parent and returns the result.
Efficient (and slightly tricky) overload of updated on parameters which are
specifically of type Node.
use iterator' instead
use corresponds instead
Use indexWhere(p) instead.
use lastIndexWhere instead
use head' instead
use headOption' instead
use view' instead
use reverseIterator' instead
A zipper which allows deep selection.
Unselection Algorithm
Let G be a group, and Z be a zipper with G as its parent. For each node, N, in G (top-level or otherwise), we make the following definitions:
unselect.(Strictly speaking, these definitions should be made on the location of N in G rather than on N itself, but we abuse the terminology for the sake of brevity)
With the above definitions, the result of
unselectcan be defined simply as G flatMapped with the pullBack function.The last line in the definition of "pullback" deserves special mention because it defines the only condition under which the ZipperMergeStrategy is invoked. Given zipper Z and parent G, if there exists an N in G such that N lies both in and above Z, then Z is said to be conflicted at N. A zipper without such an N then Z is said to be conflict free and will never invoke a ZipperMergeStrategy during unselection.