Dmn.XML.Element (dmn v0.1.0)

Copy Markdown View Source

An element of an XML document, as Dmn.XML reads it.

Names are strings and nothing is an atom: the document is not trusted, and an atom is never freed. The namespace of the element and of its attributes is resolved, so the reader of the model asks for {namespace, "decision"} and not for a prefix.

FieldIs
namespacethe URI of the namespace of the element, nil for none
namethe local name
attributesthe attributes that have no namespace, by name
qualifiedthe attributes that have one, by {namespace, name} (xsi:type, and the attributes of other vocabularies)
namespacesthe prefixes that the element declares itself, by prefix ("" for the default namespace); those in scope at it are the ones of its ancestors too (enter/2, resolve/2)
childrenthe child elements, in order
textthe character data directly inside the element, in one string: white space only when the element has child elements and nothing else
linethe line of the start tag

Summary

Types

The prefixes in scope at an element: the declarations of the element and of each of its ancestors, the innermost first. An element has only its own (namespaces), so a deep document costs what it declares and not what is in scope at each of its leaves.

t()

Functions

The value of an attribute that has no namespace, or nil.

The child elements with this local name and namespace.

The scope at an element, given the scope at its parent: its own declarations go in front of those of its ancestors.

Resolves a name with a prefix ("feel:string") to {namespace, local name}, with the prefixes in scope. A name with no prefix is in the default namespace, if there is one. :error for a prefix that is not declared.

The scope before the root element: the xml prefix, which is always declared.

Types

scope()

@type scope() :: [%{optional(String.t()) => String.t()}]

The prefixes in scope at an element: the declarations of the element and of each of its ancestors, the innermost first. An element has only its own (namespaces), so a deep document costs what it declares and not what is in scope at each of its leaves.

t()

@type t() :: %Dmn.XML.Element{
  attributes: %{optional(String.t()) => String.t()},
  children: [t()],
  line: pos_integer() | nil,
  name: String.t(),
  namespace: String.t() | nil,
  namespaces: %{optional(String.t()) => String.t()},
  qualified: %{optional({String.t(), String.t()}) => String.t()},
  text: String.t()
}

Functions

attribute(element, name)

@spec attribute(t(), String.t()) :: String.t() | nil

The value of an attribute that has no namespace, or nil.

iex> Dmn.XML.Element.attribute(%Dmn.XML.Element{name: "a", attributes: %{"id" => "x"}}, "id")
"x"

children(element, namespace, name)

@spec children(t(), String.t() | nil, String.t()) :: [t()]

The child elements with this local name and namespace.

iex> element = %Dmn.XML.Element{name: "a", children: [%Dmn.XML.Element{name: "b", namespace: "urn:x"}, %Dmn.XML.Element{name: "c"}]}
iex> Enum.map(Dmn.XML.Element.children(element, "urn:x", "b"), & &1.name)
["b"]

enter(scope, element)

@spec enter(scope(), t()) :: scope()

The scope at an element, given the scope at its parent: its own declarations go in front of those of its ancestors.

iex> parent = Dmn.XML.Element.root_scope()
iex> child = %Dmn.XML.Element{name: "a", namespaces: %{"p" => "urn:p"}}
iex> Dmn.XML.Element.enter(parent, child)
[%{"p" => "urn:p"}, %{"xml" => "http://www.w3.org/XML/1998/namespace"}]

resolve(scope, qualified_name)

@spec resolve(scope(), String.t()) :: {:ok, {String.t() | nil, String.t()}} | :error

Resolves a name with a prefix ("feel:string") to {namespace, local name}, with the prefixes in scope. A name with no prefix is in the default namespace, if there is one. :error for a prefix that is not declared.

iex> scope = Dmn.XML.Element.enter([%{"q" => "urn:outer", "p" => "urn:old"}], %Dmn.XML.Element{name: "a", namespaces: %{"p" => "urn:p", "" => "urn:d"}})
iex> {Dmn.XML.Element.resolve(scope, "p:x"), Dmn.XML.Element.resolve(scope, "y"), Dmn.XML.Element.resolve(scope, "q:z"), Dmn.XML.Element.resolve(scope, "r:z")}
{{:ok, {"urn:p", "x"}}, {:ok, {"urn:d", "y"}}, {:ok, {"urn:outer", "z"}}, :error}

root_scope()

@spec root_scope() :: scope()

The scope before the root element: the xml prefix, which is always declared.

iex> Dmn.XML.Element.root_scope()
[%{"xml" => "http://www.w3.org/XML/1998/namespace"}]