Efficient (and slightly tricky) overload of ++
on parameters which are
specifically of type com.codecommit.antixml.Group[_]
.
Efficient (and slightly tricky) overload of ++
on parameters which are
specifically of type com.codecommit.antixml.Group[_]
.
Efficient (and slightly tricky) overload of +:
on parameters which are
specifically of type com.codecommit.antixml.Node.
Efficient (and slightly tricky) overload of +:
on parameters which are
specifically of type com.codecommit.antixml.Node.
Efficient (and slightly tricky) overload of :+
on parameters which are
specifically of type com.codecommit.antixml.Node.
Efficient (and slightly tricky) overload of :+
on parameters which are
specifically of type com.codecommit.antixml.Node.
[use case]
[use case]
[use case]
Merges adjacent com.codecommit.antixml.Text as well as adjacent
com.codecommit.antixml.CDATA nodes to produce a Group
which represents
an identical XML fragment but with a minimized structure.
Merges adjacent com.codecommit.antixml.Text as well as adjacent
com.codecommit.antixml.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 com.codecommit.antixml.Text and com.codecommit.antixml.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).
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.(Changed in version 2.9.0) The behavior of scanRight
has changed. The previous behavior can be reproduced with scanRight.reverse.
[use case]
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.
(Changed in version 2.9.0) transpose
throws an IllegalArgumentException
if collections are not uniformly sized.
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 com.codecommit.antixml.Node.
Efficient (and slightly tricky) overload of updated
on parameters which are
specifically of type com.codecommit.antixml.Node.
Provides an
unselect
operation which copies this Group's nodes back to the XML tree from which it was derived.See the Anti-XML Overview for a high-level description of this functionality.The
Zipper
trait augments a com.codecommit.antixml.Group with additional immutable state used to support theunselect
method. This state is known as the "zipper context" and is defined by:Group
, known as the parent of the Zipper.Loosely speaking, the
unselect
method produces an updated version of the parent by replacing its holes with the nodes from the Zipper, as determined by the replacement map. A formal definition ofunselect
can be found below.Certain "modify" operations on a
Zipper
will propagate the zipper context to the result. The new Zipper'sunselect
method can then be viewed as applying these modifications back to the parent tree. Currently, the following methods support this propagation of the zipper context:updated
,map
,flatMap
,filter
,collect
,slice
,drop
,take
,splitAt
, andunselect
(the latter viewed as a modification of the parentZipper
).These operations all provide a natural identification of indices in the new Zipper with the indices they were derived from in the original. This identification is used to lift the replacement map to the new Zipper. The parent and holes of the new Zipper are always the same as those of the original.
Of course, propagation is only possible if the result can legally be a
Zipper
. Replacing aNode
with aString
, for example, will result in an undecoratedIndexedSeq
because the result violates Zipper's type bounds.Node Multiplication and Elision
A Zipper's replacement map need neither be injective nor surjective. Injectivity can fail due to the action of
flatMap
, which replaces a node with a sequence of nodes, all of which are associated with the original node's hole. In such cases,unselect
will replace the hole with the entire sequence of nodes mapping to it. Surjectivity can fail due to any operation that "removes" items from theZipper
. If a hole is not associated with any Zipper nodes, thenunselect
will remove that position from the resulting tree.Conflicting Holes
For a given Zipper, a hole, H, is said to be conflicted if the Zipper contains another hole, Hc , contained in the subtree at H. In this case, the Zipper is said to be conflicted at H. A Zipper that does not contain conflicted holes is said to be conflict free. Conflicted holes arise when a selection operator yields both a node and one or more of its descendants. They are of concern because there is no canonical way to specify the behavior of
unselect
at a conflicted hole. Instead, a com.codecommit.antixml.ZipperMergeStrategy, implicitly provided tounselect
, is used to resolve the conflict.A default ZipperMergeStrategy has been provided that should suffice for the most common use cases involving conflicted holes. In particular, if modifications to a conflicted element are limited to its top-level properties (
name
,attributes
, etc.), then the default strategy will apply those changes while preserving any modifications made to those descendant nodes also present in the Zipper. However, if thechildren
property of a conflicted element is directly modified, then the default strategy's behavior is formally unspecified. Currently it uses a heuristic algorithm to resolve conflicts, but its details may change in a future release.Of the com.codecommit.antixml.Selectable operators, only
\\
is capable of producing conflicts. Theselect
,\
, and\\!
operators always produce conflict-free Zippers.Unselection Algorithm
Let G be a group, and Z be a zipper with G as its parent. For each location, L, in G (top-level or otherwise), we make the following definitions:
children
(L) is the sequence of locations that are immediately below L in G.children
(L).flatMap(pullback)
.pullback
(L) is the sequence of nodes given by the following recursive definition:pullback
(L) is the singleton sequence consisting of the indirect update for Lpullback
(L) is the direct updates for L.pullback
(L) is the result of merging its direct updates and its indirect update according to the com.codecommit.antixml.ZipperMergeStrategy provided tounselect
.Let T be the sequence of top-level locations in G. Then
Z.unselect
is defined asT.flatMap(pullback)
.