sax :: NamespaceSupport :: defaultinit
# Encapsulates Namespace logic for use by applications using SAX, or internally by SAX drivers.
#
# This class encapsulates the logic of Namespace processing:
# it tracks the declarations currently in force for each context
# and automatically processes qualified XML names into their
# Namespace parts; it can also be used in reverse for generating
# XML qualified names from Namespaces.
#
# Namespace support objects are reusable, but the reset method
# must be invoked between each session.
#
# Here is a simple session:
#
# var parts: Array[String] = new Array[String].with_capacity(3)
# var support = new NamespaceSupport
# #
# support.push_context
# support.declare_prefix("", "http://www.w3.org/1999/xhtml")
# support.declare_prefix("dc", "http://www.purl.org/dc#")
# #
# parts = support.process_name("p", parts, false).as(not null)
# assert parts[0] == "http://www.w3.org/1999/xhtml"
# assert parts[1] == "p"
# assert parts[2] == "p"
# #
# parts = support.process_name("dc:title", parts, false).as(not null)
# assert parts[0] == "http://www.purl.org/dc#"
# assert parts[1] == "title"
# assert parts[2] == "dc:title"
# #
# support.pop_context
#
# Note that this class is optimized for the use case where most
# elements do not contain Namespace declarations: if the same
# prefix/URI mapping is repeated for each context (for example), this
# class will be somewhat less efficient.
#
# Although SAX drivers (parsers) may choose to use this class to
# implement namespace handling, they are not required to do so.
# Applications must track namespace information themselves if they
# want to use namespace information.
#
# Note: The original source code and documentation of this class comes, in part,
# from [SAX 2.0](http://www.saxproject.org).
class NamespaceSupport
# The XML Namespace URI as a constant.
#
# The value is `http://www.w3.org/XML/1998/namespace`
# as defined in the “Namespaces in XML” specification.
#
# This is the Namespace URI that is automatically mapped
# to the `xml` prefix.
var xmlns = "http://www.w3.org/XML/1998/namespace"
# The namespace declaration URI as a constant.
#
# The value is `http://www.w3.org/xmlns/2000/`, as defined
# in a erratum to the “Namespaces in XML” recommendation.
#
# This is the Namespace URI that is automatically mapped
# to the `xmlns` prefix.
var nsdecl = "http://www.w3.org/xmlns/2000/"
private var contexts = new Array[Context].with_capacity(32)
private var current_context = new Context
private var context_position: Int = 0
init do
contexts.push(current_context)
current_context.declare_prefix("xml", xmlns)
current_context.declare_prefix("xmlns", nsdecl)
end
# Reset this Namespace support object for reuse.
#
# It is necessary to invoke this method before reusing the
# Namespace support object for a new session.
fun reset do
contexts.clear
contexts.enlarge(32)
context_position = 0
current_context = new Context
contexts.push(current_context)
current_context.declare_prefix("xml", xmlns)
current_context.declare_prefix("xmlns", nsdecl)
end
# Start a new Namespace context.
#
# The new context will automatically inherit
# the declarations of its parent context, but it will also keep
# track of which declarations were made within this context.
#
# Event callback code should start a new context once per element.
# This means being ready to call this in either of two places.
# For elements that don't include namespace declarations, the
# `ContentHandler.start_element` callback is the right place.
# For elements with such a declaration, it'd done in the first
# `ContentHandler.start_prefix_mapping` callback.
# A boolean flag can be used to
# track whether a context has been started yet. When either of
# those methods is called, it checks the flag to see if a new context
# needs to be started. If so, it starts the context and sets the
# flag. After `ContentHandler.start_element` does that, it always clears
# the flag.
#
# Normally, SAX drivers would push a new context at the beginning
# of each XML element. Then they perform a first pass over the
# attributes to process all namespace declarations, making
# `ContentHandler.start_prefix_mapping` callbacks.
# Then a second pass is made, to determine the namespace-qualified
# names for all attributes and for the element name.
# Finally all the information for the
# `ContentHandler.start_element` callback is available,
# so it can then be made.
#
# The Namespace support object always starts with a base context
# already in force: in this context, only the `xml` prefix is
# declared.
#
# SEE: `sax::ContentHandler`
#
# SEE: `pop_context`
fun push_context do
current_context.decls_ok = false
context_position += 1
# Extend the array if necessary.
if context_position >= contexts.length then
current_context = new Context
contexts.push(current_context)
else
current_context = contexts[context_position]
end
# Set the parent, if any.
if context_position > 0 then
current_context.parent = contexts[context_position - 1]
end
end
# Revert to the previous Namespace context.
#
# Normally, you should pop the context at the end of each
# XML element. After popping the context, all Namespace prefix
# mappings that were previously in force are restored.
#
# You must not attempt to declare additional Namespace
# prefixes after popping a context, unless you push another
# context first.
#
# SEE: `push_context`
fun pop_context do
assert stack_not_empty: context_position > 0
current_context.clear
context_position -= 1
current_context = contexts[context_position]
end
# Declare a Namespace prefix.
#
# All prefixes must be declared before they are referenced.
# For example, a SAX driver (parser)
# would scan an element's attributes
# in two passes: first for namespace declarations,
# then a second pass using `process_name` to
# interpret prefixes against (potentially redefined) prefixes.
#
# This method declares a prefix in the current Namespace
# context; the prefix will remain in force until this context
# is popped, unless it is shadowed in a descendant context.
#
# To declare the default element Namespace, use the empty string as
# the prefix.
#
# Note that you must *not* declare a prefix after
# you've pushed and popped another Namespace context, or
# treated the declarations phase as complete by processing
# a prefixed name.
#
# Note that there is an asymmetry in this library:
# `prefix` will not return the `""` prefix,
# even if you have declared a default element namespace.
# To check for a default namespace,
# you have to look it up explicitly using `uri`.
# This asymmetry exists to make it easier to look up prefixes
# for attribute names, where the default prefix is not allowed.
#
# Parameters:
#
# * `prefix`: prefix to declare, or the empty string to
# indicate the default element namespace. This may never have
# the value `xml` or `xmlns`.
# * `uri`: The Namespace URI to associate with the prefix.
#
#
# Returns:
#
# `true` if the prefix and the URI are legal, `false` otherwise.
#
# SEE: `process_name`
#
# SEE: `uri`
#
# SEE: `prefix`
fun declare_prefix(prefix: String, uri: String): Bool do
if prefix == "xml" or prefix == "xmlns" or
uri == xmlns or uri == nsdecl then
return false
else
current_context.declare_prefix(prefix, uri)
return true
end
end
# Process a raw XML qualified name, after all declarations in the current context have been handled by `declare_prefix`.
#
# This method processes a raw XML qualified name in the current
# context by removing the prefix and looking it up among the
# prefixes currently declared. The return value will be the
# array supplied by the caller, filled in as follows:
#
# * `parts[0]`: Namespace URI, or an empty string if none is in use.
# * `parts[1]`: local name (without prefix).
# * `parts[2]`: original raw name.
#
# If the raw name has a prefix that has not been declared, then
# the return value will be `null`.
#
# Note that attribute names are processed differently than
# element names: an unprefixed element name will receive the
# default Namespace (if any), while an unprefixed attribute name
# will not.
#
# Parameters:
#
# * `qname`: raw XML qualified name to be processed.
# * `parts`: array supplied by the caller. Will be enlarged to 3 elements if
# needed. If the specified array contains more than 3 elements, its length
# will be kept intact.
# * `is_attribute`: flag indicating whether this is an attribute name
# (`true`) or an element name (`false`).
#
# SEE: `declare_prefix`
fun process_name(qname: String, parts: Array[String], is_attribute: Bool):
nullable Array[String] do
var my_parts = current_context.process_name(qname, is_attribute)
if my_parts == null then
return null
else
parts[0] = my_parts[0]
parts[1] = my_parts[1]
parts[2] = my_parts[2]
if parts[0] == "" and qname == "xmlns" and is_attribute then
parts[0] = nsdecl
parts[1] = ""
end
return parts
end
end
# Look up a prefix and get the currently-mapped Namespace URI.
#
# This method looks up the prefix in the current context.
# Use the empty string (`""`) for the default Namespace.
#
# Parameters:
#
# * `prefix`: The prefix to look up.
#
# Returns:
#
# The associated Namespace URI, or `null` if the prefix
# is undeclared in this context.
#
# SEE: `prefix`
#
# SEE: `prefixes_of`
fun uri(prefix: String): nullable String do
return current_context.uri(prefix)
end
# Return all prefixes currently declared.
#
# Note: if there is a default prefix, it will not be
# returned in this enumeration; check for the default prefix
# using the `uri` with an argument of `""` or use `declared_prefixes`.
#
# Returns:
#
# All prefixes declared in the current context except
# for the empty (default) prefix.
#
# SEE: `declared_prefixes`
#
# SEE: `uri`
fun prefixes: Collection[String] do return current_context.prefixes
# Return one of the prefixes mapped to a Namespace URI.
#
# If more than one prefix is currently mapped to the same
# URI, this method will make an arbitrary selection; if you
# want all of the prefixes, use the `prefixes_of` method instead.
#
# Note: this will never return the empty (default) prefix;
# to check for a default prefix, use the `uri`
# method with an argument of `""`.
#
# Parameters:
#
# * `uri`: Namespace URI.
#
# Returns:
#
# One of the prefixes currently mapped to the URI supplied,
# or `null` if none is mapped or if the URI is assigned to
# the default Namespace.
#
# SEE: `prefixes_of`
#
# SEE: `uri`
fun prefix(uri: String): nullable String do
return current_context.prefix(uri)
end
# Return all prefixes currently declared for an URI.
#
# This method returns prefixes mapped to a specific Namespace
# URI. The `xml` prefix will be included. If you want only one
# prefix that's mapped to the Namespace URI, and you don't care
# which one you get, use the `prefix` method instead.
#
# Note: the empty (default) prefix is *never* included
# in this enumeration; to check for the presence of a default
# Namespace, use the `uri` method with an argument of `""`.
#
# Parameters:
#
# * `uri`: The Namespace URI.
#
# SEE: `prefix`
#
# SEE: `declared_prefixes`
#
# SEE: `uri`
fun prefixes_of(uri: String): Collection[String] do
var prefixes = new Array[String]
var all_prefixes = self.prefixes
for prefix in all_prefixes do
if uri == self.uri(prefix) then
prefixes.push(prefix)
end
end
return prefixes
end
# Return all prefixes declared (and undeclared) in this context.
#
# The empty (default) prefix will be included in this
# enumeration; note that this behaviour differs from that of
# `prefix`, `prefixes` and `prefixes_of`.
#
# SEE: `prefixes`
#
# SEE: `uri`
fun declared_prefixes: Collection[String] do
return current_context.declared_prefixes
end
end
lib/sax/helpers/namespace_support.nit:14,1--384,3