Internal class for a single Namespace context.

This module caches and reuses Namespace contexts, so the number allocated will be equal to the element depth of the document, not to the total number of elements (i.e. 5-10 rather than tens of thousands). Also, data structures used to represent contexts are shared when possible (child contexts without declarations) to further reduce the amount of memory that's consumed.

Note: The original source code and documentation of this class comes, in part, from SAX 2.0.

Introduced properties

private var _attribute_name_table: nullable Map[String, Array[String]]

sax :: Context :: _attribute_name_table

Cache of process_name for attributes.
private var _decl_seen: Bool

sax :: Context :: _decl_seen

Was copy_tables called since the last call to parent=?
private var _declarations: nullable Array[String]

sax :: Context :: _declarations

All prefixes declared in this context.
private var _decls_ok: Bool

sax :: Context :: _decls_ok

Can we currently declare prefixes in this context?
private var _default_ns: nullable String

sax :: Context :: _default_ns

Namespace in absence of prefix.
private var _element_name_table: nullable Map[String, Array[String]]

sax :: Context :: _element_name_table

Cache of process_name for elements.
private var _empty: Collection[String]

sax :: Context :: _empty

private var _p_parent: nullable Context

sax :: Context :: _p_parent

Parent context.
private var _prefix_table: nullable Map[String, String]

sax :: Context :: _prefix_table

prefix -> uri
private fun attribute_name_table: nullable Map[String, Array[String]]

sax :: Context :: attribute_name_table

Cache of process_name for attributes.
private fun attribute_name_table=(attribute_name_table: nullable Map[String, Array[String]])

sax :: Context :: attribute_name_table=

Cache of process_name for attributes.
private fun clear

sax :: Context :: clear

Makes associated state become collectible, invalidating this context.
private fun copy_tables

sax :: Context :: copy_tables

Copy on write for the internal tables in this context.
private fun decl_seen: Bool

sax :: Context :: decl_seen

Was copy_tables called since the last call to parent=?
private fun decl_seen=(decl_seen: Bool)

sax :: Context :: decl_seen=

Was copy_tables called since the last call to parent=?
private fun declarations: nullable Array[String]

sax :: Context :: declarations

All prefixes declared in this context.
private fun declarations=(declarations: nullable Array[String])

sax :: Context :: declarations=

All prefixes declared in this context.
private fun declare_prefix(prefix: String, uri: String)

sax :: Context :: declare_prefix

Declare a Namespace prefix for this context.
private fun declared_prefixes: Collection[String]

sax :: Context :: declared_prefixes

Return all prefixes declared in this context (possibly empty).
private fun decls_ok: Bool

sax :: Context :: decls_ok

Can we currently declare prefixes in this context?
private fun decls_ok=(decls_ok: Bool)

sax :: Context :: decls_ok=

Can we currently declare prefixes in this context?
private fun default_ns: nullable String

sax :: Context :: default_ns

Namespace in absence of prefix.
private fun default_ns=(default_ns: nullable String)

sax :: Context :: default_ns=

Namespace in absence of prefix.
private fun element_name_table: nullable Map[String, Array[String]]

sax :: Context :: element_name_table

Cache of process_name for elements.
private fun element_name_table=(element_name_table: nullable Map[String, Array[String]])

sax :: Context :: element_name_table=

Cache of process_name for elements.
private fun empty: Collection[String]

sax :: Context :: empty

private fun empty=(empty: Collection[String])

sax :: Context :: empty=

private fun p_parent: nullable Context

sax :: Context :: p_parent

Parent context.
private fun p_parent=(p_parent: nullable Context)

sax :: Context :: p_parent=

Parent context.
private fun parent=(parent: Context)

sax :: Context :: parent=

(Re)set the parent of this Namespace context.
private fun prefix(uri: String): nullable String

sax :: Context :: prefix

Look up one of the prefixes associated with a URI in this context.
private fun prefix_table: nullable Map[String, String]

sax :: Context :: prefix_table

prefix -> uri
private fun prefix_table=(prefix_table: nullable Map[String, String])

sax :: Context :: prefix_table=

prefix -> uri
private fun prefixes: Collection[String]

sax :: Context :: prefixes

Return all prefixes currently in force.
private fun process_name(qname: String, is_attribute: Bool): nullable Array[String]

sax :: Context :: process_name

Process a raw XML qualified name in this context.
private fun uri(prefix: String): nullable String

sax :: Context :: uri

Look up the URI associated with a prefix in this context.

Redefined properties

redef type SELF: Context

sax $ Context :: SELF

Type of this instance, automatically specialized in every class

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
private var _attribute_name_table: nullable Map[String, Array[String]]

sax :: Context :: _attribute_name_table

Cache of process_name for attributes.
private var _decl_seen: Bool

sax :: Context :: _decl_seen

Was copy_tables called since the last call to parent=?
private var _declarations: nullable Array[String]

sax :: Context :: _declarations

All prefixes declared in this context.
private var _decls_ok: Bool

sax :: Context :: _decls_ok

Can we currently declare prefixes in this context?
private var _default_ns: nullable String

sax :: Context :: _default_ns

Namespace in absence of prefix.
private var _element_name_table: nullable Map[String, Array[String]]

sax :: Context :: _element_name_table

Cache of process_name for elements.
private var _empty: Collection[String]

sax :: Context :: _empty

private var _p_parent: nullable Context

sax :: Context :: _p_parent

Parent context.
private var _prefix_table: nullable Map[String, String]

sax :: Context :: _prefix_table

prefix -> uri
private fun attribute_name_table: nullable Map[String, Array[String]]

sax :: Context :: attribute_name_table

Cache of process_name for attributes.
private fun attribute_name_table=(attribute_name_table: nullable Map[String, Array[String]])

sax :: Context :: attribute_name_table=

Cache of process_name for attributes.
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
private fun clear

sax :: Context :: clear

Makes associated state become collectible, invalidating this context.
private fun copy_tables

sax :: Context :: copy_tables

Copy on write for the internal tables in this context.
private fun decl_seen: Bool

sax :: Context :: decl_seen

Was copy_tables called since the last call to parent=?
private fun decl_seen=(decl_seen: Bool)

sax :: Context :: decl_seen=

Was copy_tables called since the last call to parent=?
private fun declarations: nullable Array[String]

sax :: Context :: declarations

All prefixes declared in this context.
private fun declarations=(declarations: nullable Array[String])

sax :: Context :: declarations=

All prefixes declared in this context.
private fun declare_prefix(prefix: String, uri: String)

sax :: Context :: declare_prefix

Declare a Namespace prefix for this context.
private fun declared_prefixes: Collection[String]

sax :: Context :: declared_prefixes

Return all prefixes declared in this context (possibly empty).
private fun decls_ok: Bool

sax :: Context :: decls_ok

Can we currently declare prefixes in this context?
private fun decls_ok=(decls_ok: Bool)

sax :: Context :: decls_ok=

Can we currently declare prefixes in this context?
private fun default_ns: nullable String

sax :: Context :: default_ns

Namespace in absence of prefix.
private fun default_ns=(default_ns: nullable String)

sax :: Context :: default_ns=

Namespace in absence of prefix.
private fun element_name_table: nullable Map[String, Array[String]]

sax :: Context :: element_name_table

Cache of process_name for elements.
private fun element_name_table=(element_name_table: nullable Map[String, Array[String]])

sax :: Context :: element_name_table=

Cache of process_name for elements.
private fun empty: Collection[String]

sax :: Context :: empty

private fun empty=(empty: Collection[String])

sax :: Context :: empty=

fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
private intern fun native_class_name: CString

core :: Object :: native_class_name

The class name of the object in CString format.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
private fun p_parent: nullable Context

sax :: Context :: p_parent

Parent context.
private fun p_parent=(p_parent: nullable Context)

sax :: Context :: p_parent=

Parent context.
private fun parent=(parent: Context)

sax :: Context :: parent=

(Re)set the parent of this Namespace context.
private fun prefix(uri: String): nullable String

sax :: Context :: prefix

Look up one of the prefixes associated with a URI in this context.
private fun prefix_table: nullable Map[String, String]

sax :: Context :: prefix_table

prefix -> uri
private fun prefix_table=(prefix_table: nullable Map[String, String])

sax :: Context :: prefix_table=

prefix -> uri
private fun prefixes: Collection[String]

sax :: Context :: prefixes

Return all prefixes currently in force.
private fun process_name(qname: String, is_attribute: Bool): nullable Array[String]

sax :: Context :: process_name

Process a raw XML qualified name in this context.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
private fun uri(prefix: String): nullable String

sax :: Context :: uri

Look up the URI associated with a prefix in this context.
package_diagram sax::namespace_support::Context Context core::Object Object sax::namespace_support::Context->core::Object

Parents

interface Object

core :: Object

The root of the class hierarchy.

Class definitions

sax $ Context
# Internal class for a single Namespace context.
#
# This module caches and reuses Namespace contexts,
# so the number allocated
# will be equal to the element depth of the document, not to the total
# number of elements (i.e. 5-10 rather than tens of thousands).
# Also, data structures used to represent contexts are shared when
# possible (child contexts without declarations) to further reduce
# the amount of memory that's consumed.
#
# Note: The original source code and documentation of this class comes, in part,
# from [SAX 2.0](http://www.saxproject.org).
private class Context

	var empty: Collection[String] = new Array[String].with_capacity(0)

	# `prefix` -> `uri`
	var prefix_table: nullable Map[String, String] = null

	# Cache of `process_name` for elements.
	#
	# `qname -> [uri, local_name, qname]`
	var element_name_table: nullable Map[String, Array[String]] = null

	# Cache of `process_name` for attributes.
	#
	# `qname -> [uri, local_name, qname]`
	var attribute_name_table: nullable Map[String, Array[String]] = null

	# Namespace in absence of prefix.
	var default_ns: nullable String = null

	# Can we currently declare prefixes in this context?
	var decls_ok: Bool = true is writable

	# All prefixes declared in this context.
	var declarations: nullable Array[String] = null

	# Was `copy_tables` called since the last call to `parent=`?
	var decl_seen: Bool = false

	# Parent context.
	var p_parent: nullable Context = null

	# (Re)set the parent of this Namespace context.
	#
	# The context must either have been freshly constructed,
	# or must have been cleared.
	#
	# Parameters:
	#
	# * `context`: parent Namespace context object.
	fun parent=(parent: Context) do
		p_parent = parent
		declarations = null
		prefix_table = parent.prefix_table
		element_name_table = parent.element_name_table
		attribute_name_table = parent.attribute_name_table
		default_ns = parent.default_ns
		decl_seen = false
		decls_ok = true
	end

	# Makes associated state become collectible, invalidating this context.
	#
	# `parent=` must be called before this context may be used again.
	fun clear do
		p_parent = null
		prefix_table = null
		element_name_table = null
		attribute_name_table = null
		default_ns = null
		declarations = null
	end

	# Declare a Namespace prefix for this context.
	#
	# Parameters:
	#
	# * `prefix`: prefix to declare.
	# * `uri`: associated Namespace URI.
	#
	# SEE: `NamespaceSupport.declare_prefix`
	fun declare_prefix(prefix: String, uri: String) do
		assert legal_state: decls_ok else
			sys.stderr.write("Can't declare any more prefixes in this context.\n")
		end

		# Lazy processing...
		if not decl_seen then
			copy_tables
		end

		if "" == prefix then
			if "" == uri then
				default_ns = null
			else
				default_ns = uri
			end
		else if "" == uri then
			prefix_table.keys.remove(prefix)
		else
			prefix_table[prefix] = uri
		end
		declarations.push(prefix)
	end

	# Process a raw XML qualified name in this context.
	#
	# Parameters:
	#
	# * `qname`: raw XML qualified name.
	# * `is_attribute`: `true` if this is an attribute name.
	#
	# Returns:
	#
	# An array of three strings containing the URI part (or empty string),
	# the local part and the raw name, or `null` if there is an undeclared
	# prefix.
	#
	# SEE: `NamespaceSupport.process_name`
	fun process_name(qname: String, is_attribute: Bool):
			nullable Array[String] do
		var name: Array[String]
		var table: Map[String, Array[String]]
		var match: nullable Match

		# Detect errors in call sequence.
		decls_ok = false
		# Select the appropriate table.
		if is_attribute then
			table = attribute_name_table.as(not null)
		else
			table = element_name_table.as(not null)
		end

		# Start by looking in the cache, and
		# return immediately if the name
		# is already known in this content.
		if table.keys.has(qname) then
			return table[qname]
		end

		# We haven't seen this name in this
		# context before. Maybe in the parent
		# context, but we can't assume prefix
		# bindings are the same.
		name = new Array[String].with_capacity(3)
		match = qname.search(':')

		if match == null then
			# No prefix
			if is_attribute then
				name.push("")
			else
				name.push(default_ns or else "")
			end
			name.push(qname)
			name.push(qname)
		else
			# Prefix
			var prefix = qname.substring(0, match.from)

			if prefix == "" then
				if is_attribute then
					name.push("")
				else
					name.push(default_ns or else "")
				end
				name.push(qname.substring_from(match.after))
				name.push(qname)
			else if (not is_attribute) and "xmlns" == prefix then
				return null
			else if prefix_table.keys.has(prefix) then
				name.push(prefix_table[prefix])
				name.push(qname.substring_from(match.after))
				name.push(qname)
			else
				return null
			end
		end

		# Save in the cache for future use.
		# (Could be shared with parent context...)
		table[qname] = name
		return name
	end

	# Look up the URI associated with a prefix in this context.
	#
	# Return `null` if no URI is associated with a specified prefix.
	#
	# Parameters:
	#
	# * `prefix`: prefix to look up.
	#
	# SEE: `NamespaceSupport.uri`
	fun uri(prefix: String): nullable String do
		if "" == prefix then
			return default_ns
		else if prefix_table == null then
			return null
		else
			return prefix_table.get_or_null(prefix)
		end
	end

	# Look up one of the prefixes associated with a URI in this context.
	#
	# Since many prefixes may be mapped to the same URI,
	# the return value may be unreliable.
	#
	# Parameters:
	#
	# * `uri`: URI to look up.
	#
	# Returns:
	#
	# The associated prefix, or `null` if none is declared.
	#
	# SEE: `NamespaceSupport.prefix`
	fun prefix(uri: String): nullable String do
		# Note: We do not use the original code from SAX 2.0.1 because it is
		# buggy with redefined prefixes. For example, with
		# `<x xmlns:y="1"><z xmlns:y="2" /></x>`, when in `z`, `uri("1")`
		# returns `"y"` in the original code while it should return `null`.
		# Our code is slower, but it works.
		var all_prefixes = prefixes

		for prefix in all_prefixes do
			if uri == self.uri(prefix) then
				return prefix
			end
		end
		return null
	end

	# Return all prefixes declared in this context (possibly empty).
	#
	# SEE: `NamespaceSupport.declared_prefixes`
	fun declared_prefixes: Collection[String] do
		return declarations or else empty
	end

	# Return all prefixes currently in force.
	#
	# The default prefix, if in force, is *not*
	# returned, and will have to be checked for separately.
	#
	# SEE: `NamespaceSupport.prefixes`
	fun prefixes: Collection[String] do
		if prefix_table == null then
			return empty
		else
			return prefix_table.keys
		end
	end

	# Copy on write for the internal tables in this context.
	#
	# This class is optimized for the normal case where most
	# elements do not contain Namespace declarations.
	fun copy_tables do
		if prefix_table != null then
			var old_prefix_table = prefix_table.as(not null)
			prefix_table = new HashMap[String, String]
			prefix_table.add_all(old_prefix_table)
		else
			prefix_table = new HashMap[String, String]
		end
		element_name_table = new HashMap[String, Array[String]]
		attribute_name_table = new HashMap[String, Array[String]]
		declarations = new Array[String]
		decl_seen = true
	end
end
lib/sax/helpers/namespace_support.nit:387,1--662,3