Property definitions

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