1 | # -*- mode: tcl; indent-tabs-mode: nil -*- |
---|
2 | # ---------------------------------------------------------------------- |
---|
3 | # COMPONENT: ResultSet - set of XML objects for simulated results |
---|
4 | # |
---|
5 | # This data structure collects all of the simulated results |
---|
6 | # produced by a series of tool runs. It is used by the Analyzer, |
---|
7 | # ResultSelector, and other widgets to keep track of all known runs |
---|
8 | # and visualize the result that is currently selected. Each run |
---|
9 | # has an index number ("#1", "#2", "#3", etc.) that can be used to |
---|
10 | # label the run and refer to it later. |
---|
11 | # ====================================================================== |
---|
12 | # AUTHOR: Michael McLennan, Purdue University |
---|
13 | # Copyright (c) 2004-2012 HUBzero Foundation, LLC |
---|
14 | # |
---|
15 | # See the file "license.terms" for information on usage and |
---|
16 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
17 | # ====================================================================== |
---|
18 | package require Itcl |
---|
19 | |
---|
20 | itcl::class Rappture::ResultSet { |
---|
21 | constructor {args} { # defined below } |
---|
22 | destructor { # defined below } |
---|
23 | |
---|
24 | public method add {xmlobj} |
---|
25 | public method clear {{xmlobj ""}} |
---|
26 | public method diff {option args} |
---|
27 | public method find {collist vallist} |
---|
28 | public method get {collist xmlobj} |
---|
29 | public method contains {xmlobj} |
---|
30 | public method size {} |
---|
31 | |
---|
32 | public method notify {option args} |
---|
33 | protected method _notifyHandler {args} |
---|
34 | |
---|
35 | protected method _addOneResult {tuples xmlobj {simnum ""}} |
---|
36 | |
---|
37 | private variable _dispatcher "" ;# dispatchers for !events |
---|
38 | private variable _results "" ;# tuple of known results |
---|
39 | private variable _resultnum 0 ;# counter for result #1, #2, etc. |
---|
40 | private variable _notify ;# info used for notify command |
---|
41 | } |
---|
42 | |
---|
43 | # ---------------------------------------------------------------------- |
---|
44 | # CONSTRUCTOR |
---|
45 | # ---------------------------------------------------------------------- |
---|
46 | itcl::body Rappture::ResultSet::constructor {args} { |
---|
47 | # create a dispatcher for events |
---|
48 | Rappture::dispatcher _dispatcher |
---|
49 | $_dispatcher register !change |
---|
50 | $_dispatcher dispatch $this !change \ |
---|
51 | [itcl::code $this _notifyHandler] |
---|
52 | |
---|
53 | # create a list of tuples for data |
---|
54 | set _results [Rappture::Tuples ::#auto] |
---|
55 | $_results column insert end -name xmlobj -label "top-level XML object" |
---|
56 | $_results column insert end -name simnum -label "simulation number" |
---|
57 | |
---|
58 | # clear notification info |
---|
59 | set _notify(ALL) "" |
---|
60 | |
---|
61 | eval configure $args |
---|
62 | } |
---|
63 | |
---|
64 | # ---------------------------------------------------------------------- |
---|
65 | # DESTRUCTOR |
---|
66 | # ---------------------------------------------------------------------- |
---|
67 | itcl::body Rappture::ResultSet::destructor {} { |
---|
68 | clear |
---|
69 | itcl::delete object $_results |
---|
70 | } |
---|
71 | |
---|
72 | # ---------------------------------------------------------------------- |
---|
73 | # USAGE: add <xmlobj> |
---|
74 | # |
---|
75 | # Adds a new result to this result set. Scans through all existing |
---|
76 | # results to look for a difference compared to previous results. |
---|
77 | # Returns the simulation number (#1, #2, #3, etc.) of this new result |
---|
78 | # to the caller. The various data objects for this result set should |
---|
79 | # be added to their result viewers at the same index. |
---|
80 | # ---------------------------------------------------------------------- |
---|
81 | itcl::body Rappture::ResultSet::add {xmlobj} { |
---|
82 | set xmlobj0 [$_results get -format xmlobj end] |
---|
83 | if {$xmlobj0 eq ""} { |
---|
84 | # |
---|
85 | # If this is the first result, then there are no diffs. |
---|
86 | # Add it right in. |
---|
87 | # |
---|
88 | set simnum "#[incr _resultnum]" |
---|
89 | $_results insert end [list $xmlobj $simnum] |
---|
90 | } else { |
---|
91 | # |
---|
92 | # For all later results, find the diffs and add any new columns |
---|
93 | # into the results tuple. The latest result is the most recent. |
---|
94 | # |
---|
95 | set simnum [_addOneResult $_results $xmlobj] |
---|
96 | } |
---|
97 | |
---|
98 | # make sure we fix up associated controls |
---|
99 | $_dispatcher event -now !change op add what $xmlobj |
---|
100 | |
---|
101 | return $simnum |
---|
102 | } |
---|
103 | |
---|
104 | # ---------------------------------------------------------------------- |
---|
105 | # USAGE: clear ?<xmlobj>? |
---|
106 | # |
---|
107 | # Clears one or all results in this result set. If no specific |
---|
108 | # result object is specified, then all results are cleared. |
---|
109 | # ---------------------------------------------------------------------- |
---|
110 | itcl::body Rappture::ResultSet::clear {{xmlobj ""}} { |
---|
111 | if {$xmlobj ne ""} { |
---|
112 | # |
---|
113 | # Delete just one result. Look for the result among the |
---|
114 | # tuples and remove it. Then, rebuild all of the tuples |
---|
115 | # by scanning back through them and building them back up. |
---|
116 | # This will rebuild the columns/controls as they should |
---|
117 | # be now, removing anything that is no longer necessary. |
---|
118 | # |
---|
119 | set irun [$_results find -format xmlobj $xmlobj] |
---|
120 | if {[llength $irun] == 1} { |
---|
121 | # grab a description of what we're about to delete |
---|
122 | set dlist [list simnum [$_results get -format simnum $irun]] |
---|
123 | foreach col [lrange [$_results column names] 2 end] { |
---|
124 | set raw [lindex [Rappture::LibraryObj::value $xmlobj $col] 0] |
---|
125 | lappend dlist $col $raw ;# use "raw" (user-readable) label |
---|
126 | } |
---|
127 | |
---|
128 | # delete this from the tuples of all results |
---|
129 | itcl::delete object $xmlobj |
---|
130 | $_results delete $irun |
---|
131 | |
---|
132 | set new [Rappture::Tuples ::#auto] |
---|
133 | $new column insert end -name xmlobj -label "top-level XML object" |
---|
134 | $new column insert end -name simnum -label "simulation number" |
---|
135 | |
---|
136 | for {set n 0} {$n < [$_results size]} {incr n} { |
---|
137 | set rec [lindex [$_results get -format {xmlobj simnum} $n] 0] |
---|
138 | foreach {obj num} $rec break |
---|
139 | if {$n == 0} { |
---|
140 | $new insert end [list $obj $num] |
---|
141 | } else { |
---|
142 | _addOneResult $new $obj $num |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | # plug in the new set of rebuilt tuples |
---|
147 | itcl::delete object $_results |
---|
148 | set _results $new |
---|
149 | |
---|
150 | # make sure we fix up associated controls at some point |
---|
151 | $_dispatcher event -now !change op clear what $dlist |
---|
152 | } |
---|
153 | } else { |
---|
154 | # |
---|
155 | # Delete all results. |
---|
156 | # |
---|
157 | for {set irun 0} {$irun < [$_results size]} {incr irun} { |
---|
158 | set xo [$_results get -format xmlobj $irun] |
---|
159 | itcl::delete object $xo |
---|
160 | } |
---|
161 | $_results delete 0 end |
---|
162 | |
---|
163 | # make sure we fix up associated controls at some point |
---|
164 | $_dispatcher event -now !change op clear what all |
---|
165 | } |
---|
166 | |
---|
167 | if {[$_results size] == 0} { |
---|
168 | # no results left? then reset to a clean state |
---|
169 | eval $_results column delete [lrange [$_results column names] 2 end] |
---|
170 | set _resultnum 0 |
---|
171 | } |
---|
172 | } |
---|
173 | |
---|
174 | # ---------------------------------------------------------------------- |
---|
175 | # USAGE: diff names |
---|
176 | # USAGE: diff values <column> ?<which>? |
---|
177 | # |
---|
178 | # Returns information about the diffs in the current set of known |
---|
179 | # results. The "diff names" returns a list of column names for |
---|
180 | # parameters that have diffs. (These are the columns in the tuples.) |
---|
181 | # |
---|
182 | # The "diff values" returns the various values associated with a |
---|
183 | # particular <column> name. If the optional <which> is specified, |
---|
184 | # then it is treated as an index into the list of values--0 for the |
---|
185 | # first value, 1 for the second, etc. Each value is returned as |
---|
186 | # a list with two words. The first is the the label associated with |
---|
187 | # the value. The second is the normalized (numeric) value, which can |
---|
188 | # be sorted to get a particular ordering. |
---|
189 | # ---------------------------------------------------------------------- |
---|
190 | itcl::body Rappture::ResultSet::diff {option args} { |
---|
191 | switch -- $option { |
---|
192 | names { |
---|
193 | return [$_results column names] |
---|
194 | } |
---|
195 | values { |
---|
196 | if {[llength $args] < 1} { |
---|
197 | error "wrong # args: should be \"diff values col ?which?\"" |
---|
198 | } |
---|
199 | set col [lindex $args 0] |
---|
200 | |
---|
201 | set which "all" |
---|
202 | if {[llength $args] > 1} { |
---|
203 | set which [lindex $args 1] |
---|
204 | } |
---|
205 | |
---|
206 | set rlist "" |
---|
207 | # build an array of normalized values and their labels |
---|
208 | if {$col == "simnum"} { |
---|
209 | set nruns [$_results size] |
---|
210 | for {set n 0} {$n < $nruns} {incr n} { |
---|
211 | set simnum [$_results get -format simnum $n] |
---|
212 | lappend rlist $simnum $n |
---|
213 | } |
---|
214 | } else { |
---|
215 | set havenums 1 |
---|
216 | foreach rec [$_results get -format [list xmlobj $col]] { |
---|
217 | set xo [lindex $rec 0] |
---|
218 | set v [lindex $rec 1] |
---|
219 | foreach {raw norm} \ |
---|
220 | [Rappture::LibraryObj::value $xo $col] break |
---|
221 | |
---|
222 | if {![info exists unique($v)]} { |
---|
223 | # keep only unique label strings |
---|
224 | set unique($v) $norm |
---|
225 | } |
---|
226 | if {$havenums && ![string is double $norm]} { |
---|
227 | set havenums 0 |
---|
228 | } |
---|
229 | } |
---|
230 | |
---|
231 | if {!$havenums} { |
---|
232 | # don't have normalized nums? then sort and create nums |
---|
233 | set rlist "" |
---|
234 | set n 0 |
---|
235 | foreach val [lsort -dictionary [array names unique]] { |
---|
236 | lappend rlist $val [incr n] |
---|
237 | } |
---|
238 | } else { |
---|
239 | set rlist [array get unique] |
---|
240 | } |
---|
241 | } |
---|
242 | |
---|
243 | if {$which eq "all"} { |
---|
244 | return $rlist |
---|
245 | } |
---|
246 | |
---|
247 | # treat the "which" parameter as an XML object |
---|
248 | set irun [lindex [$_results find -format xmlobj $which] 0] |
---|
249 | if {$irun ne ""} { |
---|
250 | set val [lindex [$_results get -format $col $irun] 0] |
---|
251 | array set val2norm $rlist |
---|
252 | if {[info exists val2norm($val)]} { |
---|
253 | return [list $val $val2norm($val)] |
---|
254 | } |
---|
255 | } |
---|
256 | } |
---|
257 | default { |
---|
258 | error "bad option \"$option\": should be names or values" |
---|
259 | } |
---|
260 | } |
---|
261 | } |
---|
262 | |
---|
263 | # ---------------------------------------------------------------------- |
---|
264 | # USAGE: find <columnList> <valueList> |
---|
265 | # |
---|
266 | # Searches through the results for a set of tuple values that match |
---|
267 | # the <valueList> for the given <columnList>. Returns a list of |
---|
268 | # matching xml objects or "" if there is no match. If the <valueList> |
---|
269 | # is *, then it returns a list of all xml objects. |
---|
270 | # ---------------------------------------------------------------------- |
---|
271 | itcl::body Rappture::ResultSet::find {collist vallist} { |
---|
272 | if {$vallist eq "*"} { |
---|
273 | return [$_results get -format xmlobj] |
---|
274 | } |
---|
275 | |
---|
276 | set rlist "" |
---|
277 | foreach irun [$_results find -format $collist -- $vallist] { |
---|
278 | lappend rlist [$_results get -format xmlobj $irun] |
---|
279 | } |
---|
280 | return $rlist |
---|
281 | } |
---|
282 | |
---|
283 | # ---------------------------------------------------------------------- |
---|
284 | # USAGE: get <columnList> <xmlobj> |
---|
285 | # |
---|
286 | # Returns values for the specified <columnList> for the given <xmlobj>. |
---|
287 | # This is a way of querying associated data for the given object. |
---|
288 | # ---------------------------------------------------------------------- |
---|
289 | itcl::body Rappture::ResultSet::get {collist xmlobj} { |
---|
290 | set irun [lindex [$_results find -format xmlobj $xmlobj] 0] |
---|
291 | if {$irun ne ""} { |
---|
292 | return [lindex [$_results get -format $collist $irun] 0] |
---|
293 | } |
---|
294 | return "" |
---|
295 | } |
---|
296 | |
---|
297 | # ---------------------------------------------------------------------- |
---|
298 | # USAGE: contains <xmlobj> |
---|
299 | # |
---|
300 | # Checks to see if the given <xmlobj> is already represented by |
---|
301 | # some result in this result set. This comes in handy when checking |
---|
302 | # to see if an input case is already covered. |
---|
303 | # |
---|
304 | # Returns 1 if the result set already contains this result, and |
---|
305 | # 0 otherwise. |
---|
306 | # ---------------------------------------------------------------------- |
---|
307 | itcl::body Rappture::ResultSet::contains {xmlobj} { |
---|
308 | # no results? then this must be new |
---|
309 | if {[$_results size] == 0} { |
---|
310 | return 0 |
---|
311 | } |
---|
312 | |
---|
313 | # |
---|
314 | # Compare this new object against the last XML object in the |
---|
315 | # results set. If it has a difference, make sure that there |
---|
316 | # is a column to represent the quantity with the difference. |
---|
317 | # |
---|
318 | set xmlobj0 [$_results get -format xmlobj end] |
---|
319 | foreach {op vpath oldval newval} [$xmlobj0 diff $xmlobj] { |
---|
320 | if {[$xmlobj get $vpath.about.diffs] == "ignore"} { |
---|
321 | continue |
---|
322 | } |
---|
323 | if {$op == "+" || $op == "-"} { |
---|
324 | # ignore differences where parameters come and go |
---|
325 | # such differences make it hard to work controls |
---|
326 | continue |
---|
327 | } |
---|
328 | if {[$_results column names $vpath] == ""} { |
---|
329 | # no column for this quantity yet |
---|
330 | return 0 |
---|
331 | } |
---|
332 | } |
---|
333 | |
---|
334 | # |
---|
335 | # If we got this far, then look through existing results for |
---|
336 | # matching tuples, then check each one for diffs. |
---|
337 | # |
---|
338 | set format "" |
---|
339 | set tuple "" |
---|
340 | foreach col [lrange [$_results column names] 2 end] { |
---|
341 | lappend format $col |
---|
342 | set raw [lindex [Rappture::LibraryObj::value $xmlobj $col] 0] |
---|
343 | lappend tuple $raw ;# use the "raw" (user-readable) label |
---|
344 | } |
---|
345 | if {[llength $format] > 0} { |
---|
346 | set ilist [$_results find -format $format -- $tuple] |
---|
347 | } else { |
---|
348 | set ilist 0 ;# no diffs -- must match first entry |
---|
349 | } |
---|
350 | |
---|
351 | foreach i $ilist { |
---|
352 | set xmlobj0 [$_results get -format xmlobj $i] |
---|
353 | set diffs [$xmlobj0 diff $xmlobj] |
---|
354 | if {[llength $diffs] == 0} { |
---|
355 | # no diffs -- already contained here |
---|
356 | return 1 |
---|
357 | } |
---|
358 | } |
---|
359 | |
---|
360 | # must be some differences |
---|
361 | return 0 |
---|
362 | } |
---|
363 | |
---|
364 | |
---|
365 | # ---------------------------------------------------------------------- |
---|
366 | # USAGE: size |
---|
367 | # |
---|
368 | # Returns the number of results currently stored in the set. |
---|
369 | # ---------------------------------------------------------------------- |
---|
370 | itcl::body Rappture::ResultSet::size {} { |
---|
371 | return [$_results size] |
---|
372 | } |
---|
373 | |
---|
374 | # ---------------------------------------------------------------------- |
---|
375 | # USAGE: notify add <client> ?!event !event ...? <command> |
---|
376 | # USAGE: notify get ?<client>? ?!event? |
---|
377 | # USAGE: notify remove <client> ?!event !event ...? |
---|
378 | # |
---|
379 | # Clients use this to add/remove requests for notifications about |
---|
380 | # various events that signal changes to the data in each ResultSet. |
---|
381 | # |
---|
382 | # The "notify add" operation takes a <client> name (any unique string |
---|
383 | # identifying the client), an optional list of events, and the <command> |
---|
384 | # that should be called for the callback. |
---|
385 | # |
---|
386 | # The "notify get" command returns information about clients and their |
---|
387 | # registered callbacks. With no args, it returns a list of <client> |
---|
388 | # names. If the <client> is specified, it returns a list of !events. |
---|
389 | # If the <client> and !event is specified, it returns the <command>. |
---|
390 | # |
---|
391 | # The "notify remove" command removes any callback associated with |
---|
392 | # a given <client>. If no particular !events are specified, then it |
---|
393 | # removes callbacks for all events. |
---|
394 | # ---------------------------------------------------------------------- |
---|
395 | itcl::body Rappture::ResultSet::notify {option args} { |
---|
396 | set allEvents {!change} |
---|
397 | switch -- $option { |
---|
398 | add { |
---|
399 | if {[llength $args] < 2} { |
---|
400 | error "wrong # args: should be \"notify add caller ?!event !event ...? command" |
---|
401 | } |
---|
402 | set caller [lindex $args 0] |
---|
403 | set command [lindex $args end] |
---|
404 | if {[llength $args] > 2} { |
---|
405 | set events [lrange $args 1 end-1] |
---|
406 | } else { |
---|
407 | set events $allEvents |
---|
408 | } |
---|
409 | |
---|
410 | foreach name $events { |
---|
411 | if {[lsearch -exact $allEvents $name] < 0} { |
---|
412 | error "bad event \"$name\": should be [join $allEvents ,]" |
---|
413 | } |
---|
414 | if {[lsearch $_notify(ALL) $caller] < 0} { |
---|
415 | lappend _notify(ALL) $caller |
---|
416 | } |
---|
417 | set _notify($caller-$name) $command |
---|
418 | } |
---|
419 | } |
---|
420 | get { |
---|
421 | switch -- [llength $args] { |
---|
422 | 0 { |
---|
423 | return $_notify(ALL) |
---|
424 | } |
---|
425 | 1 { |
---|
426 | set caller [lindex $args 0] |
---|
427 | set rlist "" |
---|
428 | foreach key [array names _notify $caller-*] { |
---|
429 | lappend rlist [lindex [split $key -] end] |
---|
430 | } |
---|
431 | return $rlist |
---|
432 | } |
---|
433 | 2 { |
---|
434 | set caller [lindex $args 0] |
---|
435 | set name [lindex $args 1] |
---|
436 | if {[info exists _notify($caller-$name)]} { |
---|
437 | return $_notify($caller-$name) |
---|
438 | } |
---|
439 | return "" |
---|
440 | } |
---|
441 | default { |
---|
442 | error "wrong # args: should be \"notify get ?caller? ?!event?\"" |
---|
443 | } |
---|
444 | } |
---|
445 | } |
---|
446 | remove { |
---|
447 | if {[llength $args] < 1} { |
---|
448 | error "wrong # args: should be \"notify remove caller ?!event !event ...?" |
---|
449 | } |
---|
450 | set caller [lindex $args 0] |
---|
451 | if {[llength $args] > 1} { |
---|
452 | set events [lrange $args 1 end] |
---|
453 | } else { |
---|
454 | set events $allEvents |
---|
455 | } |
---|
456 | |
---|
457 | foreach name $events { |
---|
458 | catch {unset _notify($caller-$name)} |
---|
459 | } |
---|
460 | if {[llength [array names _notify $caller-*]] == 0} { |
---|
461 | set i [lsearch $_notify(ALL) $caller] |
---|
462 | if {$i >= 0} { |
---|
463 | set _notify(ALL) [lreplace $_notify(ALL) $i $i] |
---|
464 | } |
---|
465 | } |
---|
466 | } |
---|
467 | default { |
---|
468 | error "wrong # args: should be add, get, remove" |
---|
469 | } |
---|
470 | } |
---|
471 | } |
---|
472 | |
---|
473 | # ---------------------------------------------------------------------- |
---|
474 | # USAGE: _notifyHandler ?<eventArgs>...? |
---|
475 | # |
---|
476 | # Called automatically whenever a !change event is triggered in this |
---|
477 | # object. Scans through the list of clients that want to receive this |
---|
478 | # event and executes each of their callbacks. |
---|
479 | # ---------------------------------------------------------------------- |
---|
480 | itcl::body Rappture::ResultSet::_notifyHandler {args} { |
---|
481 | array set data $args |
---|
482 | set event $data(event) |
---|
483 | |
---|
484 | foreach caller $_notify(ALL) { |
---|
485 | if {[info exists _notify($caller-$event)]} { |
---|
486 | if {[catch {uplevel #0 $_notify($caller-$event) $args} result]} { |
---|
487 | # anything go wrong? then throw a background error |
---|
488 | bgerror "$result\n(while dispatching $event to $caller)" |
---|
489 | } |
---|
490 | } |
---|
491 | } |
---|
492 | } |
---|
493 | |
---|
494 | # ---------------------------------------------------------------------- |
---|
495 | # USAGE: _addOneResult <tuples> <xmlobj> ?<simNum>? |
---|
496 | # |
---|
497 | # Used internally to add one new <xmlobj> to the given <tuples> |
---|
498 | # object. If the new xmlobj contains different input parameters |
---|
499 | # that are not already columns in the tuple, then this routine |
---|
500 | # creates the new columns. If the optional <simNum> is specified, |
---|
501 | # then it is added as the simulation number #1, #2, #3, etc. If |
---|
502 | # not, then the new object is automatically numbered. |
---|
503 | # ---------------------------------------------------------------------- |
---|
504 | itcl::body Rappture::ResultSet::_addOneResult {tuples xmlobj {simnum ""}} { |
---|
505 | # |
---|
506 | # Compare this new object against the last XML object in the |
---|
507 | # results set. If it has a difference, make sure that there |
---|
508 | # is a column to represent the quantity with the difference. |
---|
509 | # |
---|
510 | set xmlobj0 [$tuples get -format xmlobj end] |
---|
511 | foreach {op vpath oldval newval} [$xmlobj0 diff $xmlobj] { |
---|
512 | if {[$xmlobj get $vpath.about.diffs] == "ignore"} { |
---|
513 | continue |
---|
514 | } |
---|
515 | if {$op == "+" || $op == "-"} { |
---|
516 | # ignore differences where parameters come and go |
---|
517 | # such differences make it hard to work controls |
---|
518 | continue |
---|
519 | } |
---|
520 | |
---|
521 | # make sure that these values really are different |
---|
522 | set oldval [lindex [Rappture::LibraryObj::value $xmlobj0 $vpath] 0] |
---|
523 | set newval [lindex [Rappture::LibraryObj::value $xmlobj $vpath] 0] |
---|
524 | |
---|
525 | if {$oldval != $newval && [$tuples column names $vpath] == ""} { |
---|
526 | # no column for this quantity yet |
---|
527 | $tuples column insert end -name $vpath -default $oldval |
---|
528 | } |
---|
529 | } |
---|
530 | |
---|
531 | # build a tuple for this new object |
---|
532 | set cols "" |
---|
533 | set tuple "" |
---|
534 | foreach col [lrange [$tuples column names] 2 end] { |
---|
535 | lappend cols $col |
---|
536 | set raw [lindex [Rappture::LibraryObj::value $xmlobj $col] 0] |
---|
537 | lappend tuple $raw ;# use the "raw" (user-readable) label |
---|
538 | } |
---|
539 | |
---|
540 | # find a matching tuple? then replace it -- only need one |
---|
541 | if {[llength $cols] > 0} { |
---|
542 | set ilist [$tuples find -format $cols -- $tuple] |
---|
543 | } else { |
---|
544 | set ilist 0 ;# no diffs -- must match first entry |
---|
545 | } |
---|
546 | |
---|
547 | # add all remaining columns for this new entry |
---|
548 | set tuple [linsert $tuple 0 $xmlobj] |
---|
549 | set cols [linsert $cols 0 "xmlobj"] |
---|
550 | |
---|
551 | if {[llength $ilist] > 0} { |
---|
552 | if {[llength $ilist] > 1} { |
---|
553 | error "why so many matching results?" |
---|
554 | } |
---|
555 | |
---|
556 | # overwrite the first matching entry |
---|
557 | # start by freeing the old result |
---|
558 | set index [lindex $ilist 0] |
---|
559 | set xo [$tuples get -format xmlobj $index] |
---|
560 | itcl::delete object $xo |
---|
561 | |
---|
562 | # put this new result in its place |
---|
563 | $tuples put -format $cols $index $tuple |
---|
564 | set simnum [$tuples get -format simnum $index] |
---|
565 | } else { |
---|
566 | if {$simnum eq ""} { |
---|
567 | set simnum "#[incr _resultnum]" |
---|
568 | } |
---|
569 | set tuple [linsert $tuple 1 $simnum] |
---|
570 | $tuples insert end $tuple |
---|
571 | } |
---|
572 | return $simnum |
---|
573 | } |
---|