A filter that internally log events it recieves.

Usually, when testing, 2 SAXEventLogger are used: one on which methods are manually called to simulate expected results, and another on which we attach the tested XMLReader. Then, we can compare logs using diff.

Note: In order to test the XMLReader behaviour with ill-formed documents, fatal errors are not thrown by default.

SEE: SAXTestSuite

Introduced properties

fun clear_log

saxophonit :: SAXEventLogger :: clear_log

Clear the internal log.
fun diff(expected: SAXEventLogger): Text

saxophonit :: SAXEventLogger :: diff

Show the differences between the internal logs of self and expected.
fun term_default: String

saxophonit :: SAXEventLogger :: term_default

Treminal’s default formatting.
protected fun term_default=(term_default: String)

saxophonit :: SAXEventLogger :: term_default=

Treminal’s default formatting.
fun term_deletion: String

saxophonit :: SAXEventLogger :: term_deletion

Formatting for deletions.
protected fun term_deletion=(term_deletion: String)

saxophonit :: SAXEventLogger :: term_deletion=

Formatting for deletions.
fun term_deletion_emphasis: String

saxophonit :: SAXEventLogger :: term_deletion_emphasis

Formatting for emphased deletions
protected fun term_deletion_emphasis=(term_deletion_emphasis: String)

saxophonit :: SAXEventLogger :: term_deletion_emphasis=

Formatting for emphased deletions
fun term_insertion: String

saxophonit :: SAXEventLogger :: term_insertion

Formatting for insertions.
protected fun term_insertion=(term_insertion: String)

saxophonit :: SAXEventLogger :: term_insertion=

Formatting for insertions.
fun term_insertion_emphasis: String

saxophonit :: SAXEventLogger :: term_insertion_emphasis

Formatting for emphased insertions.
protected fun term_insertion_emphasis=(term_insertion_emphasis: String)

saxophonit :: SAXEventLogger :: term_insertion_emphasis=

Formatting for emphased insertions.

Redefined properties

redef type SELF: SAXEventLogger

saxophonit $ SAXEventLogger :: SELF

Type of this instance, automatically specialized in every class
redef fun attribute_decl(element_name: String, attribute_name: String, attribute_type: String, mode: nullable String, value: nullable String)

saxophonit $ SAXEventLogger :: attribute_decl

Report an attribute type declaration.
redef fun characters(str: String)

saxophonit $ SAXEventLogger :: characters

Receive notification of character data.
redef fun comment(str: String)

saxophonit $ SAXEventLogger :: comment

Report an XML comment anywhere in the document.
redef fun document_locator=(locator: SAXLocator)

saxophonit $ SAXEventLogger :: document_locator=

Receive an object for locating the origin of SAX document events.
redef fun element_decl(name: String, model: String)

saxophonit $ SAXEventLogger :: element_decl

Report an element type declaration.
redef fun end_cdata

saxophonit $ SAXEventLogger :: end_cdata

Report the end of a CDATA section.
redef fun end_document

saxophonit $ SAXEventLogger :: end_document

Receive notification of the end of a document.
redef fun end_dtd

saxophonit $ SAXEventLogger :: end_dtd

Report the end of DTD declarations.
redef fun end_element(uri: String, local_name: String, qname: String)

saxophonit $ SAXEventLogger :: end_element

Receive notification of the end of an element.
redef fun end_entity(name: String)

saxophonit $ SAXEventLogger :: end_entity

Report the end of an entity.
redef fun end_prefix_mapping(prefix: String)

saxophonit $ SAXEventLogger :: end_prefix_mapping

End the scope of a prefix-URI mapping.
redef fun error(exception: SAXParseException)

saxophonit $ SAXEventLogger :: error

Receive notification of a recoverable error.
redef fun external_entity_decl(name: String, value: String)

saxophonit $ SAXEventLogger :: external_entity_decl

Report a parsed external entity declaration.
redef fun fatal_error(exception: SAXParseException)

saxophonit $ SAXEventLogger :: fatal_error

Receive notification of a non-recoverable error.
redef fun ignorable_whitespace(str: String)

saxophonit $ SAXEventLogger :: ignorable_whitespace

Receive notification of ignorable whitespace in element content.
redef fun internal_entity_decl(name: String, value: String)

saxophonit $ SAXEventLogger :: internal_entity_decl

Report an internal entity declaration.
redef fun notation_decl(name: String, public_id: String, system_id: String)

saxophonit $ SAXEventLogger :: notation_decl

Receive notification of a notation declaration event.
redef fun parse(input: InputSource)

saxophonit $ SAXEventLogger :: parse

Parse an XML document.
redef fun processing_instruction(target: String, data: nullable String)

saxophonit $ SAXEventLogger :: processing_instruction

Receive notification of a processing instruction.
redef fun property(name: String): nullable Object

saxophonit $ SAXEventLogger :: property

Look up the value of a property.
redef fun property=(name: String, value: nullable Object)

saxophonit $ SAXEventLogger :: property=

Set the value of a property.
redef fun resolve_entity(public_id: nullable String, system_id: nullable String): nullable InputSource

saxophonit $ SAXEventLogger :: resolve_entity

Allow the application to resolve external entities.
redef fun skipped_entity(name: String)

saxophonit $ SAXEventLogger :: skipped_entity

Receive notification of a skipped entity.
redef fun start_cdata

saxophonit $ SAXEventLogger :: start_cdata

Report the start of a CDATA section.
redef fun start_document

saxophonit $ SAXEventLogger :: start_document

Receive notification of the beginning of a document.
redef fun start_dtd(name: String, public_id: nullable String, system_id: nullable String)

saxophonit $ SAXEventLogger :: start_dtd

Report the start of DTD declarations, if any.
redef fun start_element(uri: String, local_name: String, qname: String, atts: Attributes)

saxophonit $ SAXEventLogger :: start_element

Receive notification of the beginning of an element.
redef fun start_entity(name: String)

saxophonit $ SAXEventLogger :: start_entity

Report the beginning of some internal and external XML entities.
redef fun start_prefix_mapping(prefix: String, uri: String)

saxophonit $ SAXEventLogger :: start_prefix_mapping

Begin the scope of a prefix-URI Namespace mapping.
redef fun unparsed_entity_decl(name: String, public_id: String, system_id: String)

saxophonit $ SAXEventLogger :: unparsed_entity_decl

Receive notification of an unparsed entity declaration event.
redef fun warning(exception: SAXParseException)

saxophonit $ SAXEventLogger :: warning

Receive notification of a warning.

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
fun attribute_decl(element_name: String, attribute_name: String, attribute_type: String, mode: nullable String, value: nullable String)

sax :: DeclHandler :: attribute_decl

Report an attribute type declaration.
fun characters(str: String)

sax :: ContentHandler :: characters

Receive notification of character data.
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.
fun clear_log

saxophonit :: SAXEventLogger :: clear_log

Clear the internal log.
fun comment(str: String)

sax :: LexicalHandler :: comment

Report an XML comment anywhere in the document.
abstract fun content_handler: nullable ContentHandler

sax :: XMLReader :: content_handler

Return the current content handler.
abstract fun content_handler=(handler: nullable ContentHandler)

sax :: XMLReader :: content_handler=

Allow an application to register a content event handler.
fun diff(expected: SAXEventLogger): Text

saxophonit :: SAXEventLogger :: diff

Show the differences between the internal logs of self and expected.
fun document_locator=(locator: SAXLocator)

sax :: ContentHandler :: document_locator=

Receive an object for locating the origin of SAX document events.
abstract fun dtd_handler: nullable DTDHandler

sax :: XMLReader :: dtd_handler

Return the current DTD handler.
abstract fun dtd_handler=(handler: nullable DTDHandler)

sax :: XMLReader :: dtd_handler=

Allow an application to register a DTD event handler.
fun element_decl(name: String, model: String)

sax :: DeclHandler :: element_decl

Report an element type declaration.
fun end_cdata

sax :: LexicalHandler :: end_cdata

Report the end of a CDATA section.
fun end_document

sax :: ContentHandler :: end_document

Receive notification of the end of a document.
fun end_dtd

sax :: LexicalHandler :: end_dtd

Report the end of DTD declarations.
fun end_element(uri: String, local_name: String, qname: String)

sax :: ContentHandler :: end_element

Receive notification of the end of an element.
fun end_entity(name: String)

sax :: LexicalHandler :: end_entity

Report the end of an entity.
fun end_prefix_mapping(prefix: String)

sax :: ContentHandler :: end_prefix_mapping

End the scope of a prefix-URI mapping.
abstract fun entity_resolver: nullable EntityResolver

sax :: XMLReader :: entity_resolver

Return the current entity resolver.
abstract fun entity_resolver=(resolver: nullable EntityResolver)

sax :: XMLReader :: entity_resolver=

Allow an application to register an entity resolver.
fun error(exception: SAXParseException)

sax :: ErrorHandler :: error

Receive notification of a recoverable error.
abstract fun error_handler: nullable ErrorHandler

sax :: XMLReader :: error_handler

Return the current error handler.
abstract fun error_handler=(handler: nullable ErrorHandler)

sax :: XMLReader :: error_handler=

Allow an application to register an error event handler.
fun external_entity_decl(name: String, value: String)

sax :: DeclHandler :: external_entity_decl

Report a parsed external entity declaration.
fun fatal_error(exception: SAXParseException)

sax :: ErrorHandler :: fatal_error

Receive notification of a non-recoverable error.
abstract fun feature(name: String): Bool

sax :: XMLReader :: feature

Look up the value of a feature flag.
abstract fun feature=(name: String, value: Bool)

sax :: XMLReader :: feature=

Set the value of a feature flag.
abstract fun feature_readable(name: String): Bool

sax :: XMLReader :: feature_readable

Is the retrieval of the specified feature flag supported given the current context?
abstract fun feature_recognized(name: String): Bool

sax :: XMLReader :: feature_recognized

Is the specified feature flag recognized by this parser?
abstract fun feature_writable(name: String): Bool

sax :: XMLReader :: feature_writable

Is the modification of the specified feature flag supported given the current context?
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.
fun ignorable_whitespace(str: String)

sax :: ContentHandler :: ignorable_whitespace

Receive notification of ignorable whitespace in element content.
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".
fun internal_entity_decl(name: String, value: String)

sax :: DeclHandler :: internal_entity_decl

Report an internal entity declaration.
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.
fun notation_decl(name: String, public_id: String, system_id: String)

sax :: DTDHandler :: notation_decl

Receive notification of a notation declaration event.
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).
abstract fun parent: nullable XMLReader

sax :: XMLFilter :: parent

Get the parent reader.
abstract fun parent=(parent: nullable XMLReader)

sax :: XMLFilter :: parent=

Set the parent reader.
abstract fun parse(input: InputSource)

sax :: XMLReader :: parse

Parse an XML document.
abstract fun parse_file(system_id: String)

sax :: XMLReader :: parse_file

Parse an XML document from a system identifier (URI).
fun processing_instruction(target: String, data: nullable String)

sax :: ContentHandler :: processing_instruction

Receive notification of a processing instruction.
abstract fun property(name: String): nullable Object

sax :: XMLReader :: property

Look up the value of a property.
abstract fun property=(name: String, value: nullable Object)

sax :: XMLReader :: property=

Set the value of a property.
abstract fun property_readable(name: String): Bool

sax :: XMLReader :: property_readable

Is the retrieval of the specified property supported given the current context?
abstract fun property_recognized(name: String): Bool

sax :: XMLReader :: property_recognized

Is the specified property recognized by this parser?
abstract fun property_writable(name: String): Bool

sax :: XMLReader :: property_writable

Is the modification of the specified property supported given the current context?
fun resolve_entity(public_id: nullable String, system_id: nullable String): nullable InputSource

sax :: EntityResolver :: resolve_entity

Allow the application to resolve external entities.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
fun skipped_entity(name: String)

sax :: ContentHandler :: skipped_entity

Receive notification of a skipped entity.
fun start_cdata

sax :: LexicalHandler :: start_cdata

Report the start of a CDATA section.
fun start_document

sax :: ContentHandler :: start_document

Receive notification of the beginning of a document.
fun start_dtd(name: String, public_id: nullable String, system_id: nullable String)

sax :: LexicalHandler :: start_dtd

Report the start of DTD declarations, if any.
fun start_element(uri: String, local_name: String, qname: String, atts: Attributes)

sax :: ContentHandler :: start_element

Receive notification of the beginning of an element.
fun start_entity(name: String)

sax :: LexicalHandler :: start_entity

Report the beginning of some internal and external XML entities.
fun start_prefix_mapping(prefix: String, uri: String)

sax :: ContentHandler :: start_prefix_mapping

Begin the scope of a prefix-URI Namespace mapping.
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
fun term_default: String

saxophonit :: SAXEventLogger :: term_default

Treminal’s default formatting.
protected fun term_default=(term_default: String)

saxophonit :: SAXEventLogger :: term_default=

Treminal’s default formatting.
fun term_deletion: String

saxophonit :: SAXEventLogger :: term_deletion

Formatting for deletions.
protected fun term_deletion=(term_deletion: String)

saxophonit :: SAXEventLogger :: term_deletion=

Formatting for deletions.
fun term_deletion_emphasis: String

saxophonit :: SAXEventLogger :: term_deletion_emphasis

Formatting for emphased deletions
protected fun term_deletion_emphasis=(term_deletion_emphasis: String)

saxophonit :: SAXEventLogger :: term_deletion_emphasis=

Formatting for emphased deletions
fun term_insertion: String

saxophonit :: SAXEventLogger :: term_insertion

Formatting for insertions.
protected fun term_insertion=(term_insertion: String)

saxophonit :: SAXEventLogger :: term_insertion=

Formatting for insertions.
fun term_insertion_emphasis: String

saxophonit :: SAXEventLogger :: term_insertion_emphasis

Formatting for emphased insertions.
protected fun term_insertion_emphasis=(term_insertion_emphasis: String)

saxophonit :: SAXEventLogger :: term_insertion_emphasis=

Formatting for emphased insertions.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
fun unparsed_entity_decl(name: String, public_id: String, system_id: String)

sax :: DTDHandler :: unparsed_entity_decl

Receive notification of an unparsed entity declaration event.
fun warning(exception: SAXParseException)

sax :: ErrorHandler :: warning

Receive notification of a warning.
init with_parent(parent_reader: XMLReader)

sax :: XMLFilterImpl :: with_parent

Construct an XML filter with the specified parent.
package_diagram saxophonit::SAXEventLogger SAXEventLogger sax::XMLFilterImpl XMLFilterImpl saxophonit::SAXEventLogger->sax::XMLFilterImpl sax::DeclHandler DeclHandler saxophonit::SAXEventLogger->sax::DeclHandler sax::LexicalHandler LexicalHandler saxophonit::SAXEventLogger->sax::LexicalHandler sax::XMLFilter XMLFilter sax::XMLFilterImpl->sax::XMLFilter sax::EntityResolver EntityResolver sax::XMLFilterImpl->sax::EntityResolver sax::DTDHandler DTDHandler sax::XMLFilterImpl->sax::DTDHandler sax::ContentHandler ContentHandler sax::XMLFilterImpl->sax::ContentHandler sax::ErrorHandler ErrorHandler sax::XMLFilterImpl->sax::ErrorHandler core::Object Object sax::DeclHandler->core::Object sax::LexicalHandler->core::Object ...sax::XMLFilter ... ...sax::XMLFilter->sax::XMLFilter ...sax::EntityResolver ... ...sax::EntityResolver->sax::EntityResolver ...sax::DTDHandler ... ...sax::DTDHandler->sax::DTDHandler ...sax::ContentHandler ... ...sax::ContentHandler->sax::ContentHandler ...sax::ErrorHandler ... ...sax::ErrorHandler->sax::ErrorHandler ...core::Object ... ...core::Object->core::Object

Ancestors

abstract class ContentHandler

sax :: ContentHandler

Receives notification of the logical content of a document.
abstract class DTDHandler

sax :: DTDHandler

Receives notification of basic DTD-related events.
abstract class EntityResolver

sax :: EntityResolver

Basic interface for resolving entities.
abstract class ErrorHandler

sax :: ErrorHandler

Basic interface for SAX error handlers.
interface Object

core :: Object

The root of the class hierarchy.
interface XMLFilter

sax :: XMLFilter

Interface for an XML filter.
interface XMLReader

sax :: XMLReader

Interface for reading an XML document using callbacks.

Parents

abstract class DeclHandler

sax :: DeclHandler

SAX2 extension handler for DTD declaration events.
abstract class LexicalHandler

sax :: LexicalHandler

SAX2 extension handler for lexical events.
class XMLFilterImpl

sax :: XMLFilterImpl

Base class for deriving an XML filter.

Class definitions

saxophonit $ SAXEventLogger
# A filter that internally log events it recieves.
#
# Usually, when testing, 2 `SAXEventLogger` are used: one on which methods are
# manually called to simulate expected results, and another on which we attach
# the tested `XMLReader`. Then, we can compare logs using `diff`.
#
# Note: In order to test the `XMLReader` behaviour with ill-formed documents,
# fatal errors are not thrown by default.
#
# SEE: SAXTestSuite
class SAXEventLogger
	super XMLFilterImpl
	super DeclHandler
	super LexicalHandler

	# The logged events.
	#
	# Each entry begins with the name of the event. Entries are sorted in the
	# order they fired (the oldest first). Two event loggers have equivalent
	# logs if and only if they received the same events in the same order and
	# with equivalent arguments.
	private var log = new Array[Array[String]]

	# http://xml.org/sax/properties/declaration-handler
	private var decl_handler: nullable DeclHandler = null
	private var decl_handler_uri = "http://xml.org/sax/properties/declaration-handler"

	# http://xml.org/sax/properties/lexical-handler
	private var lexical_handler: nullable LexicalHandler = null
	private var lexical_handler_uri = "http://xml.org/sax/properties/declaration-handler"


	# Constants for diff formatting.

	# Treminal’s default formatting.
	var term_default: String = (new TermCharFormat).to_s

	# Formatting for insertions.
	var term_insertion: String =
			(new TermCharFormat).green_fg.normal_weight.to_s

	# Formatting for emphased insertions.
	var term_insertion_emphasis: String =
			(new TermCharFormat).green_fg.bold.to_s

	# Formatting for deletions.
	var term_deletion: String =
			(new TermCharFormat).red_fg.normal_weight.to_s

	# Formatting for emphased deletions
	var term_deletion_emphasis: String =
			(new TermCharFormat).red_fg.bold.to_s


	# Clear the internal log.
	fun clear_log do
		log.clear
	end

	# Show the differences between the internal logs of `self` and `expected`.
	#
	# If there is no differences, return an empty string. Else, return a string
	# designed to be printed in the terminal. In this case, `=` means “in both”,
	# `<` means “in `self`” and `>` means “in `expected`”.
	fun diff(expected: SAXEventLogger): Text do
		var buf = new FlatBuffer
		var sub_diff: Array[Int]
		var equal: Bool
		var i = 0
		var min: Int
		var max: Int

		if log.length < expected.log.length then
			equal = false
			min = log.length
			max = expected.log.length
		else if expected.log.length < log.length then
			equal = false
			min = expected.log.length
			max = log.length
		else
			equal = true
			min = log.length
			max = log.length
		end

		while i < min do
			sub_diff = diff_entry(log[i], expected.log[i])
			if sub_diff.length > 0 then
				if equal then
					diff_append_matches(buf, log, [0..i[)
					equal = false
				end
				diff_append_deletion(buf, log, i, sub_diff)
				diff_append_insertion(buf, expected.log, i, sub_diff)
			else if not equal then
				diff_append_matches(buf, log, [i..i])
			end
			i += 1
		end
		if log.length < expected.log.length then
			while i < max do
				diff_append_insertion(buf, expected.log, i,
						[0..(expected.log[i].length)[)
				i += 1
			end
		else
			while i < max do
				diff_append_deletion(buf, log, i, [0..(log[i].length)[)
				i += 1
			end
		end
		return buf
	end

	# Return the list of positions where `actual` and `expected` mismatch.
	#
	# Indexes are in ascending order.
	private fun diff_entry(actual: Array[String], expected: Array[String]):
			Array[Int] do
		var result = new Array[Int]
		var i = 0
		var min: Int
		var max: Int

		if actual.length < expected.length then
			min = actual.length
			max = expected.length
		else if expected.length < actual.length then
			min = expected.length
			max = actual.length
		else
			min = actual.length
			max = actual.length
		end

		while i < min do
			if expected[i] != actual[i] then
				result.push(i)
			end
			i += 1
		end
		result.insert_all([i..max[, result.length)
		return result
	end

	# Append matches to the diff.
	#
	# Parameters:
	#
	# * `buf`: buffer for the diff.
	# * `log`: original log.
	# * `range`: range to append to the diff.
	private fun diff_append_matches(buf: Buffer, log: Array[Array[String]],
			range: Range[Int]) do
		for i in range do
			buf.append("= {i}|{log[i].join("; ")}\n")
		end
	end

	# Append a deletion to the diff.
	#
	# Parameters:
	#
	# * `buf`: buffer for the diff.
	# * `log`: log that contains the deleted entry.
	# * `entry_index`: index of the deleted entry in `log`.
	# * `sorted_mismatches`: sorted list of indexes of the items to emphasize
	# in the specified entry.
	private fun diff_append_deletion(buf: Buffer, log: Array[Array[String]],
			entry_index: Int, sorted_mismatches: Collection[Int]) do
		buf.append(term_deletion)
		buf.append("< {entry_index}|")
		diff_append_mismatch_entry(buf, log[entry_index], sorted_mismatches,
			term_deletion, term_deletion_emphasis)
		buf.append(term_default)
		buf.append("\n")
	end

	# Append a insertion to the diff.
	#
	# Parameters:
	#
	# * `buf`: buffer for the diff.
	# * `log`: log that contains the inserted entry.
	# * `entry_index`: index of the inserted entry in `log`.
	# * `sorted_mismatches`: sorted list of indexes of the items to emphasize
	# in the specified entry.
	private fun diff_append_insertion(buf: Buffer, log: Array[Array[String]],
			entry_index: Int, sorted_mismatches: Collection[Int]) do
		buf.append(term_insertion)
		buf.append("> {entry_index}|")
		diff_append_mismatch_entry(buf, log[entry_index], sorted_mismatches,
			term_insertion, term_insertion_emphasis)
		buf.append(term_default)
		buf.append("\n")
	end

	# Show an entry of a mismatch (without the margin).
	#
	# Append the string designed to be printed in the terminal to the
	# specified buffer.
	#
	# Parameters:
	#
	# * `buf`: output buffer.
	# * `entry`: entry to format.
	# * `sorted_mismatches`: sorted list of indexes of the items to emphasize.
	# * `term_normal`: terminal control code to re-apply the formatting that was
	# in force prior calling this method.
	# * `term_emphasis`: terminal control code to apply to items listed in
	# `sorted_mismatches`.
	private fun diff_append_mismatch_entry(buf: Buffer, entry: Array[String],
			sorted_mismatches: Collection[Int], term_normal: String,
			term_emphasis: String) do
		var i = 0
		var j = sorted_mismatches.iterator
		var length = entry.length

		while i < length do
			while j.is_ok and j.item < i do
				j.next
			end
			if j.is_ok and j.item == i then
				buf.append(term_emphasis)
				buf.append(entry[i])
				buf.append(term_normal)
			else
				buf.append(entry[i])
			end
			i += 1
			if i < length then
				buf.append("; ")
			end
		end
	end

	############################################################################
	# XMLReader

	redef fun property(name) do
		assert sax_recognized: parent != null else
			sys.stderr.write("Property: {name}\n")
		end
		if decl_handler_uri == name then
			assert property_readable: property_readable(name) else
				sys.stderr.write("Property: {name}\n")
			end
			return decl_handler
		else if lexical_handler_uri == name then
			assert property_readable: property_readable(name) else
				sys.stderr.write("Property: {name}\n")
			end
			return lexical_handler
		else
			return parent.property(name)
		end
	end

	redef fun property=(name, value) do
		assert sax_recognized: parent != null else
			sys.stderr.write("Property: {name}\n")
		end
		if decl_handler_uri == name then
			assert property_readable: property_writable(name) else
				sys.stderr.write("Property: {name}\n")
			end
			decl_handler = value.as(nullable DeclHandler)
		else if lexical_handler_uri == name then
			assert property_readable: property_writable(name) else
				sys.stderr.write("Property: {name}\n")
			end
			lexical_handler = value.as(nullable LexicalHandler)
		else
			parent.property(name) = value
		end
	end

	redef fun parse(input) do
		assert parent_is_not_null: parent != 0 else
			sys.stderr.write("No parent for filter.")
		end
		if parent.feature_writable(decl_handler_uri) then
			parent.property(decl_handler_uri) = self
		end
		if parent.feature_writable(lexical_handler_uri) then
			parent.property(lexical_handler_uri) = self
		end
		super
	end


	############################################################################
	# EntityResolver

	redef fun resolve_entity(public_id, system_id) do
		log.push(["resolve_entity",
				public_id or else "^NULL",
				system_id or else "^NULL"])
		return super
	end


	############################################################################
	# DTDHandler

	redef fun notation_decl(name, public_id, system_id) do
		log.push(["notation_decl", name, public_id, system_id])
		super
	end

	redef fun unparsed_entity_decl(name, public_id, system_id) do
		log.push(["unparsed_entity_decl", name, public_id, system_id])
		super
	end


	############################################################################
	# ContentHandler

	redef fun document_locator=(locator) do
		log.push(["document_locator=",
				locator.public_id or else "^NULL",
				locator.system_id or else "^NULL",
				locator.line_number.to_s,
				locator.column_number.to_s])
		super
	end

	redef fun start_document do
		log.push(["start_document"])
		super
	end

	redef fun end_document do
		log.push(["end_document"])
		super
	end

	redef fun start_prefix_mapping(prefix, uri) do
		log.push(["start_prefix_mapping", prefix, uri])
		super
	end

	redef fun end_prefix_mapping(prefix) do
		log.push(["end_prefix_mapping", prefix])
		super
	end

	redef fun start_element(uri, local_name, qname, atts) do
		var entry = new Array[String]
		var i = 0
		var length = atts.length

		entry.push("start_element")
		entry.push(uri)
		entry.push(local_name)
		entry.push(qname)
		while i < length do
			entry.push(atts.uri(i) or else "^NULL")
			entry.push(atts.local_name(i) or else "^NULL")
			entry.push(atts.qname(i) or else "^NULL")
			entry.push(atts.type_of(i) or else "^NULL")
			entry.push(atts.value_of(i) or else "^NULL")
			i += 1
		end
		log.push(entry)
		super
	end

	redef fun end_element(uri, local_name, qname) do
		log.push(["end_element", uri, local_name, qname])
		super
	end

	redef fun characters(str) do
		log.push(["characters", str])
		super
	end

	redef fun ignorable_whitespace(str) do
		log.push(["ignorable_witespace", str])
		super
	end

	redef fun processing_instruction(target, data) do
		log.push(["processing_instruction", target, data or else "^NULL"])
		super
	end

	redef fun skipped_entity(name) do
		log.push(["skipped_entity", name])
		super
	end


	############################################################################
	# ErrorHandler

	redef fun warning(exception) do
		log.push(["warning", exception.full_message])
		super
	end

	redef fun error(exception) do
		log.push(["error", exception.full_message])
		super
	end

	redef fun fatal_error(exception) do
		log.push(["fatal_error", exception.full_message])
		if error_handler != null then
			error_handler.fatal_error(exception)
		end
	end


	############################################################################
	# DeclHandler

	redef fun element_decl(name, model) do
		log.push(["element_decl", name, model])
		if decl_handler != null then
			decl_handler.element_decl(name, model)
		end
	end

	redef fun attribute_decl(element_name, attribute_name, attribute_type, mode, value) do
		log.push(["attribute_decl",
				element_name,
				attribute_name,
				attribute_type,
				mode or else "^NULL",
				value or else "^NULL"])
		if decl_handler != null then
			decl_handler.attribute_decl(element_name, attribute_name,
					attribute_type, mode, value)
		end
	end

	redef fun internal_entity_decl(name, value) do
		log.push(["internal_entity_decl", name, value])
		if decl_handler != null then
			decl_handler.internal_entity_decl(name, value)
		end
	end

	redef fun external_entity_decl(name, value) do
		log.push(["external_entity_decl", name, value])
		if decl_handler != null then
			decl_handler.external_entity_decl(name, value)
		end
	end


	############################################################################
	# LexicalHandler

	redef fun start_dtd(name, public_id, system_id) do
		log.push(["start_dtd", name,
				public_id or else "^NULL",
				system_id or else "^NULL"])
		if lexical_handler != null then
			lexical_handler.start_dtd(name, public_id, system_id)
		end
	end

	redef fun end_dtd do
		log.push(["end_dtd"])
		if lexical_handler != null then
			lexical_handler.end_dtd
		end
	end

	redef fun start_entity(name) do
		log.push(["start_entity", name])
		if lexical_handler != null then
			lexical_handler.start_entity(name)
		end
	end

	redef fun end_entity(name) do
		log.push(["end_entity", name])
		if lexical_handler != null then
			lexical_handler.end_entity(name)
		end
	end

	redef fun start_cdata do
		log.push(["start_cdata"])
		if lexical_handler != null then
			lexical_handler.start_cdata
		end
	end

	redef fun end_cdata do
		log.push(["end_cdata"])
		if lexical_handler != null then
			lexical_handler.end_cdata
		end
	end

	redef fun comment(str) do
		log.push(["comment", str])
		if lexical_handler != null then
			lexical_handler.comment(str)
		end
	end
end
lib/saxophonit/testing.nit:21,1--529,3