# Listing of doc/example-scripts/container.tcl

This example is a small design study to implement container classes with different features, namely a `SimpleContainer`, an `OrderedContainer` and a `SortedContainer`. First of all, we require NX:

    package req nx
    nx::test configure -count 1

## Simple Container

The first container class presented here is called `SimpleContainer`, which manages its contained items. As all container classes presented here, the items are created as child objects embedded in the container. If the container is deleted, all items are deleted as well. The items, which will be put into the container, should be instances of a certain class. We define here for this purpose an arbitrary class `C`:

    nx::Class create C

The class `SimpleContainer` keeps and manages items added to it. Every instance of this class might have different item classes. We might provide a prefix for naming the items, otherwise the default is `member`.

    nx::Class create SimpleContainer {
      :property {memberClass ::MyItem}
      :property {prefix member}

      # Require the method "autoname" for generating nice names
      :require method autoname

      # The method new is responsible for creating a child of the current
      # container.
      :public method new {args} {
        set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
        return $item
      }
    }

Create and instance of the class `SimpleContainer` …

    % SimpleContainer create container1 -memberClass ::C
    ::container1

and add a few items:

    % container1 new
    ::container1::member1

    % container1 new
    ::container1::member2

    % container1 new
    ::container1::member3

The elements of the container can be obtained via `info children`:

    % container1 info children
    ::container1::member1 ::container1::member2 ::container1::member3

## Ordered Container

In the example with `SimpleContainer`, the order of the results of `info children` just happens to be in the order of the added items, but in general, this order is not guaranteed, but depends on the population of the hash tables. In the next step, we extend the example above by preserving the order of the elements.

The class `OrderedContainer` is similar to `SimpleContainer`, but keeps a list of items that were added to the container. The item list is managed in a property `items` which is defined as `incremental` to make use of the `add` and `delete` methods provided by the slots.

    nx::Class create OrderedContainer -superclass SimpleContainer {
      :property -incremental {items:0..n {}}

      :public method new {args} {
        set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
        :items add $item end
        return $item
      }

      # Since we keep the list of items, we have to maintain it in case
      # items are deleted.
      :public method delete {item:object} {
        :items delete $item
        $item destroy
      }

    }

Create an instance of `OrderedContainer` …

    % OrderedContainer create container2 -memberClass ::C
    ::container2

and add a few items:

    % container2 new
    ::container2::member1

    % container2 new
    ::container2::member2

    % container2 new
    ::container2::member3

The elements of the container are obtained via the method `items`.

    % container2 items get
    ::container2::member1 ::container2::member2 ::container2::member3

When we delete an item in the container …

    % container2 delete ::container2::member2

the item is as well removed from the `items` list.

    % container2 items get
    ::container2::member1 ::container2::member3

## Sorted Container

In the next step, we define a `SortedContainer`, that keeps additionally a sorted list for iterating through the items without needing to sort the items when needed. The implementation maintains an additional sorted list. The implementation of the SortedContainer depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if we have no Tcl 8.6, just return here.

    if {[info command yield] eq ""} return

For sorting, we require the item class to have a key, that can be freely specified. We use there the property `name` of Class `D`:

    nx::Class create D {
      :property name:required
    }

    nx::Class create SortedContainer -superclass OrderedContainer {

      # In order to keep the index consisting of just the objects and to
      # ease sorting, we maintain two list, one list of values and one
      # list of objects. We assume for the time being, that the keys are
      # not changing.

      :variable values {}
      :variable index {}
      :property key

      :public method index {} { return ${:index}}

      :public method new {args} {
        set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
        if {[info exists :key]} {
          set value [$item cget -${:key}]
          set pos [lsearch -bisect ${:values} $value]
          set :values [linsert ${:values} [expr {$pos + 1}] $value]
          set :index  [linsert ${:index}  [expr {$pos + 1}] $item]
        }
        lappend :items $item
        return $item
      }

      # Since we keep the list of items, we have to maintain it in case
      # items are deleted.
      :public method delete {item:object} {
        set pos [lsearch ${:index} $item]
        if {$pos == -1} {error "item $item not found in container; items: ${:index}"}
        set :values [lreplace ${:values} $pos $pos]
        set :index  [lreplace ${:index}  $pos $pos]
        next
      }
    }

Create a container for class `D` with key `name`:

    SortedContainer create container3 -memberClass ::D -key name

Add a few items

    % container3 new -name victor
    ::container3::member1

    % container3 new -name stefan
    ::container3::member2

    % container3 new -name gustaf
    ::container3::member3

The method `items` returns the items in the order of insertion (as before):

    % container3 items get
    ::container3::member1 ::container3::member2 ::container3::member3

The method `index` returns the items in sorting order (sorted by the `name` member):

    % container3 index
    ::container3::member3 ::container3::member2 ::container3::member1

Now we delete an item:

    % container3 delete ::container3::member2

The item is as well removed from the result lists

    % container3 items get
    ::container3::member1 ::container3::member3

    % container3 index
    ::container3::member3 ::container3::member1