Reader’s model.

Introduced classes

class XophonReaderModel

saxophonit :: XophonReaderModel

Reader’s model.

All class definitions

class XophonReaderModel

saxophonit $ XophonReaderModel

Reader’s model.
package_diagram saxophonit::reader_model reader_model sax sax saxophonit::reader_model->sax sax::sax_locator_impl sax_locator_impl saxophonit::reader_model->sax::sax_locator_impl sax::attributes_impl attributes_impl saxophonit::reader_model->sax::attributes_impl sax::namespace_support namespace_support saxophonit::reader_model->sax::namespace_support core core sax->core sax::sax_locator sax_locator sax::sax_locator_impl->sax::sax_locator sax::attributes attributes sax::attributes_impl->sax::attributes sax::namespace_support->core ...core ... ...core->core ...sax::sax_locator ... ...sax::sax_locator->sax::sax_locator ...sax::attributes ... ...sax::attributes->sax::attributes saxophonit::lexer lexer saxophonit::lexer->saxophonit::reader_model saxophonit::saxophonit saxophonit saxophonit::saxophonit->saxophonit::lexer saxophonit::saxophonit... ... saxophonit::saxophonit...->saxophonit::saxophonit

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module attributes

sax :: attributes

Interface for a list of XML attributes.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module content_handler

sax :: content_handler

Receives notification of the logical content of a document.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module dtd_handler

sax :: dtd_handler

Receives notification of basic DTD-related events.
module entity_resolver

sax :: entity_resolver

Basic interface for resolving entities.
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module error_handler

sax :: error_handler

Basic interface for SAX error handlers.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module input_source

sax :: input_source

A single input source for an XML entity.
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module sax_locator

sax :: sax_locator

Interface for associating a SAX event with a document location.
module sax_parse_exception

sax :: sax_parse_exception

Encapsulates an XML parse error or warning.
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module xml_filter

sax :: xml_filter

Interface for an XML filter.
module xml_reader

sax :: xml_reader

Interface for reading an XML document using callbacks.

Parents

module attributes_impl

sax :: attributes_impl

Default implementation of the Attributes interface.
module namespace_support

sax :: namespace_support

Encapsulates Namespace logic for use by applications using SAX, or internally by SAX drivers.
module sax

sax :: sax

Core SAX APIs.
module sax_locator_impl

sax :: sax_locator_impl

Provides an optional convenience implementation of Locator.

Children

module lexer

saxophonit :: lexer

SAXophoNit’s lexer

Descendants

module a_star-m

a_star-m

module saxophonit

saxophonit :: saxophonit

A SAX 2 parser in Nit.
# Reader’s model.
module saxophonit::reader_model

import sax
import sax::helpers::sax_locator_impl
import sax::helpers::attributes_impl
import sax::helpers::namespace_support

# Reader’s model.
#
# Handle event dispatching, settings and internal state.
class XophonReaderModel

	# Stack of the current element type qname and of the qnames of the ancestors.
	#
	# Current element is at the peek, the root element is at the bottom.
	# Used to check if end tags match start tags.
	private var element_path = new Array[XmlName]

	# Current element’s attributes
	private var atts = new AttributesImpl

	private var ns = new NamespaceSupport

	# Regular expression to match a `Name` against the `QName` production of
	# “Namespaces in XML”.
	private var qname_re: Regex = "^[^:]+(:[^:]+)?$".to_re

	# The locator that is used to indicate the current location.
	var locator: nullable SAXLocatorImpl = null is writable


	# TODO: Handle these features.

	private var features: Map[String, Bool] = new HashMap[String, Bool]

	# SEE: `sax`
	var feature_namespaces_uri =
			"http://xml.org/sax/features/namespaces"

	# SEE: `sax`
	var feature_namespace_prefixes_uri =
			"http://xml.org/sax/features/namespace-prefixes"


	# SEE: `sax::XMLReader.entity_resolver`
	var entity_resolver: nullable EntityResolver = null is writable

	# SEE: `sax::XMLReader.dtd_handler`
	var dtd_handler: nullable DTDHandler = null is writable

	# SEE: `sax::XMLReader.content_handler`
	var content_handler: nullable ContentHandler = null is writable

	# SEE: `sax::XMLReader.error_handler`
	var error_handler: nullable ErrorHandler = null is writable


	init do
		qname_re.optimize_has = true
		features[feature_namespaces_uri] = true
		features[feature_namespace_prefixes_uri] = false
	end

	# SEE: `sax::XMLReader.feature_recognized`
	fun feature_recognized(name: String): Bool do
		return features.keys.has(name)
	end

	# SEE: `sax::XMLReader.feature_readable`
	fun feature_readable(name: String): Bool do
		return features.keys.has(name)
	end

	# SEE: `sax::XMLReader.feature_writable`
	fun feature_writable(name: String): Bool do
		return features.keys.has(name)
	end

	# SEE: `sax::XMLReader.feature`
	fun feature(name: String): Bool do
		assert feature_recognized: feature_recognized(name)
		return features[name]
	end

	# SEE: `sax::XMLReader.feature=`
	fun feature=(name: String, value: Bool) do
		assert feature_recognized: feature_recognized(name)
		if name == feature_namespaces_uri then
			assert legal_state: value or features[feature_namespace_prefixes_uri] else
				sys.stderr.write("At least one of <{feature_namespaces_uri}> or <{feature_namespace_prefixes_uri}> must be true.\n")
			end
		else if name == feature_namespace_prefixes_uri then
			assert legal_state: value or features[feature_namespaces_uri] else
				sys.stderr.write("At least one of <{feature_namespaces_uri}> or <{feature_namespace_prefixes_uri}> must be true.\n")
			end
		end
		features[name] = value
	end

	# SEE: `sax::XMLReader.property_recognized`
	fun property_recognized(name: String): Bool do return false

	# SEE: `sax::XMLReader.property_readable`
	fun property_readable(name: String): Bool do return false

	# SEE: `sax::XMLReader.property_writable`
	fun property_writable(name: String): Bool do return false

	# SEE: `sax::XMLReader.property`
	fun property(name: String): nullable Object do
		assert property_recognized: false
		return null
	end

	# SEE: `sax::XMLReader.property=`
	fun property=(name: String, value: nullable Object) do
		assert property_recognized: false
	end

	# Is the root element closed?
	fun root_closed: Bool do return element_path.length <= 0

	# Expect the root element is closed.
	fun expect_root_closed: Bool do
		if root_closed then
			return true
		else if element_path.length > 1 then
			return fire_fatal_error("Reached the end of the file with " +
					"{element_path.length.to_s} open elements.", null)
		else
			return fire_fatal_error("Reached the end of the file with an " +
					"open element.", null)
		end
	end


	###########################################################################
	# Dispatching

	# Set the document locator of the content handler, if needed.
	fun fire_document_locator do
		if content_handler != null then
			content_handler.document_locator = locator.as(not null)
		end
	end

	# Fire the start of the document.
	fun fire_start_document do
		if content_handler != null then
			content_handler.start_document
		end
		ns.reset
	end

	# Fire the end of the document.
	fun fire_end_document do
		if content_handler != null then
			content_handler.end_document
		end
	end

	# Fire the start of an attribute list.
	fun fire_start_attributes do
		atts.clear
		ns.push_context
	end

	# Fire the appearance of an attribute.
	fun fire_attribute(qname: String, value: String) do
		if "xmlns" == qname or qname.has_prefix("xmlns:") then
			var prefix = qname.substring_from("xmlns:".length)

			if not prefix.has(":") then
				fire_start_prefix_mapping(prefix, value)
				if not feature(feature_namespace_prefixes_uri) then return
			end
		end
		# TODO: Types.
		atts.add("", "", qname, "CDATA", value)
	end

	# Fire the start of an element.
	fun fire_start_element(name: String) do
		var parts = ["", "", ""]

		for i in [0..atts.length[ do
			set_attribute_ns(i)
		end
		process_name(name, parts, false)
		element_path.push(new XmlName(parts[0], parts[1], parts[2]))
		if content_handler != null then
			content_handler.start_element(parts[0], parts[1], parts[2], atts)
		end
	end

	# Now prefixes are mapped, set the expanded name of the attribute at `index`.
	private fun set_attribute_ns(index: Int) do
		var name = ["", "", ""]

		process_name(atts.qname(index).as(not null), name, true)
		atts.uri(index) = name[0]
		atts.local_name(index) = name[1]
	end

	# Like `ns.process_name`, but with error handling.
	private fun process_name(qname: String, parts: Array[String],
			is_attribute: Bool) do
		if qname.has(qname_re) then
			if ns.process_name(qname, parts, is_attribute) == null then
				fire_error("The namespace IRI of `{qname}` was not found in " +
						"this scope. Passing the original name as the local " +
						"name.", null)
				parts = ["", qname, qname]
			end
		else
			fire_error("The name `{qname}` contains more than one colon. " +
					"Passing the original name as the local name.", null)
			parts = ["", qname, qname]
		end
	end

	# Fire the end of an element.
	#
	# Return `true` on success.
	fun fire_end_element(name: String):Bool do
		var peek_name = element_path.last

		if peek_name.qname == name then
			element_path.pop
			if content_handler != null then
				content_handler.end_element(peek_name.uri,
						peek_name.local_name, peek_name.qname)
			end
			return true
		else
			fire_fatal_error("The type in the closing tag (`{name}`) does " +
					"not match the type in the opening tag " +
					"(`{element_path.last.qname}`).", null)
			return false
		end
	end

	# Fire the start of a mapping between `prefix` and `uri`.
	private fun fire_start_prefix_mapping(prefix: String, uri: String) do
		if not ns.declare_prefix(prefix, uri) then
			fire_error("The mapping between the prefix `{prefix}` and " +
					"the namespace IRI `{uri}` breaks a built-in " +
					"mapping. Ignoring the declaration.", null)
		end
		if content_handler != null then
			content_handler.start_prefix_mapping(prefix, uri)
		end
	end

	# Fire the end of the current mapping of `prefix`.
	private fun end_prefix_mapping(prefix: String) do
		if content_handler != null then
			content_handler.end_prefix_mapping(prefix)
		end
	end

	# Fire the appearance of a comment.
	fun fire_comment(content: String) do
		# TODO
	end

	# Fire the appearance of a processing instruction.
	fun fire_processing_instruction(target: String, data: nullable String) do
		if content_handler != null then
			content_handler.processing_instruction(target, data)
		end
	end

	# Fire the start of a `CDATA` section.
	fun fire_start_cdata do
		# TODO
	end

	# Fire the end of a `CDATA` section.
	fun fire_end_cdata do
		# TODO
	end

	# Fire the appearance of a text node.
	fun fire_characters(str: String) do
		if content_handler != null then
			content_handler.characters(str)
		end
	end

	private fun exception(message: String, cause: nullable Error):
			SAXParseException do
		var e: SAXParseException

		if locator == null then
			e = new SAXParseException(message)
		else
			e = new SAXParseException.with_locator(message, locator.as(not null))
		end
		e.cause = cause
		return e
	end

	# Fire a fatal error with the specified message and cause.
	#
	# Return `false`.
	fun fire_fatal_error(message: String, cause: nullable Error):Bool do
		var e = exception(message, cause)

		if error_handler == null then
			e.throw
		else
			error_handler.fatal_error(e)
		end
		return false
	end

	# Fire an error with the specified message and cause.
	fun fire_error(message: String, cause: nullable Error) do
		var e = exception(message, cause)

		if error_handler != null then
			error_handler.error(e)
		end
	end

	# Fire a warning with the specified message and cause.
	fun fire_warning(message: String, cause: nullable Error) do
		var e = exception(message, cause)

		if error_handler != null then
			error_handler.warning(e)
		end
	end
end

# An XML expanded name.
private class XmlName
	# Namespace IRI or `""`.
	var uri: String

	# Local name or `""`.
	var local_name: String

	# Original qualified name.
	var qname: String
end
lib/saxophonit/reader_model.nit:11,1--358,3