1 # vim: ft=tcl foldmarker=<<<,>>>
3 # item_schema example: <<<
4 # variable item_schema {
6 # "Table" {table combobox \
7 # -choices {sql_features sql_languages sql_packages} \
8 # -initial_choice sql_languages
11 # "Has Indexes" {indexed checkbutton}
12 # "Has Rules" {ruled checkbutton}
13 # "Has Triggers" {hastriggers checkbutton}
17 class tlc
::Datasource {
25 variable criteria_values
""
27 variable id_column
0 ;# column to use as the ID column
28 variable criteria_map
{}
30 variable item_schema
{}
32 method get_item_schema
{}
33 method set_criteria_map
{mapping
}
34 method get_criteria
{}
35 method set_criteria
{arraylist
}
36 method get_criteria_values
{}
37 method set_criteria_values
{arraylist
}
38 method set_field_defs
{arraylist
}
39 method get_field_defs
{}
40 method set_defaults
{rowarray
}
41 method get_defaults
{}
43 method get_list
{criteria
{headersvar
{}}}
44 method get_labelled_list
{criteria
{headersvar
{}}}
45 method get_id_column
{}
46 method add_item
{row
{col_list
""}}
47 method update_item
{oldrow newrow
{old_col_list
""} {new_col_list
""}}
48 method remove_item
{row
{col_list
""}}
49 method extract_id
{row
}
50 method get_full_row
{id
}
51 method can_do
{action args
}
53 method lookup
{key match
{mode
-exact}}
54 method slice
{column args
}
55 method build_map
{from_column args
}
61 variable acriteria_values
62 variable last_headers
{}
64 method replace_criteria
{str
{criteria_arraylist
{}}
66 method resolve_row
{row col_list
}
67 method criteria_changed
{}
72 body tlc
::Datasource::constructor {args
} { #<<<1
79 body tlc
::Datasource::destructor {} { #<<<1
80 invoke_handlers destroyed
84 body tlc
::Datasource::replace_criteria {str
{criteria_arraylist
{}} {recursion_level
0}} { #<<<1
85 # TODO: figure out a "proper" way to allow substitutions, and still allow
86 # 1) %var% strign literals that match to pass through
87 # 2) a barrier against infinite recursion
90 if {$criteria_arraylist eq
{}} {
91 set driving_force
$criteria_values
93 set driving_force
$criteria_arraylist
96 array set valmappings
$criteria_map
97 foreach {idx val
} $driving_force {
98 if {[info exists valmappings
($idx)]} {
99 array set tmp
$valmappings($idx)
100 if {[info exists tmp
($val)]} {
101 lappend map_directives
"%${idx}%" "$tmp($val)"
103 lappend map_directives
"%${idx}%" "$val"
106 lappend map_directives
"%${idx}%" "$val"
109 set ret
[string map
"$map_directives" "$str"]
110 # now, check for recursive re-inclusion of criteria within criteria only
111 foreach {idx val
} $criteria_values {
112 if {[string first
"%$idx%" $ret]>-1} {
113 if {$recursion_level<5} {
114 set ret
[replace_criteria
$ret $driving_force
115 [incr recursion_level
]]
124 body tlc
::Datasource::get_criteria {} { #<<<1
125 # this basically gives the form defination give from the intersection of
126 # the Criteria value and the names of the field_defs array-var
132 body tlc
::Datasource::get_criteria_values {} { #<<<1
133 return $criteria_values
137 body tlc
::Datasource::set_criteria {arraylist
} { #<<<1
138 # sets the criteria (and acriteria) from an array-style list. the list
139 # element style is: {criteria_label} {criteria_varname {form style}}
141 set criteria
$arraylist
145 body tlc
::Datasource::set_criteria_values {arraylist
} { #<<<1
146 # sets the replacement values for the criteria tokenlist from an
147 # array-style list the list element style is:
148 # {criteria_varname} {variable_value}
150 set criteria_values
$arraylist
154 body tlc
::Datasource::set_field_defs {arraylist
} { #<<<1
155 set field_defs
$arraylist
158 body tlc
::Datasource::get_field_defs {} { #<<<1
162 body tlc
::Datasource::resolve_row {row col_list
} { #<<<1
163 # resolves a list of values (row) and a column name list (col_list) into an
165 # inputs: row (raw data list); col_list: column names for items in row
166 # returns: array-style list of the style: {col_name} {col_value}
168 foreach col
$col_list val
$row {
169 lappend ret
$col $val
176 body tlc
::Datasource::get_labelled_list {criteria
{headersvar
{}}} { #<<<1
177 # does the same as a get_list, but each row contains interleaved
178 # header-names with each field; makes the loading of an array or a treeview
179 # structure a lot easier for the client
181 if {$headersvar != {}} {
182 upvar $headersvar headers
184 set rawlist
[get_list
$criteria headers
]
186 foreach rawrow
$rawlist {
188 foreach rawcol
$rawrow head
$headers {
189 lappend lrow
$head $rawcol
194 # puts "\n\nDatasource::get_labelled_list: returning: ($llist)"
199 body tlc
::Datasource::get_list {criteria
{headersvar
{}}} { #<<<1
203 body tlc
::Datasource::get_id_column {} { #<<<1
204 return [list $id_column [lindex $last_headers $id_column]]
208 body tlc
::Datasource::add_item {row
{col_list
{}}} { #<<<1
212 body tlc
::Datasource::update_item {oldrow newrow
{old_col_list
""} {new_col_list
""}} { #<<<1
216 body tlc
::Datasource::remove_item {row
{col_list
""}} { #<<<1
220 body tlc
::Datasource::extract_id {row
} { #<<<1
221 return [lindex $row $id_column]
225 body tlc
::Datasource::criteria_changed {} { #<<<1
229 body tlc
::Datasource::set_criteria_map {mapping
} { #<<<1
230 set criteria_map
$mapping
234 body tlc
::Datasource::get_item_schema {} { #<<<1
238 body tlc
::Datasource::set_defaults {rowarray
} { #<<<1
239 set defaults
$rowarray
242 body tlc
::Datasource::get_defaults {} { #<<<1
246 body tlc
::Datasource::get_full_row {id
} { #<<<1
247 # purpose: to return all fields defined in the filed definitions for the id specified -- to be used
248 # by a client who will be doing an update later
249 # returns: array-style list of {col} {val} {col} {val} ...
250 # this is to be implemented by the client
253 body tlc
::Datasource::can_do {action args
} { #<<<1
254 if {[llength $args] == 0} {
255 return [expr {[info exists can_do
($action)] && $can_do($action)}]
256 } elseif
{[llength $args] == 1} {
257 set can_do
($action) [expr {[lindex $args 0]}]
259 error "Wrong # of args: must be action ?newvalue?"
264 body tlc
::Datasource::lookup {key match
{mode
-exact}} { #<<<1
271 error "Invalid match mode: \"$mode\", must be one of -exact, -glob or -regexp" "" [list bad_match_mode
$mode]
275 set rows
[get_list
{} headers
]
277 set idx
[lsearch $headers $key]
279 error "Invalid key: \"$key\", must be one of \"[join $headers {", "}]\""
282 if {[package vsatisfies
$::tcl_version 8.5]} {
283 set matches
[lsearch -all -inline $mode -index $idx $rows $match]
287 switch $mode -- [lindex $row $idx] $match {
296 foreach row
$matches {
298 foreach h
$headers v
$row {
308 body tlc
::Datasource::slice {column args
} { #<<<1
309 # slice returns all instances of a column in the datasource, sorted
312 set raw
[get_list
{} headers
]
314 # Check that the specified column is valid <<<
315 if {[lsearch $headers $column] == -1} {
316 error "Invalid slice column \"$column\", should be one of ([join $headers {, }])" "" \
317 [list invalid_column
$column]
319 # Check that the specified column is valid >>>
322 set sortcolumn
$column
323 set sortmode
"dictionary"
324 set sortdir increasing
327 while {[llength $remaining] > 0} {
328 set option [lindex $remaining 0]
329 set remaining
[lrange $remaining 1 end
]
331 if {[string index
$option 0] != "-"} {
332 error "Expecting an option, got \"$option\"" "" [list syntax_error
]
336 -orderby - -sort { #<<<
337 set sortcolumn
[lindex $remaining 0]
338 set remaining
[lrange $remaining 1 end
]
340 if {[lsearch $headers $sortcolumn] == -1} {
341 error "Specified sort column ($sortcolumn) doesn't exist. Should be one of ([join $headers {, }])" "" \
342 [list invalid_sortcolumn
$sortcolumn $headers]
345 if {[string index
[lindex $remaining 0] 0] != "-"} {
346 set sortdir
[lindex $remaining 0]
347 set remaining
[lrange $remaining 1 end
]
350 asc
- ascending
- increasing
{
351 set sortdir increasing
354 desc
- descending
- decreasing
{
355 set sortdir decreasing
359 error "Invalid sortdir specified: ($sortdir)" "" \
360 [list invalid_sortdir
$sortdir]
368 set sortmode
[lindex $remaining 0]
369 set remaining
[lrange $sortmode 1 end
]
371 switch -- $sortmode {
372 ascii
- dictionary
- integer
- real
{}
375 error "Invalid sortmode: \"$sortmode\"" "" \
376 [list invalid_sortmode
$sortmode]
383 error "Invalid option \"$option\"" "" \
384 [list invalid_option
$option]
390 set sort_col_idx
[lsearch $headers $sortcolumn]
391 set slice_col_idx
[lsearch $headers $column]
394 foreach row
[lsort -$sortmode -$sortdir -index $sort_col_idx $raw] {
395 lappend build
[lindex $row $slice_col_idx]
402 body tlc
::Datasource::build_map {from_column args
} { #<<<1
403 set rows
[get_list
{} headers
]
405 switch -- [llength $args] {
407 error "Must specify a target column format"
412 set to_column_list
[list [lindex $args 0]]
416 set to_column_list
[lassign
$args format]
419 set from_idx
[lsearch $headers $from_column]
420 if {$from_idx == -1} {
421 error "From column \"$from_column\" doesn't exist"
424 foreach col
$to_column_list {
425 set to_idx
[lsearch $headers $col]
427 error "To column \"$col\" doesn't exist"
429 lappend to_idx_list
$to_idx
432 set res
[dict create
]
435 foreach idx
$to_idx_list {
436 lappend to_cols
[lindex $row $idx]
439 [lindex $row $from_idx] \
440 [format $format {*}$to_cols]