File: container.tcl

package info (click to toggle)
nsf 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 13,208 kB
  • sloc: ansic: 32,687; tcl: 10,723; sh: 660; pascal: 176; javascript: 135; lisp: 41; makefile: 24
file content (181 lines) | stat: -rw-r--r-- 6,266 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#
# 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
package req nx::test
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"