1 # vim: foldmethod=marker foldmarker=<<<,>>> ft=tcl ts=4 shiftwidth=4
3 # Datasource_filter is a datasource stacked on top of another datasource.
4 # Items can be filtered to a subset of the source datasource by means of
5 # filter expressions that each row must pass to be included in our filtered
7 # Items may also be translated (and have the header list changed) by
8 # a translator that given an input row, produces the output row.
13 # id_column_changed(new_id_column)
14 # headers_changed(new_headerlist)
15 # new_item(pool, id, newitem)
16 # change_item(pool, id, olditem, newitem)
17 # remove_item(pool, id, olditem)
21 class tlc
::Datasource_filter {
22 inherit tlc
::Datasource tlc
::Baselog
28 variable ds
"" need_refilter
29 variable override_headers
{} need_refilter
30 variable filter
{true
} need_refilter
31 variable translator
{} need_refilter
32 variable id_column
"" ;# column to use as the ID column
33 variable cache
0 ;# 1 = Cache the filtered items, 0 = do jit
36 method get_list
{criteria
{headersvar
{}}}
37 method get_list_extended
{criteria
{headersvar
{}}}
39 method pool_meta
{pool
}
43 variable link_id_column
1
44 variable damp_onchange
1
47 method need_refilter
{}
51 method id_column_changed
{new_id_column
}
52 method headers_changed
{new_headerlist
}
53 method new_item
{pool id newitem
}
54 method change_item
{pool id olditem newitem
}
55 method remove_item
{pool id olditem
}
56 method new_pool
{pool
}
57 method remove_pool
{pool
}
62 configbody tlc
::Datasource_filter::id_column { #<<<1
63 if {$id_column == ""} {
65 if {[info exists ds
] && [itcl
::is object
$ds] && [$ds isa tlc
::Datasource]} {
66 set id_column
[$ds cget
-id_column]
74 configbody tlc
::Datasource_filter::ds { #<<<1
75 if {![itcl
::is object
$ds] ||
!([$ds isa tlc
::DSchan] ||
[$ds isa tlc
::Datasource_filter])} {
76 if {![itcl
::is object
$ds]} {
77 log
error "-ds ($ds) is not an object"
78 } elseif
{!([$ds isa tlc
::DSchan] ||
[$ds isa tlc
::Datasource_filter])} {
79 log
error "-ds ($ds) is not a tlc::DSchan or tlc::Datasource_filter: ([$ds info heritage]))"
81 log
error "non-specific -ds ($ds) problem"
83 error "Only tlc::DSchan and its subclasses are allowed for -ds" "" \
87 if {$link_id_column} {
88 set id_column
[$ds cget
-id_column]
93 configbody tlc
::Datasource_filter::cache { #<<<1
95 error "Caching not implemented yet" "" [list caching_not_supported
]
100 configbody tlc
::Datasource_filter::filter { #<<<1
101 if {[string trim
$filter] == ""} {
102 error "-filter cannot be blank" "" [list invalid_filter
$filter]
104 $dominos(need_refilter
) tip
108 body tlc
::Datasource_filter::constructor {args
} { #<<<1
112 tlc
::Domino #auto dominos(need_refilter) -name "$this need_refilter"
117 if {![info exists
$reqf]} {
118 error "Must set -$reqf" "" [list missing_field
$reqf]
123 error "Must set -ds" "" [list missing_field ds
]
126 $dominos(need_refilter
) attach_output
[code
$this refilter
]
128 $ds register_handler init
[code
$this init
]
129 $ds register_handler onchange
[code
$this onchange
]
130 $ds register_handler id_column_changed
[code
$this id_column_changed
]
131 $ds register_handler headers_changed
[code
$this headers_changed
]
132 $ds register_handler new_item
[code
$this new_item
]
133 $ds register_handler change_item
[code
$this change_item
]
134 $ds register_handler remove_item
[code
$this remove_item
]
135 $ds register_handler new_pool
[code
$this new_pool
]
136 $ds register_handler remove_pool
[code
$this remove_pool
]
140 body tlc
::Datasource_filter::destructor {} { #<<<1
143 $dominos(need_refilter
) detach_output
[code
$this refilter
]
145 $ds deregister_handler init
[code
$this init
]
146 $ds deregister_handler onchange
[code
$this onchange
]
147 $ds deregister_handler id_column_changed
[code
$this id_column_changed
]
148 $ds deregister_handler headers_changed
[code
$this headers_changed
]
149 $ds deregister_handler new_item
[code
$this new_item
]
150 $ds deregister_handler change_item
[code
$this change_item
]
151 $ds deregister_handler remove_item
[code
$this remove_item
]
152 $ds deregister_handler new_pool
[code
$this new_pool
]
153 $ds deregister_handler remove_pool
[code
$this remove_pool
]
157 body tlc
::Datasource_filter::get_list {criteria
{headersvar
{}}} { #<<<1
159 if {$headersvar != {}} {
160 upvar $headersvar hdrs
165 set pool_data
[lindex [get_list_extended
$criteria hdrs
] 1]
166 #log debug "got pool_data: ($pool_data)"
169 foreach {pool data
} $pool_data {
170 #log debug "Appending data from pool ($pool): ($data)"
174 set flatlist
[eval concat $list]
175 log debug
"returning [llength $flatlist] items, sorted by id column $id_column"
176 return [lsort -unique -index $id_column $flatlist]
180 body tlc
::Datasource_filter::get_list_extended {criteria
{headersvar
{}}} { #<<<1
182 if {$headersvar != {}} {
183 upvar $headersvar hdrs
186 set raw
[$ds get_list_extended
$criteria hdrs
]
187 set pool_meta_arr
[lindex $raw 0]
188 set pool_data
[lindex $raw 1]
191 array set pool_meta
$pool_meta_arr
194 error "Cannot parse pool_meta: ($pool_meta_arr): $errmsg" "" \
195 [list pool_meta_syntax_error
$errmsg]
199 set use_trans
[expr {$translator != {}}]
201 if {$override_headers != {}} {
202 set outhdrs
$override_headers
207 foreach {pool data
} $pool_data {
209 array set meta
$pool_meta($pool)
214 foreach f
$r h
$hdrs {
220 if {![expr $filter]} {
221 log debug
"Row ([array get row]) fails filter ($filter)"
224 log debug
"Row ([array get row]) passes filter ($filter)"
227 if {![expr $filter]} continue
231 error "Error applying filter ($filter): $errmsg" "" \
232 [list filter_error
$errmsg]
236 if {$use_trans} $translator
242 log debug
"Assembling output row field ($h): ($row($h))"
244 lappend outrow
$row($h)
246 lappend new_pool_data
$outrow
248 lappend new_pool_data
$r
252 lappend build
$pool $new_pool_data
254 if {$override_headers != {}} {
255 set hdrs
$override_headers
257 set last_headers
$hdrs
260 log debug
"Returning pool_meta_arr: ($pool_meta_arr), data: ([llength $build] items)"
262 return [list $pool_meta_arr $build]
266 body tlc
::Datasource_filter::need_refilter {} { #<<<1
267 $dominos(need_refilter
) tip
271 body tlc
::Datasource_filter::refilter {} { #<<<1
272 #invoke_handlers init
273 invoke_handlers onchange
277 body tlc
::Datasource_filter::init {} { #<<<1
278 invoke_handlers onchange
283 body tlc
::Datasource_filter::onchange {} { #<<<1
284 if {!($damp_onchange)} {
285 invoke_handlers onchange
291 body tlc
::Datasource_filter::id_column_changed {new_id_column
} { #<<<1
292 if {$link_id_column} {
293 set id_column
$new_id_column
295 invoke_handlers id_column_changed
$id_column
300 body tlc
::Datasource_filter::headers_changed {new_headerlist
} { #<<<1
301 if {$override_headers == {}} {
302 set last_headers
$new_headerlist
304 invoke_handlers headers_changed
$new_headerlist
309 body tlc
::Datasource_filter::new_item {pool id newitem
} { #<<<1
310 set hdrs
[$ds get_headers
]
311 array set meta
[$ds pool_meta
$pool]
313 foreach f
$newitem h
$hdrs {
316 #log debug "new row:"
319 #log debug "testing against filter: ($filter)"
321 if {![expr $filter]} return
324 error "Error applying filter ($filter): $errmsg" "" \
325 [list filter_error
$errmsg]
328 #log debug "survives filter"
330 set use_trans
[expr {$translator != {}}]
332 if {$use_trans} $translator
333 #log debug "after translator:"
338 foreach h
$last_headers {
339 lappend outrow
$row($h)
341 set id
[lindex $outrow $id_column]
345 #log debug "id: ($id) outrow: ($outrow)"
348 invoke_handlers new_item
$pool $id $outrow
352 body tlc
::Datasource_filter::change_item {pool id olditem newitem
} { #<<<1
353 set hdrs
[$ds get_headers
]
354 array set meta
[$ds pool_meta
$pool]
356 # Process olditem <<<
358 foreach f
$olditem h
$hdrs {
362 set use_trans
[expr {$translator != {}}]
364 set old_visible
[expr $filter]
367 if {$use_trans} $translator
371 foreach h
$last_headers {
372 lappend old_outrow
$row($h)
375 set old_outrow
$olditem
377 set old_id
[lindex $old_outrow $id_column]
379 # Process olditem >>>
380 # Process newitem <<<
382 foreach f
$newitem h
$hdrs {
386 set new_visible
[expr $filter]
389 if {$use_trans} $translator
393 foreach h
$last_headers {
394 lappend new_outrow
$row($h)
397 set new_outrow
$newitem
399 set new_id
[lindex $new_outrow $id_column]
401 # Process newitem >>>
404 switch -- $old_visible,$new_visible {
405 0,0 {set damp_onchange
1}
406 1,0 {invoke_handlers remove_item
$pool $old_id $old_outrow}
407 0,1 {invoke_handlers new_item
$pool $new_id $new_outrow}
408 1,1 {invoke_handlers change_item
$pool $old_id $old_outrow $new_outrow}
413 body tlc
::Datasource_filter::remove_item {pool id olditem
} { #<<<1
414 set hdrs
[$ds get_headers
]
415 array set meta
[$ds pool_meta
$pool]
418 foreach f
$olditem h
$hdrs {
423 if {![expr $filter]} return
426 error "Error applying filter ($filter): $errmsg" "" \
427 [list filter_error
$errmsg]
431 set use_trans
[expr {$translator != {}}]
433 if {$use_trans} $translator
437 foreach h
$last_headers {
438 lappend old_outrow
$row($h)
441 set old_outrow
$olditem
443 set old_id
[lindex $old_outrow $id_column]
446 invoke_handlers remove_item
$pool $old_id $old_outrow
450 body tlc
::Datasource_filter::new_pool {pool
} { #<<<1
451 $dominos(need_refilter
) tip
453 invoke_handlers new_pool
$pool
457 body tlc
::Datasource_filter::remove_pool {pool
} { #<<<1
458 $dominos(need_refilter
) tip
460 invoke_handlers remove_pool
$pool
464 body tlc
::Datasource_filter::get_headers {} { #<<<1
465 if {$override_headers != {}} {
466 set outhdrs
$override_headers
468 set outhdrs
[$ds get_headers
]
474 body tlc
::Datasource_filter::pool_meta {pool
} { #<<<1
475 return [$ds pool_meta
$pool]