report.tcl

Go to the documentation of this file.
00001 /*  report.tcl --*/
00002 /* */
00003 /*  Implementation of report objects for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 2001 by Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00006 /* */
00007 /*  See the file "license.terms" for information on usage and redistribution*/
00008 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00009 /*  */
00010 /*  RCS: @(#) $Id: report.tcl,v 1.8 2004/01/15 06:36:13 andreas_kupries Exp $*/
00011 
00012 package require Tcl 8.2
00013 package provide report 0.3.1
00014 
00015 namespace ::report {
00016     /*  Data storage in the report module*/
00017     /*  -------------------------------*/
00018     /* */
00019     /*  One namespace per object, containing*/
00020     /*   1) An array mapping from template codes to templates*/
00021     /*   2) An array mapping from template codes and columns to horizontal template items*/
00022     /*   3) An array mapping from template codes and columns to vertical template items*/
00023     /*   4) ... deleted, local to formatting*/
00024     /*   5) An array mapping from columns to left padding*/
00025     /*   6) An array mapping from columns to right padding*/
00026     /*   7) An array mapping from columns to column size*/
00027     /*   8) An array mapping from columns to justification*/
00028     /*   9) A scalar containing the number of columns in the report.*/
00029     /*  10) An array mapping from template codes to enabledness*/
00030     /*  11) A scalar containing the size of the top caption*/
00031     /*  12) A scalar containing the size of the bottom caption*/
00032     /* */
00033     /*  1 - template        5 - lpad     9 - columns*/
00034     /*  2 - hTemplate       6 - rpad    10 - enabled*/
00035     /*  3 - vTemplate       7 - csize   11 - tcaption*/
00036     /*  4 - fullHTemplate       8 - cjust   12 - bcaption*/
00037 
00038     /*  commands is the list of subcommands recognized by the report*/
00039     variable commands [list     \
00040         "bcaption"          \
00041         "botcapsep"         \
00042         "botdata"           \
00043         "botdatasep"        \
00044         "bottom"            \
00045         "columns"           \
00046         "data"          \
00047         "datasep"           \
00048         "justify"           \
00049         "pad"           \
00050         "printmatrix"       \
00051         "printmatrix2channel"   \
00052         "size"          \
00053         "sizes"         \
00054         "tcaption"          \
00055         "top"           \
00056         "topcapsep"         \
00057         "topdata"           \
00058         "topdatasep"
00059         ]
00060 
00061     /*  Only export the toplevel commands*/
00062     namespace export report defstyle rmstyle stylearguments stylebody
00063 
00064     /*  Global data, style definitions*/
00065 
00066     variable styles [list plain]
00067     variable styleargs
00068     variable stylebody
00069 
00070     array  styleargs =  {plain {}}
00071     array  stylebody =  {plain {}}
00072 
00073     /*  Global data, template codes, for easy checking*/
00074 
00075     variable  tcode
00076     array  tcode =  {
00077     topdata    0    data       0
00078     botdata    0    top        1
00079     topdatasep 1    topcapsep  1
00080     datasep    1    botcapsep  1
00081     botdatasep 1    bottom     1
00082     }
00083 }
00084 
00085 /*  ::report::report --*/
00086 /* */
00087 /*  Create a new report with a given name*/
00088 /* */
00089 /*  Arguments:*/
00090 /*  name    Optional name of the report; if null or not given, generate one.*/
00091 /* */
00092 /*  Results:*/
00093 /*  name    Name of the report created*/
00094 
00095 ret  ::report::report (type name , type columns , type args) {
00096     variable styleargs
00097 
00098     if { [llength [info commands ::$name]] } {
00099     error "command \"$name\" already exists, unable to create report"
00100     }
00101     if {![string is integer $columns]} {
00102     return -code error "columns: expected integer greater than zero, got \"$columns\""
00103     } elseif {$columns <= 0} {
00104     return -code error "columns: expected integer greater than zero, got \"$columns\""
00105     }
00106 
00107     set styleName ""
00108     switch -exact -- [llength $args] {
00109     0 {# No style was specied. This is OK}
00110     1 {
00111         # We possibly got the "style" keyword, but everything behind is missing
00112         return -code error "wrong # args: report name columns ?\"style\" styleName ?arg...??"
00113     }
00114     default {
00115         # Break tail apart, check for correct keyword, ensure that style is known too.
00116         # Don't forget to check the actual against the formal arguments.
00117 
00118         foreach {dummy styleName} $args break
00119         set args [lrange $args 2 end]
00120 
00121         if {![string equal $dummy style]} {
00122         return -code error "wrong # args: report name columns ?\"style\" styleName ?arg...??"
00123         }
00124         if {![info exists styleargs($styleName)]} {
00125         return -code error "style \"$styleName\" is not known"
00126         }
00127         CheckStyleArguments $styleName $args
00128     }
00129     }
00130 
00131     # The arguments seem to be ok, setup the namespace for the object
00132     # and configure it to style "plain".
00133 
00134     namespace eval ::report::report$name "variable columns $columns"
00135     namespace eval ::report::report$name {
00136     variable tcaption 0
00137     variable bcaption 0
00138     variable template
00139     variable enabled
00140     variable hTemplate
00141     variable vTemplate
00142     variable lpad
00143     variable rpad
00144     variable csize
00145     variable cjust
00146 
00147     variable t
00148     variable i
00149     variable dt [list]
00150     variable st [list]
00151     for {set i 0} {$i < $columns} {incr i} {
00152         set lpad($i) ""
00153         set rpad($i) ""
00154         set csize($i) dyn
00155         set cjust($i) left
00156         lappend dt {}
00157         lappend st {} {}
00158     }
00159     lappend dt {}
00160     lappend st {}
00161 
00162     foreach t {
00163         topdata data botdata
00164     } {
00165         set enabled($t) 1
00166         set template($t) $dt
00167         for {set i 0} {$i <= $columns} {incr i} {
00168         set vTemplate($t,$i) {}
00169         }
00170     }
00171     foreach t {
00172         top topdatasep topcapsep
00173         datasep
00174         botcapsep botdatasep bottom
00175     } {
00176         set enabled($t) 0
00177         set template($t) $st
00178         for {set i 0} {$i < $columns} {incr i} {
00179         set hTemplate($t,$i) {}
00180         }
00181         for {set i 0} {$i <= $columns} {incr i} {
00182         set vTemplate($t,$i) {}
00183         }
00184     }
00185 
00186     unset t i dt st
00187     }
00188 
00189     # Create the command to manipulate the report
00190     #                 $name -> ::report::ReportProc $name
00191     interp alias {} ::$name {} ::report::ReportProc $name
00192 
00193     # If a style was specified execute it now, before the oobject is
00194     # handed back to the user.
00195 
00196     if {$styleName != {}} {
00197     ExecuteStyle $name $styleName $args
00198     }
00199 
00200     return $name
00201 }
00202 
00203 /*  ::report::defstyle --*/
00204 /* */
00205 /*  Defines a new named style, with arguments and defining script.*/
00206 /* */
00207 /*  Arguments:*/
00208 /*  styleName   Name of the new style.*/
00209 /*  arguments   Formal arguments of the style, some format as for proc.*/
00210 /*  body        The script actually defining the style.*/
00211 /* */
00212 /*  Results:*/
00213 /*  None.*/
00214 
00215 ret  ::report::defstyle (type styleName , type arguments , type body) {
00216     variable styleargs
00217     variable stylebody
00218     variable styles
00219 
00220     if {[info exists styleargs($styleName)]} {
00221     return -code error "Cannot create style \"$styleName\", already exists"
00222     }
00223 
00224     # Check the formal arguments
00225     # 1. Arguments without default may not follow an argument with a
00226     #    default. The special "args" is no exception!
00227     # 2. Compute the minimal number of arguments required by the proc.
00228 
00229     set min 0
00230     set def 0
00231     set ca  0
00232 
00233     foreach v $arguments {
00234     switch -- [llength $v] {
00235         1 {
00236         if {$def} {
00237             return -code error \
00238                 "Found argument without default after arguments having defaults"
00239         }
00240         incr min
00241         }
00242         2 {
00243         set def 1
00244         }
00245         default {
00246         error "Illegal length of value \"$v\""
00247         }
00248     }
00249     }
00250     if {[string equal args [lindex $arguments end]]} {
00251     # Correct requirements if we have a catch-all at the end.
00252     incr min -1
00253     set  ca 1
00254     }
00255 
00256     # Now we are allowed to extend the internal database
00257 
00258     set styleargs($styleName) [list $min $ca $arguments]
00259     set stylebody($styleName) $body
00260     lappend styles $styleName
00261     return
00262 }
00263 
00264 /*  ::report::rmstyle --*/
00265 /* */
00266 /*  Deletes the specified style.*/
00267 /* */
00268 /*  Arguments:*/
00269 /*  styleName   Name of the style to destroy.*/
00270 /* */
00271 /*  Results:*/
00272 /*  None.*/
00273 
00274 ret  ::report::rmstyle (type styleName) {
00275     variable styleargs
00276     variable stylebody
00277     variable styles
00278 
00279     if {![info exists styleargs($styleName)]} {
00280     return -code error "cannot delete unknown style \"$styleName\""
00281     }
00282     if {[string equal $styleName plain]} {
00283     return -code error {cannot delete builtin style "plain"}
00284     }
00285 
00286     unset styleargs($styleName)
00287     unset stylebody($styleName)
00288 
00289     set pos    [lsearch -exact $styles $styleName]
00290     set styles [lreplace $styles $pos $pos]
00291     return
00292 }
00293 
00294 /*  ::report::_stylearguments --*/
00295 /* */
00296 /*  Introspection, returns the list of formal arguments of the*/
00297 /*  specified style.*/
00298 /* */
00299 /*  Arguments:*/
00300 /*  styleName   Name of the style to query.*/
00301 /* */
00302 /*  Results:*/
00303 /*  A list containing the formal argument of the style*/
00304 
00305 ret  ::report::stylearguments (type styleName) {
00306     variable styleargs
00307     if {![info exists styleargs($styleName)]} {
00308     return -code error "style \"$styleName\" is not known"
00309     }
00310     return [lindex $styleargs($styleName) 2]
00311 }
00312 
00313 /*  ::report::_stylebody --*/
00314 /* */
00315 /*  Introspection, returns the body/script of the*/
00316 /*  specified style.*/
00317 /* */
00318 /*  Arguments:*/
00319 /*  styleName   Name of the style to query.*/
00320 /* */
00321 /*  Results:*/
00322 /*  A script, the body of the style.*/
00323 
00324 ret  ::report::stylebody (type styleName) {
00325     variable stylebody
00326     if {![info exists stylebody($styleName)]} {
00327     return -code error "style \"$styleName\" is not known"
00328     }
00329     return $stylebody($styleName)
00330 }
00331 
00332 /*  ::report::_styles --*/
00333 /* */
00334 /*  Returns alist containing the names of all known styles.*/
00335 /* */
00336 /*  Arguments:*/
00337 /*  None.*/
00338 /* */
00339 /*  Results:*/
00340 /*  A list containing the names of all known styles*/
00341 
00342 ret  ::report::styles () {
00343     variable styles
00344     return  $styles
00345 }
00346 
00347 /* */
00348 /*  Private functions follow*/
00349 
00350 /*  ::report::CheckStyleArguments --*/
00351 /* */
00352 /*  Internal helper. Used to check actual arguments of a style against the formal ones.*/
00353 /* */
00354 /*  Arguments:*/
00355 /*  styleName   Name of the style in question*/
00356 /*  arguments   Actual arguments for the style.*/
00357 /* */
00358 /*  Results:*/
00359 /*  None, or an error in case of problems.*/
00360 
00361 ret  ::report::CheckStyleArguments (type styleName , type arguments) {
00362     variable styleargs
00363 
00364     # Match formal and actual arguments, error out in case of problems.
00365     foreach {min catchall formal} $styleargs($styleName) break
00366 
00367     if {[llength $arguments] < $min} {
00368     # Determine the name of the first formal parameter which did not get a value.
00369     set firstmissing [lindex $formal [llength $arguments]]
00370     return -code error "no value given for parameter \"$firstmissing\" to style \"$styleName\""
00371     } elseif {[llength $arguments] > $min} {
00372     if {!$catchall && ([llength $arguments] > [llength $formal])} {
00373         # More actual arguments than formals, without catch-all argument, error
00374         return -code error "called style \"$styleName\" with too many arguments"
00375     }
00376     }
00377 }
00378 
00379 /*  ::report::ExecuteStyle --*/
00380 /* */
00381 /*  Internal helper. Applies a named style to the specified report object.*/
00382 /* */
00383 /*  Arguments:*/
00384 /*  name        Name of the report the style is applied to.*/
00385 /*  styleName   Name of the style to apply*/
00386 /*  arguments   Actual arguments for the style.*/
00387 /* */
00388 /*  Results:*/
00389 /*  None.*/
00390 
00391 ret  ::report::ExecuteStyle (type name , type styleName , type arguments) {
00392     variable styleargs
00393     variable stylebody
00394     variable styles
00395     variable commands
00396 
00397     CheckStyleArguments $styleName $arguments
00398     foreach {min catchall formal} $styleargs($styleName) break
00399 
00400     array set a {}
00401 
00402     if {([llength $arguments] > $min) && $catchall} {
00403     # #min = number of formal arguments - 1
00404     set a(args) [lrange $arguments $min end]
00405     set formal  [lrange $formal 0 end-1]
00406     incr min -1
00407     set arguments [lrange $arguments 0 $min]
00408 
00409     # arguments and formal are now of equal length and we also
00410     # know that there are no arguments having a default value.
00411     foreach v $formal aval $arguments {
00412         set a($v) $aval
00413     }
00414     }
00415 
00416     # More arguments than minimally required, but no more than formal
00417     # arguments! Proceed to standard matching: Go through the actual
00418     # values and associate them with a formal argument. Then fill the
00419     # remaining formal arguments with their default values.
00420 
00421     foreach aval $arguments {
00422     set v      [lindex $formal 0]
00423     set formal [lrange $formal 1 end]
00424     if {[llength $v] > 1} {set v [lindex $v 0]}
00425     set a($v) $aval
00426     }
00427 
00428     foreach vd $formal {
00429     foreach {var default} $vd {
00430         set a($var) $default
00431     }
00432     }
00433 
00434     # Create and initialize a safe interpreter, execute the style and
00435     # then break everything down again.
00436 
00437     set ip [interp create -safe]
00438 
00439     # -- Report methods --
00440 
00441     foreach m $commands {
00442     # safe-ip method --> here report method
00443     interp alias $ip $m {} $name $m
00444     }
00445 
00446     # -- Styles defined before this one --
00447 
00448     foreach s $styles {
00449     if {[string equal $s $styleName]} {break}
00450     interp alias $ip $s {} ::report::LinkExec $name $s
00451     }
00452 
00453     # -- Arguments as variables --
00454 
00455     foreach {var val} [array get a] {
00456     $ip eval [list set $var $val]
00457     }
00458 
00459     # Finally execute / apply the style.
00460 
00461     $ip eval $stylebody($styleName)
00462     interp delete $ip
00463     return
00464 }
00465 
00466 /*  ::report::_LinkExec --*/
00467 /* */
00468 /*  Internal helper. Used for application of styles from within*/
00469 /*  another style script. Collects the formal arguments into the*/
00470 /*  one list which is expected by "ExecuteStyle".*/
00471 /* */
00472 /*  Arguments:*/
00473 /*  name        Name of the report the style is applied to.*/
00474 /*  styleName   Name of the style to apply*/
00475 /*  args        Actual arguments for the style.*/
00476 /* */
00477 /*  Results:*/
00478 /*  None.*/
00479 
00480 ret  ::report::LinkExec (type name , type styleName , type args) {
00481     ExecuteStyle $name $styleName $args
00482 }
00483 
00484 /*  ::report::ReportProc --*/
00485 /* */
00486 /*  Command that processes all report object commands.*/
00487 /* */
00488 /*  Arguments:*/
00489 /*  name    Name of the report object to manipulate.*/
00490 /*  cmd Subcommand to invoke.*/
00491 /*  args    Arguments for subcommand.*/
00492 /* */
00493 /*  Results:*/
00494 /*  Varies based on command to perform*/
00495 
00496 ret  ::report::ReportProc (type name , optional cmd ="" , type args) {
00497     variable tcode
00498 
00499     # Do minimal args checks here
00500     if { [llength [info level 0]] == 2 } {
00501     error "wrong # args: should be \"$name option ?arg arg ...?\""
00502     }
00503     
00504     # Split the args into command and args components
00505 
00506     if {[info exists tcode($cmd)]} {
00507     # Template codes are a bit special
00508     eval [list ::report::_tAction $name $cmd] $args
00509     } else {
00510     if { [llength [info commands ::report::_$cmd]] == 0 } {
00511         variable commands
00512         set optlist [join $commands ", "]
00513         set optlist [linsert $optlist "end-1" "or"]
00514         error "bad option \"$cmd\": must be $optlist"
00515     }
00516     eval [list ::report::_$cmd $name] $args
00517     }
00518 }
00519 
00520 /*  ::report::CheckColumn --*/
00521 /* */
00522 /*  Helper to check and transform column indices. Returns the*/
00523 /*  absolute index number belonging to the specified*/
00524 /*  index. Rejects indices out of the valid range of columns.*/
00525 /* */
00526 /*  Arguments:*/
00527 /*  columns Number of columns*/
00528 /*  column  The incoming index to check and transform*/
00529 /* */
00530 /*  Results:*/
00531 /*  The absolute index to the column*/
00532 
00533 ret  ::report::CheckColumn (type columns , type column) {
00534     switch -regex -- $column {
00535     {end-[0-9]+} {
00536         regsub -- {end-} $column {} column
00537         set cc [expr {$columns - 1 - $column}]
00538         if {($cc < 0) || ($cc >= $columns)} {
00539         return -code error "column: index \"end-$column\" out of range"
00540         }
00541         return $cc
00542     }
00543     end {
00544         if {$columns <= 0} {
00545         return -code error "column: index \"$column\" out of range"
00546         }
00547         return [expr {$columns - 1}]
00548     }
00549     {[0-9]+} {
00550         if {($column < 0) || ($column >= $columns)} {
00551         return -code error "column: index \"$column\" out of range"
00552         }
00553         return $column
00554     }
00555     default {
00556         return -code error "column: syntax error in index \"$column\""
00557     }
00558     }
00559 }
00560 
00561 /*  ::report::CheckVerticals --*/
00562 /* */
00563 /*  Internal helper. Used to check the consistency of all active*/
00564 /*  templates with respect to the generated vertical separators*/
00565 /*  (Same length).*/
00566 /* */
00567 /*  Arguments:*/
00568 /*  name    Name of the report object to check.*/
00569 /* */
00570 /*  Results:*/
00571 /*  None.*/
00572 
00573 ret  ::report::CheckVerticals (type name) {
00574     upvar ::report::report${name}::vTemplate vTemplate
00575     upvar ::report::report${name}::enabled   enabled
00576     upvar ::report::report${name}::columns   columns
00577     upvar ::report::report${name}::tcaption  tcaption
00578     upvar ::report::report${name}::bcaption  bcaption
00579 
00580     for {set c 0} {$c <= $columns} {incr c} {
00581     # Collect all lengths for a column in a list, sort that and
00582     # compare first against last element. If they are not equal we
00583     # have found an inconsistent definition.
00584 
00585     set     res [list]
00586     lappend res [string length $vTemplate(data,$c)]
00587 
00588     if {$tcaption > 0} {
00589         lappend res [string length $vTemplate(topdata,$c)]
00590         if {($tcaption > 1) && $enabled(topdatasep)} {
00591         lappend res [string length $vTemplate(topdatasep,$c)]
00592         }
00593         if {$enabled(topcapsep)} {
00594         lappend res [string length $vTemplate(topcapsep,$c)]
00595         }
00596     }
00597     if {$bcaption > 0} {
00598         lappend res [string length $vTemplate(botdata,$c)]
00599         if {($bcaption > 1) && $enabled(botdatasep)} {
00600         lappend res [string length $vTemplate(botdatasep,$c)]
00601         }
00602         if {$enabled(botcapsep)} {
00603         lappend res [string length $vTemplate(botcapsep,$c)]
00604         }
00605     }
00606     foreach t {top datasep bottom} {
00607         if {$enabled($t)} {
00608         lappend res [string length $vTemplate($t,$c)]
00609         }
00610     }
00611 
00612     set res [lsort $res]
00613 
00614     if {[lindex $res 0] != [lindex $res end]} {
00615         return -code error "inconsistent verticals in report"
00616     }
00617     }
00618 }
00619 
00620 /*  ::report::_tAction --*/
00621 /* */
00622 /*  Implements the actions on templates (set, get, enable, disable, enabled)*/
00623 /* */
00624 /*  Arguments:*/
00625 /*  name        Name of the report object.*/
00626 /*  template    Name of the template to query or manipulate.*/
00627 /*  cmd     The action applied to the template*/
00628 /*  args        Additional arguments per action, see documentation.*/
00629 /* */
00630 /*  Results:*/
00631 /*  None.*/
00632 
00633 ret  ::report::_tAction (type name , type template , type cmd , type args) {
00634     # When coming in here we know that $template contains a legal
00635     # template code. No need to check again. We need 'tcode'
00636     # nevertheless to distinguish between separator (1) and data
00637     # templates (0).
00638 
00639     variable tcode
00640 
00641     switch -exact -- $cmd {
00642     set {
00643         if {[llength $args] != 1} {
00644         return -code error "Wrong # args: $name $template $cmd template"
00645         }
00646         set templval [lindex $args 0]
00647 
00648         upvar ::report::report${name}::columns   columns
00649         upvar ::report::report${name}::template  tpl
00650         upvar ::report::report${name}::hTemplate hTemplate
00651         upvar ::report::report${name}::vTemplate vTemplate
00652         upvar ::report::report${name}::enabled   enabled        
00653 
00654         if {$tcode($template)} {
00655         # Separator template, expected size = 2*colums+1
00656         if {[llength $templval] > (2*$columns+1)} {
00657             return -code error {template to long for number of columns in report}
00658         } elseif {[llength $templval] < (2*$columns+1)} {
00659             return -code error {template to short for number of columns in report}
00660         }
00661 
00662         set tpl($template) $templval
00663 
00664         set even 1
00665         set c1   0
00666         set c2   0
00667         foreach item $templval {
00668             if {$even} {
00669             set vTemplate($template,$c1) $item
00670             incr c1
00671             set even 0
00672             } else {
00673             set hTemplate($template,$c2) $item
00674             incr c2
00675             set even 1
00676             }
00677         }
00678         } else {
00679         # Data template, expected size = columns+1
00680         if {[llength $templval] > ($columns+1)} {
00681             return -code error {template to long for number of columns in report}
00682         } elseif {[llength $templval] < ($columns+1)} {
00683             return -code error {template to short for number of columns in report}
00684         }
00685 
00686         set tpl($template) $templval
00687 
00688         set c 0
00689         foreach item $templval {
00690             set vTemplate($template,$c) $item
00691             incr c
00692         }
00693         }
00694         if {$enabled($template)} {
00695         # Perform checks for active separator templates and
00696         # all data templates.
00697         CheckVerticals $name
00698         }
00699     }
00700     get -
00701     enable -
00702     disable -
00703     enabled {
00704         if {[llength $args] > 0} {
00705         return -code error "Wrong # args: $name $template $cmd"
00706         }
00707         switch -exact -- $cmd {
00708         get {
00709             upvar ::report::report${name}::template  tpl
00710             return $tpl($template)
00711         }
00712         enable {
00713             if {!$tcode($template)} {
00714             # Data template, can't be enabled.
00715             return -code error "Cannot enable data template \"$template\""
00716             }
00717 
00718             upvar ::report::report${name}::enabled enabled
00719 
00720             if {!$enabled($template)} {
00721             set enabled($template) 1
00722             CheckVerticals $name
00723             }
00724 
00725         }
00726         disable {
00727             if {!$tcode($template)} {
00728             # Data template, can't be disabled.
00729             return -code error "Cannot disable data template \"$template\""
00730             }
00731 
00732             upvar ::report::report${name}::enabled enabled
00733             if {$enabled($template)} {
00734             set enabled($template) 0
00735             }
00736         }
00737         enabled {
00738             if {!$tcode($template)} {
00739             # Data template, can't be disabled.
00740             return -code error "Cannot query state of data template \"$template\""
00741             }
00742 
00743             upvar ::report::report${name}::enabled enabled
00744             return $enabled($template)
00745         }
00746         default {error "Can't happen, panic, run, shout"}
00747         }
00748     }
00749     default {
00750         return -code error "Unknown template command \"$cmd\""
00751     }
00752     }
00753     return ""
00754 }
00755 
00756 /*  ::report::_tcaption --*/
00757 /* */
00758 /*  Sets or queries the size of the top caption region of the report.*/
00759 /* */
00760 /*  Arguments:*/
00761 /*  name    Name of the report object.*/
00762 /*  size    The new size, if not empty. Emptiness indicates that a*/
00763 /*      query was requested*/
00764 /* */
00765 /*  Results:*/
00766 /*  None, or the current size of the top caption region*/
00767 
00768 ret  ::report::_tcaption (type name , optional size ={)} {
00769     upvar ::report::report${name}::tcaption tcaption
00770 
00771     if {$size == {}} {
00772     return $tcaption
00773     }
00774     if {![string is integer $size]} {
00775     return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00776     }
00777     if {$size < 0} {
00778     return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00779     }
00780     if {$size == $tcaption} {
00781     /*  No change, nothing to do*/
00782     return ""
00783     }
00784     if {($size > 0) && ($tcaption == 0)} {
00785     /*  Perform a consistency check after the assignment, the*/
00786     /*  template might have been changed.*/
00787      tcaption =  $size
00788     CheckVerticals $name
00789     } else {
00790      tcaption =  $size
00791     }
00792     return ""
00793 }
00794 
00795 /*  ::report::_bcaption --*/
00796 /* */
00797 /*  Sets or queries the size of the bottom caption region of the report.*/
00798 /* */
00799 /*  Arguments:*/
00800 /*  name    Name of the report object.*/
00801 /*  size    The new size, if not empty. Emptiness indicates that a*/
00802 /*      query was requested*/
00803 /* */
00804 /*  Results:*/
00805 /*  None, or the current size of the bottom caption region*/
00806 
00807 ret  ::report::_bcaption (type name , optional size ={)} {
00808     upvar ::report::report${name}::bcaption bcaption
00809 
00810     if {$size == {}} {
00811     return $bcaption
00812     }
00813     if {![string is integer $size]} {
00814     return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00815     }
00816     if {$size < 0} {
00817     return -code error "size: expected integer greater than or equal to zero, got \"$size\""
00818     }
00819     if {$size == $bcaption} {
00820     /*  No change, nothing to do*/
00821     return ""
00822     }
00823     if {($size > 0) && ($bcaption == 0)} {
00824     /*  Perform a consistency check after the assignment, the*/
00825     /*  template might have been changed.*/
00826      bcaption =  $size
00827     CheckVerticals $name
00828     } else {
00829      bcaption =  $size
00830     }
00831     return ""
00832 }
00833 
00834 /*  ::report::_size --*/
00835 /* */
00836 /*  Sets or queries the size of the specified column.*/
00837 /* */
00838 /*  Arguments:*/
00839 /*  name    Name of the report object.*/
00840 /*  column  Index of the column to manipulate or query*/
00841 /*  size    The new size, if not empty. Emptiness indicates that a*/
00842 /*      query was requested*/
00843 /* */
00844 /*  Results:*/
00845 /*  None, or the current size of the column*/
00846 
00847 ret  ::report::_size (type name , type column , optional size ={)} {
00848     upvar ::report::report${name}::columns columns
00849     upvar ::report::report${name}::csize   csize
00850 
00851      column =  [CheckColumn $columns $column]
00852 
00853     if {$size == {}} {
00854     return $csize($column)
00855     }
00856     if {[string equal $size dyn]} {
00857      csize = ($column) $size
00858     return ""
00859     }
00860     if {![string is integer $size]} {
00861     return -code error "expected integer greater than zero, got \"$size\""
00862     }
00863     if {$size <= 0} {
00864     return -code error "expected integer greater than zero, got \"$size\""
00865     }
00866      csize = ($column) $size
00867     return ""
00868 }
00869 
00870 /*  ::report::_sizes --*/
00871 /* */
00872 /*  Sets or queries the sizes of all columns.*/
00873 /* */
00874 /*  Arguments:*/
00875 /*  name    Name of the report object.*/
00876 /*  sizes   The new sizes, if not empty. Emptiness indicates that a*/
00877 /*      query was requested*/
00878 /* */
00879 /*  Results:*/
00880 /*  None, or a list containing the sizes of all columns.*/
00881 
00882 ret  ::report::_sizes (type name , optional sizes ={)} {
00883     upvar ::report::report${name}::columns columns
00884     upvar ::report::report${name}::csize   csize
00885 
00886     if {$sizes == {}} {
00887      res =  [list]
00888     foreach k [lsort -integer [array names csize]] {
00889         lappend res $csize($k)
00890     }
00891     return $res
00892     }
00893     if {[llength $sizes] != $columns} {
00894     return -code error "Wrong /*  number of column sizes"*/
00895     }
00896     foreach size $sizes {
00897     if {[string equal $size dyn]} {
00898         continue
00899     }
00900     if {![string is integer $size]} {
00901         return -code error "expected integer greater than zero, got \"$size\""
00902     }
00903     if {$size <= 0} {
00904         return -code error "expected integer greater than zero, got \"$size\""
00905     }
00906     }
00907 
00908      i =  0
00909     foreach s $sizes {
00910      csize = ($i) $s
00911     incr i
00912     }
00913     return ""
00914 }
00915 
00916 /*  ::report::_pad --*/
00917 /* */
00918 /*  Sets or queries the padding for the specified column.*/
00919 /* */
00920 /*  Arguments:*/
00921 /*  name    Name of the report object.*/
00922 /*  column  Index of the column to manipulate or query*/
00923 /*  where   Where to place the padding. Emptiness indicates*/
00924 /*      that a query was requested.*/
00925 /* */
00926 /*  Results:*/
00927 /*  None, or the padding for the specified column.*/
00928 
00929 ret  ::report::_pad (type name , type column , optional where ={) {string { }}} {
00930     upvar ::report::report${name}::columns columns
00931     upvar ::report::report${name}::lpad   lpad
00932     upvar ::report::report${name}::rpad   rpad
00933 
00934      column =  [CheckColumn $columns $column]
00935 
00936     if {$where == {}} {
00937     return [list $lpad($column) $rpad($column)]
00938     }
00939 
00940     switch -exact -- $where {
00941     left {
00942          lpad = ($column) $string
00943     }
00944     right {
00945          rpad = ($column) $string
00946     }
00947     both {
00948          lpad = ($column) $string
00949          rpad = ($column) $string
00950     }
00951     default {
00952         return -code error "where: expected left, right, or both, got \"$where\""
00953     }
00954     }
00955     return ""
00956 }
00957 
00958 /*  ::report::_justify --*/
00959 /* */
00960 /*  Sets or queries the justification for the specified column.*/
00961 /* */
00962 /*  Arguments:*/
00963 /*  name    Name of the report object.*/
00964 /*  column  Index of the column to manipulate or query*/
00965 /*  jvalue  Justification to set. Emptiness indicates*/
00966 /*      that a query was requested*/
00967 /* */
00968 /*  Results:*/
00969 /*  None, or the current justication for the specified column*/
00970 
00971 ret  ::report::_justify (type name , type column , optional jvalue ={)} {
00972     upvar ::report::report${name}::columns columns
00973     upvar ::report::report${name}::cjust   cjust
00974 
00975      column =  [CheckColumn $columns $column]
00976 
00977     if {$jvalue == {}} {
00978     return $cjust($column)
00979     }
00980     switch -exact -- $jvalue {
00981     left - right - center {
00982          cjust = ($column) $jvalue
00983         return ""
00984     }
00985     default {
00986         return -code error "justification: expected, left, right, or center, got \"$jvalue\""
00987     }
00988     }
00989 }
00990 
00991 /*  ::report::_printmatrix --*/
00992 /* */
00993 /*  Format the specified matrix according to the configuration of*/
00994 /*  the report.*/
00995 /* */
00996 /*  Arguments:*/
00997 /*  name    Name of the report object.*/
00998 /*  matrix  Name of the matrix object to format.*/
00999 /* */
01000 /*  Results:*/
01001 /*  A string containing the formatted matrix.*/
01002 
01003 ret  ::report::_printmatrix (type name , type matrix) {
01004     CheckMatrix $name $matrix
01005     ColumnSizes $name $matrix state
01006 
01007     upvar ::report::report${name}::tcaption tcaption
01008     upvar ::report::report${name}::bcaption bcaption
01009 
01010     set    row 0
01011     set    out ""
01012     append out [Separator top $name $matrix state]
01013     if {$tcaption > 0} {
01014     set n $tcaption
01015     while {$n > 0} {
01016         append out [FormatData topdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01017         if {$n > 1} {
01018         append out [Separator topdatasep $name $matrix state]
01019         }
01020         incr n -1
01021         incr row
01022     }
01023     append out [Separator topcapsep $name $matrix state]
01024     }
01025 
01026     set n [expr {[$matrix rows] - $bcaption}]
01027 
01028     while {$row < $n} {
01029     append out [FormatData data $name state [$matrix get row $row] [$matrix rowheight $row]]
01030     incr row
01031     if {$row < $n} {
01032         append out [Separator datasep $name $matrix state]
01033     }
01034     }
01035 
01036     if {$bcaption > 0} {
01037     append out [Separator botcapsep $name $matrix state]
01038     set n $bcaption
01039     while {$n > 0} {
01040         append out [FormatData botdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01041         if {$n > 1} {
01042         append out [Separator botdatasep $name $matrix state]
01043         }
01044         incr n -1
01045         incr row
01046     }
01047     }
01048 
01049     append out [Separator bottom $name $matrix state]
01050 
01051     #parray state
01052     return $out
01053 }
01054 
01055 /*  ::report::_printmatrix2channel --*/
01056 /* */
01057 /*  Format the specified matrix according to the configuration of*/
01058 /*  the report.*/
01059 /* */
01060 /*  Arguments:*/
01061 /*  name    Name of the report.*/
01062 /*  matrix  Name of the matrix object to format.*/
01063 /*  chan    Handle of the channel to write the formatting result into.*/
01064 /* */
01065 /*  Results:*/
01066 /*  None.*/
01067 
01068 ret  ::report::_printmatrix2channel (type name , type matrix , type chan) {
01069     CheckMatrix $name $matrix
01070     ColumnSizes $name $matrix state
01071 
01072     upvar ::report::report${name}::tcaption tcaption
01073     upvar ::report::report${name}::bcaption bcaption
01074 
01075     set    row 0
01076     puts -nonewline $chan [Separator top $name $matrix state]
01077     if {$tcaption > 0} {
01078     set n $tcaption
01079     while {$n > 0} {
01080         puts -nonewline $chan \
01081             [FormatData topdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01082         if {$n > 1} {
01083         puts -nonewline $chan [Separator topdatasep $name $matrix state]
01084         }
01085         incr n -1
01086         incr row
01087     }
01088     puts -nonewline $chan [Separator topcapsep $name $matrix state]
01089     }
01090 
01091     set n [expr {[$matrix rows] - $bcaption}]
01092 
01093     while {$row < $n} {
01094     puts -nonewline $chan \
01095         [FormatData data $name state [$matrix get row $row] [$matrix rowheight $row]]
01096     incr row
01097     if {$row < $n} {
01098         puts -nonewline $chan [Separator datasep $name $matrix state]
01099     }
01100     }
01101 
01102     if {$bcaption > 0} {
01103     puts -nonewline $chan [Separator botcapsep $name $matrix state]
01104     set n $bcaption
01105     while {$n > 0} {
01106         puts -nonewline $chan \
01107             [FormatData botdata $name state [$matrix get row $row] [$matrix rowheight $row]]
01108         if {$n > 1} {
01109         puts -nonewline $chan [Separator botdatasep $name $matrix state]
01110         }
01111         incr n -1
01112         incr row
01113     }
01114     }
01115 
01116     puts -nonewline $chan [Separator bottom $name $matrix state]
01117     return
01118 }
01119 
01120 /*  ::report::_columns --*/
01121 /* */
01122 /*  Retrieves the number of columns in the report.*/
01123 /* */
01124 /*  Arguments:*/
01125 /*  name    Name of the report queried*/
01126 /* */
01127 /*  Results:*/
01128 /*  A number*/
01129 
01130 ret  ::report::_columns (type name) {
01131     upvar ::report::report${name}::columns columns
01132     return $columns
01133 }
01134 
01135 /*  ::report::_destroy --*/
01136 /* */
01137 /*  Destroy a report, including its associated command and data storage.*/
01138 /* */
01139 /*  Arguments:*/
01140 /*  name    Name of the report to destroy.*/
01141 /* */
01142 /*  Results:*/
01143 /*  None.*/
01144 
01145 ret  ::report::_destroy (type name) {
01146     namespace delete ::report::report$name
01147     interp alias {} ::$name {}
01148     return
01149 }
01150 
01151 /*  ::report::CheckMatrix --*/
01152 /* */
01153 /*  Internal helper for the "print" methods. Checks that the*/
01154 /*  supplied matrix can be formatted by the specified report.*/
01155 /* */
01156 /*  Arguments:*/
01157 /*  name    Name of the report to use for the formatting*/
01158 /*  matrix  Name of the matrix to format.*/
01159 /* */
01160 /*  Results:*/
01161 /*  None, or an error in case of problems.*/
01162 
01163 ret  ::report::CheckMatrix (type name , type matrix) {
01164     upvar ::report::report${name}::columns  columns
01165     upvar ::report::report${name}::tcaption tcaption
01166     upvar ::report::report${name}::bcaption bcaption
01167 
01168     if {$columns != [$matrix columns]} {
01169     return -code error "report/matrix mismatch in number of columns"
01170     }
01171     if {($tcaption + $bcaption) > [$matrix rows]} {
01172     return -code error "matrix too small, top and bottom captions overlap"
01173     }
01174 }
01175 
01176 /*  ::report::ColumnSizes --*/
01177 /* */
01178 /*  Internal helper for the "print" methods. Computes the final*/
01179 /*  column sizes (with and without padding) and stores them in*/
01180 /*  the print-state*/
01181 /* */
01182 /*  Arguments:*/
01183 /*  name        Name of the report used for the formatting*/
01184 /*  matrix      Name of the matrix to format.*/
01185 /*  statevar    Name of the array variable holding the state*/
01186 /*          of the formatter.*/
01187 /* */
01188 /*  Results:*/
01189 /*  None.*/
01190 
01191 ret  ::report::ColumnSizes (type name , type matrix , type statevar) {
01192     # Calculate the final column sizes with and without padding and
01193     # store them in the local state.
01194 
01195     upvar $statevar state
01196 
01197     upvar ::report::report${name}::columns  columns
01198     upvar ::report::report${name}::csize    csize
01199     upvar ::report::report${name}::lpad     lpad
01200     upvar ::report::report${name}::rpad     rpad
01201 
01202     for {set c 0} {$c < $columns} {incr c} {
01203     if {[string equal dyn $csize($c)]} {
01204         set size [$matrix columnwidth $c]
01205     } else {
01206         set size $csize($c)
01207     }
01208 
01209     set state(s,$c) $size
01210 
01211     incr size [string length $lpad($c)]
01212     incr size [string length $rpad($c)]
01213 
01214     set state(s/pad,$c) $size
01215     }
01216 
01217     return
01218 }
01219 
01220 /*  ::report::Separator --*/
01221 /* */
01222 /*  Internal helper for the "print" methods. Computes the final*/
01223 /*  shape of the various separators using the column sizes with*/
01224 /*  padding found in the print state. Uses also the print state as*/
01225 /*  a cache to avoid costly recomputation for the separators which*/
01226 /*  are used multiple times.*/
01227 /* */
01228 /*  Arguments:*/
01229 /*  tcode       Code of the separator to compute / template to use*/
01230 /*  name        Name of the report used for the formatting*/
01231 /*  matrix      Name of the matrix to format.*/
01232 /*  statevar    Name of the array variable holding the state*/
01233 /*          of the formatter.*/
01234 /* */
01235 /*  Results:*/
01236 /*  The final separator string. Empty for disabled separators.*/
01237 
01238 ret  ::report::Separator (type tcode , type name , type matrix , type statevar) {
01239     upvar ::report::report${name}::enabled  e
01240     if {!$e($tcode)} {return ""}
01241     upvar $statevar state
01242     if {![info exists state($tcode)]} {
01243     upvar ::report::report${name}::vTemplate vt
01244     upvar ::report::report${name}::hTemplate ht
01245     upvar ::report::report${name}::columns   cs
01246     set str ""
01247     for {set c 0} {$c < $cs} {incr c} {
01248         append str $vt($tcode,$c)
01249         set fill $ht($tcode,$c)
01250         set flen [string length $fill]
01251         set rep  [expr {($state(s/pad,$c)/$flen)+1}]
01252         append str [string range [string repeat $fill $rep] 0 [expr {$state(s/pad,$c)-1}]]
01253     }
01254     append str $vt($tcode,$cs)
01255     set state($tcode) $str
01256     }
01257     return $state($tcode)\n
01258 }
01259 
01260 /*  ::report::FormatData --*/
01261 /* */
01262 /*  Internal helper for the "print" methods. Computes the output*/
01263 /*  for one row in the matrix, given its values, the rowheight,*/
01264 /*  padding and justification.*/
01265 /* */
01266 /*  Arguments:*/
01267 /*  tcode       Code of the data template to use*/
01268 /*  name        Name of the report used for the formatting*/
01269 /*  statevar    Name of the array variable holding the state*/
01270 /*          of the formatter.*/
01271 /*  line        List containing the values to format*/
01272 /*  rh      Height of the row (line) in lines.*/
01273 /* */
01274 /*  Results:*/
01275 /*  The formatted string for the supplied row.*/
01276 
01277 ret  ::report::FormatData (type tcode , type name , type statevar , type line , type rh) {
01278     upvar $statevar state
01279     upvar ::report::report${name}::vTemplate vt
01280     upvar ::report::report${name}::columns   cs
01281     upvar ::report::report${name}::lpad      lpad
01282     upvar ::report::report${name}::rpad      rpad
01283     upvar ::report::report${name}::cjust     cjust
01284 
01285     if {$rh == 1} {
01286     set str ""
01287     set c 0
01288     foreach cell $line {
01289         # prefix, cell (pad-l, value, pad-r)
01290         append str $vt($tcode,$c)$lpad($c)[FormatCell $cell $state(s,$c) $cjust($c)]$rpad($c)
01291         incr c
01292     }
01293     append str $vt($tcode,$cs)\n
01294     return $str
01295     } else {
01296     array set str {}
01297     for {set l 1} {$l <= $rh} {incr l} {set str($l) ""}
01298 
01299     # - Future - Vertical justification of cells less tall than rowheight
01300     # - Future - Vertical cutff aftert n lines, auto-repeat of captions
01301     # - Future - => Higher level, not here, use virtual matrices for this
01302     # - Future -  and count the generated lines
01303 
01304     set c 0
01305     foreach fcell $line {
01306         set fcell [split $fcell \n]
01307         for {set l 1; set lo 0} {$l <= $rh} {incr l; incr lo} {
01308         append str($l) $vt($tcode,$c)$lpad($c)[FormatCell \
01309             [lindex $fcell $lo] $state(s,$c) $cjust($c)]$rpad($c)
01310         }
01311         incr c
01312     }
01313     set strout ""
01314     for {set l 1} {$l <= $rh} {incr l} {
01315         append strout $str($l)$vt($tcode,$cs)\n
01316     }
01317     return $strout
01318     }
01319 }
01320 
01321 /*  ::report::FormatCell --*/
01322 /* */
01323 /*  Internal helper for the "print" methods. Formats the value of*/
01324 /*  a single cell according to column size and justification.*/
01325 /* */
01326 /*  Arguments:*/
01327 /*  value   The value to format*/
01328 /*  size    The size of the column, without padding*/
01329 /*  just    The justification for the current cell/column*/
01330 /* */
01331 /*  Results:*/
01332 /*  The formatted string for the supplied cell.*/
01333 
01334 ret  ::report::FormatCell (type value , type size , type just) {
01335     set vlen [string length $value]
01336 
01337     if {$vlen == $size} {
01338     # Value fits exactly, justification is irrelevant
01339     return $value
01340     }
01341 
01342     # - Future - Other fill characters ...
01343     # - Future - Different fill characters per class of value => regex/glob pattern|functions
01344     # - Future - Wraparound - interacts with rowheight!
01345 
01346     switch -exact -- $just {
01347     left {
01348         if {$vlen < $size} {
01349         return $value[string repeat " " [expr {$size - $vlen}]]
01350         }
01351         return [string range $value [expr {$vlen - $size}] end]
01352     }
01353     right {
01354         if {$vlen < $size} {
01355         return [string repeat " " [expr {$size - $vlen}]]$value
01356         }
01357         incr size -1
01358         return [string range $value 0 $size]
01359     }
01360     center {
01361         if {$vlen < $size} {
01362         set fill  [expr {$size - $vlen}]
01363         set rfill [expr {$fill / 2}]
01364         set lfill [expr {$fill - $rfill}]
01365         return [string repeat " " $lfill]$value[string repeat " " $rfill]
01366         }
01367 
01368         set cut  [expr {$vlen - $size}]
01369         set lcut [expr {$cut / 2}]
01370         set rcut [expr {$cut - $lcut}]
01371 
01372         return [string range $value $lcut end-$rcut]
01373     }
01374     default {
01375         error "Can't happen, panic, run, shout"
01376     }
01377     }
01378 }
01379 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1