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
}
60 variable acriteria_values
61 variable last_headers
{}
63 method replace_criteria
{str
{criteria_arraylist
{}}
65 method resolve_row
{row col_list
}
66 method criteria_changed
{}
71 body tlc
::Datasource::constructor {args
} { #<<<1
78 body tlc
::Datasource::destructor {} { #<<<1
79 invoke_handlers destroyed
83 body tlc
::Datasource::replace_criteria {str
{criteria_arraylist
{}} {recursion_level
0}} { #<<<1
84 # TODO: figure out a "proper" way to allow substitutions, and still allow
85 # 1) %var% strign literals that match to pass through
86 # 2) a barrier against infinite recursion
89 if {$criteria_arraylist=={}} {
90 set driving_force
$criteria_values
92 set driving_force
$criteria_arraylist
95 array set valmappings
$criteria_map
96 foreach {idx val
} $driving_force {
97 if {[info exists valmappings
($idx)]} {
98 array set tmp
$valmappings($idx)
99 if {[info exists tmp
($val)]} {
100 lappend map_directives
"%${idx}%" "$tmp($val)"
102 lappend map_directives
"%${idx}%" "$val"
105 lappend map_directives
"%${idx}%" "$val"
108 set ret
[string map
"$map_directives" "$str"]
109 # now, check for recursive re-inclusion of criteria within criteria only
110 foreach {idx val
} $criteria_values {
111 if {[string first
"%$idx%" $ret]>-1} {
112 if {$recursion_level<5} {
113 set ret
[replace_criteria
$ret $driving_force
114 [incr recursion_level
]]
123 body tlc
::Datasource::get_criteria {} { #<<<1
124 # this basically gives the form defination give from the intersection of
125 # the Criteria value and the names of the field_defs array-var
131 body tlc
::Datasource::get_criteria_values {} { #<<<1
132 return $criteria_values
136 body tlc
::Datasource::set_criteria {arraylist
} { #<<<1
137 # sets the criteria (and acriteria) from an array-style list. the list
138 # element style is: {criteria_label} {criteria_varname {form style}}
140 set criteria
$arraylist
144 body tlc
::Datasource::set_criteria_values {arraylist
} { #<<<1
145 # sets the replacement values for the criteria tokenlist from an
146 # array-style list the list element style is:
147 # {criteria_varname} {variable_value}
149 set criteria_values
$arraylist
153 body tlc
::Datasource::set_field_defs {arraylist
} { #<<<1
154 set field_defs
$arraylist
157 body tlc
::Datasource::get_field_defs {} { #<<<1
161 body tlc
::Datasource::resolve_row {row col_list
} { #<<<1
162 # resolves a list of values (row) and a column name list (col_list) into an
164 # inputs: row (raw data list); col_list: column names for items in row
165 # returns: array-style list of the style: {col_name} {col_value}
167 foreach col
$col_list val
$row {
168 lappend ret
$col $val
175 body tlc
::Datasource::get_labelled_list {criteria
{headersvar
{}}} { #<<<1
176 # does the same as a get_list, but each row contains interleaved
177 # header-names with each field; makes the loading of an array or a treeview
178 # structure a lot easier for the client
180 if {$headersvar != {}} {
181 upvar $headersvar headers
183 set rawlist
[get_list
$criteria headers
]
185 foreach rawrow
$rawlist {
187 foreach rawcol
$rawrow head
$headers {
188 lappend lrow
$head $rawcol
193 # puts "\n\nDatasource::get_labelled_list: returning: ($llist)"
198 body tlc
::Datasource::get_list {criteria
{headersvar
{}}} { #<<<1
202 body tlc
::Datasource::get_id_column {} { #<<<1
203 return [list $id_column [lindex $last_headers $id_column]]
207 body tlc
::Datasource::add_item {row
{col_list
{}}} { #<<<1
211 body tlc
::Datasource::update_item {oldrow newrow
{old_col_list
""} {new_col_list
""}} { #<<<1
215 body tlc
::Datasource::remove_item {row
{col_list
""}} { #<<<1
219 body tlc
::Datasource::extract_id {row
} { #<<<1
220 return [lindex $row $id_column]
224 body tlc
::Datasource::criteria_changed {} { #<<<1
228 body tlc
::Datasource::set_criteria_map {mapping
} { #<<<1
229 set criteria_map
$mapping
233 body tlc
::Datasource::get_item_schema {} { #<<<1
237 body tlc
::Datasource::set_defaults {rowarray
} { #<<<1
238 set defaults
$rowarray
241 body tlc
::Datasource::get_defaults {} { #<<<1
245 body tlc
::Datasource::get_full_row {id
} { #<<<1
246 # purpose: to return all fields defined in the filed definitions for the id specified -- to be used
247 # by a client who will be doing an update later
248 # returns: array-style list of {col} {val} {col} {val} ...
249 # this is to be implemented by the client
252 body tlc
::Datasource::can_do {action args
} { #<<<1
253 if {[llength $args] == 0} {
254 return [expr {[info exists can_do
($action)] && $can_do($action)}]
255 } elseif
{[llength $args] == 1} {
256 set can_do
($action) [expr {[lindex $args 0]}]
258 error "Wrong # of args: must be action ?newvalue?"
263 body tlc
::Datasource::lookup {key match
{mode
-exact}} { #<<<1
270 error "Invalid match mode: \"$mode\", must be one of -exact, -glob or -regexp" "" [list bad_match_mode
$mode]
274 set rows
[get_list
{} headers
]
276 set idx
[lsearch $headers $key]
278 error "Invalid key: \"$key\", must be one of \"[join $headers {", "}]\""
281 if {[package vsatisfies
$::tcl_version 8.5]} {
282 set matches
[lsearch -all -inline $mode -index $idx $rows $match]
286 switch $mode -- [lindex $row $idx] $match {
295 foreach row
$matches {
297 foreach h
$headers v
$row {
307 body tlc
::Datasource::slice {column args
} { #<<<1
308 # slice returns all instances of a column in the datasource, sorted
311 set raw
[get_list
{} headers
]
313 # Check that the specified column is valid <<<
314 if {[lsearch $headers $column] == -1} {
315 error "Invalid slice column \"$column\", should be one of ([join $headers {, }])" "" \
316 [list invalid_column
$column]
318 # Check that the specified column is valid >>>
321 set sortcolumn
$column
322 set sortmode
"dictionary"
323 set sortdir increasing
326 while {[llength $remaining] > 0} {
327 set option [lindex $remaining 0]
328 set remaining
[lrange $remaining 1 end
]
330 if {[string index
$option 0] != "-"} {
331 error "Expecting an option, got \"$option\"" "" [list syntax_error
]
335 -orderby - -sort { #<<<
336 set sortcolumn
[lindex $remaining 0]
337 set remaining
[lrange $remaining 1 end
]
339 if {[lsearch $headers $sortcolumn] == -1} {
340 error "Specified sort column ($sortcolumn) doesn't exist. Should be one of ([join $headers {, }])" "" \
341 [list invalid_sortcolumn
$sortcolumn $headers]
344 if {[string index
[lindex $remaining 0] 0] != "-"} {
345 set sortdir
[lindex $remaining 0]
346 set remaining
[lrange $remaining 1 end
]
349 asc
- ascending
- increasing
{
350 set sortdir increasing
353 desc
- descending
- decreasing
{
354 set sortdir decreasing
358 error "Invalid sortdir specified: ($sortdir)" "" \
359 [list invalid_sortdir
$sortdir]
367 set sortmode
[lindex $remaining 0]
368 set remaining
[lrange $sortmode 1 end
]
370 switch -- $sortmode {
371 ascii
- dictionary
- integer
- real
{}
374 error "Invalid sortmode: \"$sortmode\"" "" \
375 [list invalid_sortmode
$sortmode]
382 error "Invalid option \"$option\"" "" \
383 [list invalid_option
$option]
389 set sort_col_idx
[lsearch $headers $sortcolumn]
390 set slice_col_idx
[lsearch $headers $column]
393 foreach row
[lsort -$sortmode -$sortdir -index $sort_col_idx $raw] {
394 lappend build
[lindex $row $slice_col_idx]