Documentation
¶
Overview ¶
Package xdmbuild constructs XDM sequences and trees from the results of a sequence constructor.
XSLT and XQuery build result trees the same way. The rules in XSLT 3.0 §5.7.1 and XQuery 3.1 §3.9.1.3 are, for the part that matters here, the same text: arrays are flattened, a run of adjacent atomic values becomes one text node with a single space between each, adjacent text nodes are merged with no separator, zero-length text nodes are dropped, a document node is replaced by its children, and an attribute or namespace node may not follow a node that is neither.
What differs between the two languages is small and enumerable: five error codes, one genuine difference in behaviour, and the namespace and type policies a copy is made under. Those arrive through a Policy rather than being written into the builder, so this package names neither language.
Index ¶
- func DeepCopy(n *xdm.Node) *xdm.Node
- func Rebase(n *xdm.Node, parentBase string)
- func RebaseDetached(n *xdm.Node, instrBase string)
- func ResolveAgainst(base, ref string) string
- type Builder
- func (b *Builder) AddAttribute(name xdm.QName, value string) error
- func (b *Builder) AddAttributeTyped(name xdm.QName, value string, typeAnnotation string) error
- func (b *Builder) AddNamespace(prefix, uri string) error
- func (b *Builder) AppendNode(n *xdm.Node)
- func (b *Builder) AppendOpaque(it xdm.Item) error
- func (b *Builder) AppendText(s string)
- func (b *Builder) AppendValue(a *xdm.Atomic)
- func (b *Builder) EndAtomicRun()
- func (b *Builder) Items() xdm.Sequence
- func (b *Builder) NoteDeclared(prefix, uri string)
- func (b *Builder) Open() *xdm.Node
- func (b *Builder) Sequence() xdm.Sequence
- func (b *Builder) SetItemSeparator(sep *string)
- func (b *Builder) StartElement(name xdm.QName) *Builder
- func (b *Builder) ToDocument() (*xdm.Node, error)
- func (b *Builder) ToTree() *xdm.Node
- type Fault
- type Policy
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DeepCopy ¶
DeepCopy clones a subtree, detached from its original parent.
The type annotation travels with the copy. validation="preserve" is defined as keeping the types the source carried, and dropping them here left a preserved copy untyped, so "$v instance of element(e, xs:anyURI)" answered false for a node that had just been copied from a validated document. Stripping is done by the validation spec, which is the thing that knows whether the instruction asked for it.
func Rebase ¶
Rebase recomputes the base URIs of a subtree that has just been re-parented.
A copied element keeps whatever xml:base attribute it carried, and XSLT 2.0 section 11.9 makes the base URI of the copy a function of that attribute and of the *new* parent, not of the document it came from. So a source element written xml:base="/xml/" under a document based at http://a.example/ becomes based at http://b.example/xml/ once copied under a parent based at http://b.example/main/ — carrying the resolved http://a.example/xml/ across unchanged is the one answer that is wrong in every case.
An element with no xml:base of its own simply inherits, which is the same rule with an empty reference.
func RebaseDetached ¶
RebaseDetached recomputes the base URI of a copy that has no parent.
Section 11.9: "the base URI of a node is copied, except in the case of an element node having an xml:base attribute, in which case the base URI of the new node is taken as the value of the xml:base attribute, resolved if it is relative against the base URI of the xsl:copy/xsl:copy-of instruction". So an element without its own xml:base keeps the source's base URI unchanged — which is why this cannot just call Rebase, whose empty-reference case makes the node inherit from its new parent.
func ResolveAgainst ¶
ResolveAgainst resolves a possibly-relative reference against a base URI, returning the reference unchanged when the base is unusable.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder accumulates the result of a sequence constructor.
XSLT output is a sequence of nodes and atomic values, not a string: an attribute added after an element has children is an error, adjacent text must be merged, and the result may be a temporary tree that later instructions navigate. Building a string directly would make all of that impossible.
func New ¶
New returns a builder that reports faults through p.
p must not be nil: every fault the builder detects is named by the caller's language, and there is no default to fall back on.
func (*Builder) AddAttribute ¶
AddAttribute attaches an attribute to the element under construction.
func (*Builder) AddAttributeTyped ¶
AddAttributeTyped adds an attribute carrying a type annotation.
The annotation has to travel with the attribute rather than be applied to a throwaway node: xsl:attribute assesses the value it is about to write, and a pattern such as schema-attribute(A) matches only a node that was actually validated against the declaration, so an attribute that lost its annotation on the way into the element could never match however it was named.
func (*Builder) AddNamespace ¶
AddNamespace attaches a namespace node to the element under construction.
It is shared by xsl:namespace and by xsl:copy-of of a namespace node: both add a binding to the element being built, and a namespace node appended as if it were a child would silently vanish from the result.
func (*Builder) AppendNode ¶
AppendNode adds a node to the current output position.
A node that already belongs to a tree is copied first. AppendChild rewrites the node's Parent and tree pointers and Finalize renumbers its document order, so adopting a source node in place *mutates the source document* — evaluating an unused variable containing xsl:sequence was enough to reorder the input, and two goroutines transforming a shared parsed tree raced on it. xsl:copy-of already copied; xsl:sequence and xsl:perform-sort did not, and the guard belongs here where every caller is covered.
func (*Builder) AppendOpaque ¶
AppendOpaque adds an item that is neither a node nor an atomic value — a map, an array, or a function item.
Such an item is a legal member of a sequence but has no representation inside element content, so it is accepted at the top level and refused under an open element. Both languages refuse it; they differ only in the code, which is why the fault is reported rather than named here.
func (*Builder) AppendText ¶
AppendText adds text, merging with a preceding text node so that the XDM invariant of no adjacent text nodes holds in constructed trees too.
func (*Builder) AppendValue ¶
AppendValue adds an atomic value to the output sequence.
func (*Builder) EndAtomicRun ¶
func (b *Builder) EndAtomicRun()
EndAtomicRun declares that the values appended so far form a complete run, so that the next atomic value starts a new one and takes no separator.
Both languages separate a run of adjacent atomic values with single spaces, and both scope the run to one sequence — XSLT §5.7.1 to the sequence an instruction returns, XQuery §3.9.1.3 to the value of one enclosed expression. The builder cannot see either boundary, because a caller appends a sequence item by item and nothing in the calls says where one ended and the next began.
XSLT does not call this: it appends the whole of an instruction's sequence before anything else can intervene, so its runs already end where they should. XQuery needs it because "<e>{1}{2}</e>" is two enclosed expressions, each a one-item sequence, and the two items must abut rather than be separated.
func (*Builder) Items ¶
Items returns the accumulated items without finishing the builder.
Sequence is the ordinary way to read the result. This exists for the section 8.4.4 algorithm, which has to look at what a constructor produced so far in order to decide whether xsl:on-empty fires, and then carry on appending to the same builder.
func (*Builder) NoteDeclared ¶
NoteDeclared records a namespace node the element was constructed with, so that a later xsl:namespace binding the same prefix elsewhere is seen as the XTDE0430 conflict it is.
The distinction is which namespaces reach "the result sequence". A prefix carried only by the element's *name* is not a namespace node the sequence constructor produced, and section 11.7 resolves a clash with it by renaming — namespace-alias-1903 writes xsl:element name="ns:e" with xmlns:ns on the instruction, which is not copied to the result, and requires the rename. A literal result element is the other case: 11.7 copies its namespace nodes into the result, so two conflicting bindings for one prefix really are two namespace nodes with the same name and different values. namespace-2618 is the spec's own "Conflicting Namespace Prefixes" example and expects the error.
func (*Builder) Open ¶
Open returns the element currently being built, or nil at the top level.
A caller needs it for the work that happens after the content is in place but before the element is finished: namespace fixup, schema validation, and stamping a base URI all act on the element itself rather than on what the builder appended to it. It is the live node, not a copy — mutating it is the point.
func (*Builder) SetItemSeparator ¶
SetItemSeparator records the item-separator that applies to the tree this builder produces. A nil argument leaves the default 5.7.1 rules in force.
func (*Builder) StartElement ¶
StartElement opens a new element, returning a builder scoped to it.
func (*Builder) ToDocument ¶
ToTree wraps the accumulated items in a document node, which is what a variable with content produces. ToDocument is ToTree with the check XTDE0420 requires.
"It is a non-recoverable dynamic error if the result sequence used to construct the content of a document node contains a namespace node or attribute node." A document node has no attributes and carries no namespace declarations of its own, so such an item has nowhere to go: appending it silently discarded it, and the stylesheet saw a document that was missing what it had just built.
It is separate from ToTree because not every temporary builder becomes a document. A sequence constructor producing a bare attribute is legitimate — xsl:function as="attribute()" is written that way — and only wrapping the result in a document node makes it wrong.
type Fault ¶
type Fault int
A Fault is a structural fault detected while building content.
The builder reports what went wrong and leaves naming it to the caller, because the two languages that use this give the same fault different codes — and, for FaultDuplicateAttribute, different behaviour. Reporting the condition rather than the code is what lets one builder serve both.
const ( // FaultAttrAfterChild is an attribute or namespace node added to an // element that already has a child which is neither. // // XSLT: XTDE0410. XQuery: XQTY0024. FaultAttrAfterChild Fault = iota // FaultAttrOnDocument is an attribute or namespace node appearing as the // content of a document node. // // XSLT: XTDE0420. XQuery: XPTY0004. FaultAttrOnDocument // FaultConflictingPrefix is one prefix bound to two different URIs on the // same element by the sequence being constructed. // // XSLT: XTDE0430. XQuery: XQDY0102. FaultConflictingPrefix // FaultDefaultNSOnNoNS is a default namespace declared on an element whose // own name is in no namespace. // // XSLT: XTDE0440. XQuery: XQDY0102. FaultDefaultNSOnNoNS // FaultFunctionItem is a function item, map or array appearing where the // content model admits only nodes and atomic values. // // XSLT: XTDE0450. XQuery: XQTY0105. In a serialization context both // languages raise SENR0001 instead, which is why this is a fault rather // than a fixed code. FaultFunctionItem // FaultDuplicateAttribute is two attributes with the same name on one // element. // // This is the one fault where the languages disagree about more than the // code. XSLT 3.0 §5.7.1 discards the earlier attribute silently — "if an // attribute A in the sequence has the same name as another attribute B // that appears later in the sequence, then attribute A is discarded". // XQuery 3.1 §3.9.1.3 raises XQDY0025. // // A Policy selects XSLT's behaviour by returning nil for this fault: the // builder then replaces the earlier attribute and carries on. Returning an // error selects XQuery's. FaultDuplicateAttribute )
type Policy ¶
type Policy interface {
// Err names a structural fault, or returns nil to accept it.
//
// Only FaultDuplicateAttribute is meaningfully acceptable: returning nil
// there selects XSLT's rule that a later attribute replaces an earlier one
// of the same name. Returning nil for any other fault lets the builder
// carry on with content the data model does not admit, so a Policy should
// name them all.
//
// detail describes the particular node or prefix at fault and is meant to
// be quoted in the message.
Err(f Fault, detail string) error
// InheritNamespaces reports whether the namespaces in scope on a
// constructed element are copied to elements copied beneath it.
//
// XSLT: xsl:element/@inherit-namespaces and xsl:copy/@inherit-namespaces.
// XQuery: the inherit / no-inherit half of copy-namespaces.
InheritNamespaces() bool
// PreserveNamespaces reports whether a copied element keeps every
// namespace that was in scope on the original, or only those used in the
// names of the element and its attributes.
//
// XSLT has no way to ask for anything but the first, so an XSLT policy
// returns true. XQuery: the preserve / no-preserve half of
// copy-namespaces.
PreserveNamespaces() bool
// PreserveTypes reports whether a copied node keeps its type annotation
// and its is-id, is-idrefs and nilled properties.
//
// XSLT: validation="preserve" against any other validation mode.
// XQuery: construction mode preserve against strip.
PreserveTypes() bool
}
A Policy supplies what the builder cannot know by itself: how to name a structural fault, and the namespace and type rules a copy is made under.
The namespace questions are asked as two independent booleans because the specifications ask them that way. XQuery's copy-namespaces has two axes, preserve/no-preserve and inherit/no-inherit, which vary independently; XSLT's xsl:element and xsl:copy carry inherit-namespaces and always preserve.