Generic structure to manage and display an ordered tree

Ordered tree are tree where the elements of a same parent are in a specific order

Elements of the trees are added with the add method that takes a parent and a sub-element. If the parent is null, then the element is considered a root.

var t = new OrderedTree[String]
t.add(null, "root")
t.add("root", "child1")
t.add("root", "child2")
t.add("child1", "grand-child")
assert t.length == 4

By default, the elements with a same parent are visited in the order they are added.

assert t.to_a == ["root", "child1", "grand-child", "child2"]
assert t.write_to_string == """
root
|--child1
|  `--grand-child
`--child2
"""

The sort_with method can be used reorder elements

t.add("root", "aaa")
assert t.to_a == ["root", "child1", "grand-child", "child2", "aaa"]
t.sort_with(alpha_comparator)
assert t.to_a == ["root", "aaa", "child1", "grand-child", "child2"]

This class can be used as it to work with generic trees but can also be specialized to provide more specific behavior or display. It is why the internal attributes are mutable.

Introduced properties

fun add(p: nullable E, e: E)

ordered_tree :: OrderedTree :: add

Add a new element e in the tree.
fun add_all(p: nullable E, es: Collection[E])

ordered_tree :: OrderedTree :: add_all

Append all nodes es as children of p.
fun display(e: E): String

ordered_tree :: OrderedTree :: display

How to display a specific element of the tree
fun parent(e: E): nullable E

ordered_tree :: OrderedTree :: parent

The parent of the element e
fun roots: Array[E]

ordered_tree :: OrderedTree :: roots

The roots of the tree (in sequence)
protected fun roots=(roots: Array[E])

ordered_tree :: OrderedTree :: roots=

The roots of the tree (in sequence)
fun sort_with(comparator: Comparator)

ordered_tree :: OrderedTree :: sort_with

Sort roots and other elements using a comparator method
fun sub: HashMap[E, Array[E]]

ordered_tree :: OrderedTree :: sub

The branches of the trees.
protected fun sub=(sub: HashMap[E, Array[E]])

ordered_tree :: OrderedTree :: sub=

The branches of the trees.
protected fun write_line(o: Writer, e: E, prefix: String)

ordered_tree :: OrderedTree :: write_line

Write the full line for the element e in o.

Redefined properties

redef fun ==(other: nullable Object): Bool

ordered_tree $ OrderedTree :: ==

Two trees are equal if they have the same nodes in the same order
redef type SELF: OrderedTree[E]

ordered_tree $ OrderedTree :: SELF

Type of this instance, automatically specialized in every class
redef fun clone: SELF

ordered_tree $ OrderedTree :: clone

Shallow clone of the tree.
redef fun first: E

ordered_tree $ OrderedTree :: first

var tree = new OrderedTree[Int]
redef fun has(e: nullable Object): Bool

ordered_tree $ OrderedTree :: has

Is item in the collection ?
redef fun hash: Int

ordered_tree $ OrderedTree :: hash

The hash code of the object.
redef fun is_empty: Bool

ordered_tree $ OrderedTree :: is_empty

var tree = new OrderedTree[Int]
redef fun iterator: Iterator[E]

ordered_tree $ OrderedTree :: iterator

var tree = new OrderedTree[Int]
redef fun length: Int

ordered_tree $ OrderedTree :: length

Number of items in the collection.
redef fun to_a: Array[E]

ordered_tree $ OrderedTree :: to_a

Get an array of the contained elements
redef fun write_to(stream: Writer)

ordered_tree $ OrderedTree :: write_to

print the full tree on o

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 CONCURRENT: ConcurrentCollection[E]

core :: Collection :: CONCURRENT

Type of the concurrent variant of this collection
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
fun add(p: nullable E, e: E)

ordered_tree :: OrderedTree :: add

Add a new element e in the tree.
fun add_all(p: nullable E, es: Collection[E])

ordered_tree :: OrderedTree :: add_all

Append all nodes es as children of p.
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.
abstract fun clone: SELF

core :: Cloneable :: clone

Duplicate self
fun combinations(r: Int): Collection[SequenceRead[E]]

core :: Collection :: combinations

All r-length combinations on self (in same order) without repeated elements.
fun combinations_with_replacement(r: Int): Collection[SequenceRead[E]]

core :: Collection :: combinations_with_replacement

All r-length combination on self (in same order) with repeated elements.
fun count(item: nullable Object): Int

core :: Collection :: count

How many occurrences of item are in the collection?
fun display(e: E): String

ordered_tree :: OrderedTree :: display

How to display a specific element of the tree
fun first: E

core :: Collection :: first

Return the first item of the collection
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun has(item: nullable Object): Bool

core :: Collection :: has

Is item in the collection ?
fun has_all(other: Collection[nullable Object]): Bool

core :: Collection :: has_all

Does the collection contain at least each element of other?
fun has_any(other: Collection[nullable Object]): Bool

core :: Collection :: has_any

Does the collection contain at least one element of other?
fun has_exactly(other: Collection[nullable Object]): Bool

core :: Collection :: has_exactly

Does the collection contain exactly all the elements of other?
fun has_only(item: nullable Object): Bool

core :: Collection :: has_only

Is the collection contain only item?
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".
fun is_empty: Bool

core :: Collection :: is_empty

Is there no item in the collection?
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.
abstract fun iterator: Iterator[E]

core :: Collection :: iterator

Get a new iterator on the collection.
fun join(separator: nullable Text, last_separator: nullable Text): String

core :: Collection :: join

Concatenate and separate each elements with separator.
fun length: Int

core :: Collection :: length

Number of items in the collection.
fun not_empty: Bool

core :: Collection :: not_empty

Alias for not is_empty.
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).
fun parent(e: E): nullable E

ordered_tree :: OrderedTree :: parent

The parent of the element e
fun permutations(r: Int): Collection[SequenceRead[E]]

core :: Collection :: permutations

All r-length permutations on self (all possible ordering) without repeated elements.
fun plain_to_s: String

core :: Collection :: plain_to_s

Concatenate elements without separators
fun product(r: Int): Collection[SequenceRead[E]]

core :: Collection :: product

Cartesian product, over r times self.
fun rand: E

core :: Collection :: rand

Return a random element form the collection
fun roots: Array[E]

ordered_tree :: OrderedTree :: roots

The roots of the tree (in sequence)
protected fun roots=(roots: Array[E])

ordered_tree :: OrderedTree :: roots=

The roots of the tree (in sequence)
fun sample(length: Int): Array[E]

core :: Collection :: sample

Return a new array made of (at most) length elements randomly chosen.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
fun sort_with(comparator: Comparator)

ordered_tree :: OrderedTree :: sort_with

Sort roots and other elements using a comparator method
fun sub: HashMap[E, Array[E]]

ordered_tree :: OrderedTree :: sub

The branches of the trees.
protected fun sub=(sub: HashMap[E, Array[E]])

ordered_tree :: OrderedTree :: sub=

The branches of the trees.
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
fun to_a: Array[E]

core :: Collection :: to_a

Build a new array from a collection
abstract fun to_concurrent: CONCURRENT

core :: Collection :: to_concurrent

Wraps self in a thread-safe collection
fun to_counter: Counter[E]

core :: Collection :: to_counter

Create and fill up a counter with the elements of `self.
fun to_curlslist: CURLSList

core :: Collection :: to_curlslist

Convert Collection[String] to CURLSList
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 to_shuffle: Array[E]

core :: Collection :: to_shuffle

Return a new array made of elements in a random order.
protected fun write_line(o: Writer, e: E, prefix: String)

ordered_tree :: OrderedTree :: write_line

Write the full line for the element e in o.
abstract fun write_to(stream: Writer)

core :: Writable :: write_to

Write itself to a stream
fun write_to_bytes: Bytes

core :: Writable :: write_to_bytes

Like write_to but return a new Bytes (may be quite large)
fun write_to_file(filepath: String)

core :: Writable :: write_to_file

Like write_to but take care of creating the file
fun write_to_string: String

core :: Writable :: write_to_string

Like write_to but return a new String (may be quite large).
package_diagram ordered_tree::OrderedTree OrderedTree core::Writable Writable ordered_tree::OrderedTree->core::Writable core::Collection Collection ordered_tree::OrderedTree->core::Collection core::Cloneable Cloneable ordered_tree::OrderedTree->core::Cloneable core::Object Object core::Writable->core::Object core::Collection->core::Object core::Cloneable->core::Object ...core::Object ... ...core::Object->core::Object

Ancestors

interface Object

core :: Object

The root of the class hierarchy.

Parents

interface Cloneable

core :: Cloneable

Something that can be cloned
interface Collection[E: nullable Object]

core :: Collection

The root of the collection hierarchy.
interface Writable

core :: Writable

Things that can be efficienlty written to a Writer

Class definitions

ordered_tree $ OrderedTree
# Generic structure to manage and display an ordered tree
#
# Ordered tree are tree where the elements of a same parent are in a specific order
#
# Elements of the trees are added with the `add` method that takes a parent and
# a sub-element.
# If the parent is `null`, then the element is considered a root.
#
# ~~~~
# var t = new OrderedTree[String]
# t.add(null, "root")
# t.add("root", "child1")
# t.add("root", "child2")
# t.add("child1", "grand-child")
# assert t.length == 4
# ~~~~
#
# By default, the elements with a same parent
# are visited in the order they are added.
#
# ~~~
# assert t.to_a == ["root", "child1", "grand-child", "child2"]
# assert t.write_to_string == """
# root
# |--child1
# |  `--grand-child
# `--child2
# """
# ~~~
#
# The `sort_with` method can be used reorder elements
#
# ~~~
# t.add("root", "aaa")
# assert t.to_a == ["root", "child1", "grand-child", "child2", "aaa"]
# t.sort_with(alpha_comparator)
# assert t.to_a == ["root", "aaa", "child1", "grand-child", "child2"]
# ~~~
#
# This class can be used as it to work with generic trees but can also be specialized to provide more specific
# behavior or display. It is why the internal attributes are mutable.
class OrderedTree[E: Object]
	super Writable
	super Collection[E]
	super Cloneable

	# The roots of the tree (in sequence)
	var roots = new Array[E]

	# The branches of the trees.
	# For each element, the ordered array of its direct sub-elements.
	var sub = new HashMap[E, Array[E]]

	# The parent of each element.
	#
	# Roots have `null` as parent.
	private var parents = new HashMap[E, nullable E]

	redef fun length do return parents.length

	redef fun has(e) do return parents.has_key(e)

	# The parent of the element `e`
	#
	# Roots have `null` as parent.
	#
	# ~~~
	# var tree = new OrderedTree[Int]
	# tree.add(1, 2)
	# assert tree.parent(2) == 1
	# assert tree.parent(1) == null
	# ~~~
	fun parent(e: E): nullable E do return parents[e]

	# Add a new element `e` in the tree.
	#
	# `p` is the parent of `e`.
	# If `p` is null, then `e` is a root element.
	#
	# If `e` is already in the tree, it is detached from its old
	# parent and attached to the new parent `p`.
	fun add(p: nullable E, e: E)
	do
		detach(e)
		parents[e] = p
		if p == null then
			roots.add(e)
		else
			if not has(p) then add(null, p)
			if sub.has_key(p) then
				sub[p].add(e)
			else
				sub[p] = [e]
			end
		end
	end

	# Append all nodes `es` as children of `p`.
	fun add_all(p: nullable E, es: Collection[E])
	do
		for e in es do add(p, e)
	end

	# Temporary remove `e`.
	#
	# Children of `e` are left untouched in the tree.
	# This make the tree inconstant until `e` is added back.
	private fun detach(e: E)
	do
		var old_parent = parents.get_or_null(e)
		if old_parent != null then
			var subs = sub[old_parent]
			subs.remove(e)
			if subs.is_empty then
				# remove the sub when all children are detached
				# so that `==` and `hash` are sane
				# Otherwise an empty array will be considered
				# differently than no array.
				sub.keys.remove(old_parent)
			end
		else if roots.has(e) then
			roots.remove(e)
		end
	end

	# print the full tree on `o`
	# Write a ASCII-style tree and use the `display` method to label elements
	redef fun write_to(stream: Writer)
	do
		for r in roots do
			write_line(stream, r, "")
			sub_write_to(stream, r, "")
		end
	end

	private fun sub_write_to(o: Writer, e: E, prefix: String)
	do
		if not sub.has_key(e) then return
		var subs = sub[e]
		if subs.is_empty then return
		var last = subs.last
		for e2 in subs do
			if e2 != last then
				write_line(o, e2, prefix+"|--")
				sub_write_to(o, e2, prefix+"|  ")
			else
				write_line(o, e2, prefix+"`--")
				sub_write_to(o, e2, prefix+"   ")
			end
		end
	end

	# Write the full line for the element `e` in `o`.
	#
	# Basically it does:
	#
	# ~~~nitish
	# o.write "{prefix}{display(e)}\n"
	# ~~~
	#
	# Usually, you should redefine `display` to change the display of an element.
	protected fun write_line(o: Writer, e: E, prefix: String)
	do
		o.write "{prefix}{display(e)}\n"
	end

	# Sort roots and other elements using a comparator method
	# This method basically sorts roots then each group of children
	fun sort_with(comparator: Comparator)
	do
		comparator.sort(roots)
		for a in sub.values do
			comparator.sort(a)
		end
	end

	# How to display a specific element of the tree
	# By defaut, uses `to_s`
	#
	# Subclasses should redefine this method to provide a specific output
	fun display(e: E): String do return e.to_s

	# Get an array of the contained elements
	# Order is preserved
	#
	#     var tree = new OrderedTree[Int]
	#     tree.add_all(null, [1, 2])
	#     tree.add_all(1, [11, 12])
	#     tree.add_all(11, [111, 112])
	#     tree.add_all(12, [121, 122])
	#     tree.add_all(2, [21, 22])
	#     assert tree.to_a == [1, 11, 111, 112, 12, 121, 122, 2, 21, 22]
	redef fun to_a: Array[E] do
		var res = new Array[E]
		for r in roots do sub_to_a(r, res)
		return res
	end

	private fun sub_to_a(e: E, res: Array[E]) do
		res.add e
		if sub.has_key(e) then for e2 in sub[e] do sub_to_a(e2, res)
	end

	#     var tree = new OrderedTree[Int]
	#     assert tree.is_empty
	#     tree.add(null, 1)
	#     assert not tree.is_empty
	redef fun is_empty: Bool do return roots.is_empty

	#     var tree = new OrderedTree[Int]
	#     tree.add(null, 1)
	#     tree.add(1, 11)
	#     assert tree.first == 1
	redef fun first do return roots.first

	#     var tree = new OrderedTree[Int]
	#     tree.add_all(null, [1, 2])
	#     tree.add_all(1, [11, 12])
	#     tree.add_all(11, [111, 112])
	#     tree.add_all(12, [121, 122])
	#     tree.add_all(2, [21, 22])
	#     var order = [1, 11, 111, 112, 12, 121, 122, 2, 21, 22]
	#     assert tree.iterator.to_a == order
	redef fun iterator do return new OrderedTreeIterator[E](self)

	# Two trees are equal if they have the same nodes in the same order
	#
	# ~~~
	# var t1 = new OrderedTree[Int]
	# t1.add_all(null, [1, 2])
	# t1.add_all(1, [11, 12])
	#
	# var t2 = new OrderedTree[Int]
	# t2.add_all(null, [1, 2])
	#
	# assert t1 != t2
	#
	# t2.add_all(1, [11, 12])
	#
	# assert t1 == t2
	# ~~~
	redef fun ==(other)
	do
		if not other isa OrderedTree[Object] then return false
		return roots == other.roots and sub == other.sub
	end

	redef fun hash
	do
		return roots.hash + sub.hash
	end

	# Shallow clone of the tree.
	#
	# ~~~
	# var t = new OrderedTree[Int]
	# t.add_all(null, [1, 2])
	# t.add_all(1, [11, 12])
	#
	# assert t.clone == t
	# ~~~
	redef fun clone
	do
		var res = new OrderedTree[E]
		res.add_all(null, roots)
		for p, es in sub do
			res.add_all(p, es)
		end
		return res
	end
end
lib/ordered_tree/ordered_tree.nit:20,1--290,3