record.tcl

Go to the documentation of this file.
00001 /* ============================================================*/
00002 /*  ::struct::record --*/
00003 /* */
00004 /*     Implements a container data structure similar to a 'C' */
00005 /*     structure. It hides the ugly details about keeping the*/
00006 /*     data organized by using a combination of arrays, lists*/
00007 /*     and namespaces.*/
00008 /*    */
00009 /*     Each record definition is kept in a master array */
00010 /*     (_recorddefn) under the ::struct::record namespace. Each*/
00011 /*     instance of a record is kept within a separate namespace*/
00012 /*     for each record definition. Hence, instances of*/
00013 /*     the same record definition are managed under the*/
00014 /*     same namespace. This avoids possible collisions, and*/
00015 /*     also limits one big global array mechanism.*/
00016 /* */
00017 /*  Copyright (c) 2002 by Brett Schwarz*/
00018 /* */
00019 /*  See the file "license.terms" for information on usage and redistribution*/
00020 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00021 /* */
00022 /*  This code may be distributed under the same terms as Tcl.*/
00023 /* */
00024 /*  $Id: record.tcl,v 1.10 2004/09/29 20:56:18 andreas_kupries Exp $*/
00025 /* */
00026 /* ============================================================*/
00027 /* */
00028 /*   FIX ERROR MESSAGES SO THEY MAKE SENSE (Wrong args)*/
00029 
00030 namespace ::struct {}
00031 
00032 namespace ::struct::record {
00033 
00034     /** 
00035      *#  array of lists that holds the 
00036      *#  definition (variables) for each 
00037      *#  record
00038      *#
00039      *#  _recorddefn(some_record) var1 var2 var3 ...
00040      *#
00041  */
00042     variable _recorddefn
00043 
00044     /** 
00045      *#  holds the count for each record
00046      *#  in cases where the instance is
00047      *#  automatically generated
00048      *#
00049      *#  _count(some_record) 0
00050      *#
00051  */
00052 
00053     /*  This is not a count, but an id generator. Its value has to*/
00054     /*  increase monotonicaly.*/
00055 
00056     variable _count
00057 
00058     /** 
00059      *#  array that holds the defining record's
00060      *#  name for each instances
00061      *#
00062      *#  _defn(some_instances) name_of_defining_record
00063      *#
00064  */
00065     variable  _defn
00066     array  _defn =  {}
00067 
00068     /** 
00069      *#  This holds the defaults for a record definition.
00070      *#  If no default is given for a member of a record,
00071      *#  then the value is assigned to the empty string
00072      *#
00073  */
00074     variable _defaults
00075 
00076     /** 
00077      *#  These are the possible sub commands
00078      *#
00079  */
00080     variable commands
00081      commands =  [list define delete exists show]
00082 
00083     /** 
00084      *#  This keeps track of the level that we are in
00085      *#  when handling nested records. This is kind of
00086      *#  a hack, and probably can be handled better
00087      *#
00088  */
00089      _level =  0
00090 
00091     namespace export record
00092 }
00093 
00094 /* ------------------------------------------------------------*/
00095 /*  ::struct::record::record --*/
00096 /* */
00097 /*     main command used to access the other sub commands*/
00098 /* */
00099 /*  Arguments:*/
00100 /*     cmd_   The sub command (i.e. define, show, delete, exists)*/
00101 /*     args   arguments to pass to the sub command*/
00102 /* */
00103 /*  Results:*/
00104 /*   none returned*/
00105 /* ------------------------------------------------------------*/
00106 /* */
00107 ret  ::struct::record::record (type cmd_ , type args) {
00108     variable commands
00109 
00110     if {[lsearch $commands $cmd_] < 0} {
00111         error "Sub command \"$cmd_\" is not recognized. Must be [join $commands ,]"
00112     }
00113 
00114     set cmd_ [string totitle "$cmd_"]
00115     return [uplevel 1 ::struct::record::${cmd_} $args]
00116 
00117 }; /*  end proc ::struct::record::record*/
00118 
00119 
00120 /* ------------------------------------------------------------*/
00121 /*  ::struct::record::Define --*/
00122 /* */
00123 /*     Used to define a record*/
00124 /* */
00125 /*  Arguments:*/
00126 /*     defn_    the name of the record definition*/
00127 /*     vars_    the variables of the record (as a list)*/
00128 /*     args     instances to be create during definition*/
00129 /* */
00130 /*  Results:*/
00131 /*    Returns the name of the definition during successful*/
00132 /*    creation.*/
00133 /* ------------------------------------------------------------*/
00134 /* */
00135 ret  ::struct::record::Define (type defn_ , type vars_ , type args) {
00136 
00137     variable _recorddefn
00138     variable _count
00139     variable _defaults
00140 
00141     set defn_ [Qualify $defn_]
00142 
00143     if {[info exists _recorddefn($defn_)]} {
00144         error "Record definition $defn_ already exists"
00145     }
00146 
00147     if {[lsearch [info commands] $defn_] >= 0} {
00148         error "Structure definition name can not be a Tcl command name"
00149     }
00150 
00151     set _defaults($defn_)   [list]
00152     set _recorddefn($defn_) [list]
00153 
00154 
00155     ##
00156     ##  Loop through the members of the record
00157     ##  definition
00158     ##
00159     foreach V $vars_ {
00160 
00161         set len [llength $V]
00162         set D ""
00163 
00164         ##
00165         ##  2 --> there is a default value
00166         ##        assigned to the member
00167         ##
00168         ##  3 --> there is a nested record
00169         ##        definition given as a member
00170         ##
00171         if {$len == 2} {
00172 
00173             set D [lindex $V 1]
00174             set V [lindex $V 0]
00175 
00176         } elseif {$len == 3} {
00177 
00178             if {![string match "record" "[lindex $V 0]"]} {
00179 
00180                 Delete record $defn_
00181                 error "$V is a Bad member for record definition
00182                 definition creation aborted."
00183             }
00184 
00185             set new [lindex $V 1]
00186 
00187             set new [Qualify $new]
00188 
00189             ##
00190             ##  Right now, there can not be circular records
00191             ##  so, we abort the creation
00192             ##
00193             if {[string match "$defn_" "$new"]} {
00194                 Delete record $defn_
00195                 error "Can not have circular records. Structure was not created."
00196             }
00197 
00198             ##
00199             ##  Will take care of the nested record later
00200             ##  We just join by :: because this is how it
00201             ##  use to be declared, so the parsing code
00202             ##  is already there.
00203             ##
00204             set V [join [lrange $V 1 2] "::"]
00205         }
00206 
00207         lappend _recorddefn($defn_) $V
00208         lappend _defaults($defn_)   $D
00209     }
00210     
00211 
00212     uplevel #0 [list interp alias {} $defn_ {} ::struct::record::Create $defn_]
00213 
00214     set _count($defn_) 0
00215 
00216     namespace eval ::struct::record${defn_} {
00217         variable values
00218         variable instances
00219 
00220         set instances [list]
00221     }
00222 
00223     ##
00224     ##    If there were args given (instances), then
00225     ##    create them now
00226     ##
00227     foreach A $args {
00228 
00229         uplevel 1 [list ::struct::record::Create $defn_ $A]
00230     }
00231 
00232     return $defn_
00233 
00234 }; /*  end proc ::struct::record::Define*/
00235 
00236 
00237 /* ------------------------------------------------------------*/
00238 /*  ::struct::record::Create --*/
00239 /* */
00240 /*     Creates an instance of a record definition*/
00241 /* */
00242 /*  Arguments:*/
00243 /*     defn_    the name of the record definition*/
00244 /*     inst_    the name of the instances to create*/
00245 /*     args     values to set to the record's members*/
00246 /* */
00247 /*  Results:*/
00248 /*    Returns the name of the instance for a successful creation*/
00249 /* ------------------------------------------------------------*/
00250 /* */
00251 ret  ::struct::record::Create (type defn_ , type inst_ , type args) {
00252 
00253     variable _recorddefn
00254     variable _count
00255     variable _defn
00256     variable _defaults
00257     variable _level
00258 
00259     set inst_ [Qualify "$inst_"]
00260 
00261     ##
00262     ##    test to see if the record
00263     ##    definition has been defined yet
00264     ##
00265     if {![info exists _recorddefn($defn_)]} {
00266         error "Structure $defn_ does not exist"
00267     }
00268 
00269 
00270     ##
00271     ##    if there was no argument given,
00272     ##    then assume that the record
00273     ##    variable is automatically
00274     ##    generated
00275     ##
00276     if {[string match "[Qualify #auto]" "$inst_"]} {
00277         set c $_count($defn_)
00278         set inst_ [format "%s%s" ${defn_} $_count($defn_)]
00279         incr _count($defn_)
00280     }
00281 
00282     ##
00283     ##    Test to see if this instance is already
00284     ##    created. This avoids any collisions with
00285     ##    previously created instances
00286     ##
00287     if {[info exists _defn($inst_)]} {
00288         incr _count($defn_) -1
00289         error "Instances $inst_ already exists"
00290     }
00291 
00292     set _defn($inst_) $defn_
00293 
00294     ##
00295     ##    Initialize record variables to
00296     ##    defaults
00297     ##
00298 
00299     uplevel #0 [list interp alias {} ${inst_} {} ::struct::record::Cmd $inst_]
00300 
00301     set cnt 0
00302     foreach V $_recorddefn($defn_) D $_defaults($defn_) {
00303 
00304         set [Ns $inst_]values($inst_,$V) $D
00305 
00306         ##
00307         ##  Test to see if there is a nested record
00308         ##
00309         if {[regexp -- {([\w]*)::([\w]*)} $V m def inst]} {
00310 
00311             if {$_level == 0} {
00312                 set _level 2
00313             }
00314 
00315             ##
00316             ##  This is to guard against if the creation
00317             ##  had failed, that there isn't any
00318             ##  lingering variables/alias around
00319             ##
00320             set def [Qualify $def $_level]
00321 
00322             if {![info exists _recorddefn($def)]} {
00323 
00324                 Delete inst "$inst_"
00325 
00326                 return
00327             }
00328 
00329             ##
00330             ##    evaluate the nested record. If there
00331             ##    were values for the variables passed
00332             ##    in, then we assume that the value for
00333             ##    this nested record is a list 
00334             ##    corresponding the the nested list's
00335             ##    variables, and so we pass that to
00336             ##    the nested record's instantiation.
00337             ##    We then get rid of those args for later
00338             ##    processing.
00339             ##
00340             set cnt_plus [expr {$cnt + 1}]
00341             set mem [lindex $args $cnt]
00342             if {![string match "" "$mem"]} {
00343                  if {![string match "-$inst" "$mem"]} {
00344                     Delete inst "$inst_"
00345                     error "$inst is not a member of $defn_"
00346                 }
00347             }
00348             incr _level
00349             set narg [lindex $args $cnt_plus]
00350             eval [linsert $narg 0 Create $def ${inst_}.${inst}]
00351             set args [lreplace $args $cnt $cnt_plus]
00352 
00353             incr _level -1
00354         } else {
00355 
00356             uplevel #0 [list interp alias {} ${inst_}.$V {} ::struct::record::Access $defn_ $inst_ $V]
00357             incr cnt 2
00358         }
00359 
00360     }; # end foreach variable
00361 
00362     lappend [Ns $inst_]instances $inst_
00363 
00364     foreach {k v} $args {
00365 
00366         Access $defn_ $inst_ [string trimleft "$k" -] $v
00367 
00368     }; # end foreach arg {}
00369 
00370     if {$_level == 2} {
00371     set _level 0
00372     }
00373 
00374     return $inst_
00375 
00376 }; /*  end proc ::struct::record::Create*/
00377 
00378 
00379 /* ------------------------------------------------------------*/
00380 /*  ::struct::record::Access --*/
00381 /* */
00382 /*     Provides a common proc to access the variables*/
00383 /*     from the aliases create for each variable in the record*/
00384 /* */
00385 /*  Arguments:*/
00386 /*     defn_    the name of the record to access*/
00387 /*     inst_    the name of the instance to create*/
00388 /*     var_     the variable of the record to access*/
00389 /*     args     a value to set to var_ (if any)*/
00390 /* */
00391 /*  Results:*/
00392 /*     Returns the value of the record member (var_)*/
00393 /* ------------------------------------------------------------*/
00394 /* */
00395 ret  ::struct::record::Access (type defn_ , type inst_ , type var_ , type args) {
00396 
00397     variable _recorddefn
00398     variable _defn
00399 
00400     set i [lsearch $_recorddefn($defn_) $var_]
00401 
00402     if {$i < 0} {
00403          error "$var_ does not exist in record $defn_"
00404     }
00405 
00406     if {![info exists _defn($inst_)]} {
00407 
00408          error "$inst_ does not exist"
00409     }
00410 
00411     if {[set idx [lsearch $args "="]] >= 0} {
00412         set args [lreplace $args $idx $idx]
00413     } 
00414 
00415     ##
00416     ##    If a value was given, then set it
00417     ##
00418     if {[llength $args] != 0} {
00419 
00420         set val_ [lindex $args 0]
00421 
00422         set [Ns $inst_]values($inst_,$var_) $val_
00423     }
00424 
00425     return [set [Ns $inst_]values($inst_,$var_)]
00426      
00427 }; /*  end proc ::struct::record::Access*/
00428 
00429 
00430 /* ------------------------------------------------------------*/
00431 /*  ::struct::record::Cmd --*/
00432 /* */
00433 /*     Used to process the set/get requests.*/
00434 /* */
00435 /*  Arguments:*/
00436 /*     inst_    the record instance name*/
00437 /*     args     For 'get' this is the record members to*/
00438 /*              retrieve. For 'set' this is a member/value*/
00439 /*              pair.*/
00440 /* */
00441 /*  Results:*/
00442 /*    For 'set' returns the empty string. For 'get' it returns*/
00443 /*    the member values.*/
00444 /* ------------------------------------------------------------*/
00445 /* */
00446 ret  ::struct::record::Cmd (type inst_ , type args) {
00447 
00448     variable _defn
00449 
00450     set result [list]
00451 
00452     set len [llength $args]
00453     if {$len <= 1} {return [Show values "$inst_"]}
00454 
00455     set cmd [lindex $args 0]
00456 
00457     if {[string match "cget" "$cmd"]} {
00458 
00459             set cnt 0
00460             foreach k [lrange $args 1 end] {
00461                 if {[catch {set r [${inst_}.[string trimleft ${k} -]]} err]} {
00462                     error "Bad option \"$k\""
00463                 }
00464 
00465                 lappend result $r
00466                 incr cnt
00467             }
00468             if {$cnt == 1} {set result [lindex $result 0]}
00469             return $result
00470 
00471     } elseif {[string match "config*" "$cmd"]} {
00472 
00473             set L [lrange $args 1 end]
00474             foreach {k v} $L {
00475                  ${inst_}.[string trimleft ${k} -] $v
00476             }
00477 
00478     } else {
00479             error "Wrong argument.
00480             must be \"object cget|configure args\""
00481     }
00482 
00483     return [list]
00484 
00485 }; /*  end proc ::struct::record::Cmd*/
00486 
00487 
00488 /* ------------------------------------------------------------*/
00489 /*  ::struct::record::Ns --*/
00490 /* */
00491 /*     This just constructs a fully qualified namespace for a*/
00492 /*     particular instance.*/
00493 /* */
00494 /*  Arguments;*/
00495 /*     inst_    instance to construct the namespace for.*/
00496 /* */
00497 /*  Results:*/
00498 /*     Returns the fully qualified namespace for the instance*/
00499 /* ------------------------------------------------------------*/
00500 /* */
00501 ret  ::struct::record::Ns (type inst_) {
00502 
00503     variable _defn
00504 
00505     if {[catch {set ret $_defn($inst_)} err]} {
00506         return $inst_
00507     }
00508 
00509     return [format "%s%s%s" "::struct::record" $ret "::"]
00510 
00511 }; /*  end proc ::struct::record::Ns*/
00512 
00513 
00514 /* ------------------------------------------------------------*/
00515 /*  ::struct::record::Show --*/
00516 /* */
00517 /*      Display info about the record that exist*/
00518 /* */
00519 /*  Arguments:*/
00520 /*     what_    subcommand*/
00521 /*     record_  record or instance to process*/
00522 /* */
00523 /*  Results:*/
00524 /*     if what_ = record, then return list of records*/
00525 /*                definition names.*/
00526 /*     if what_ = members, then return list of members*/
00527 /*                or members of the record.*/
00528 /*     if what_ = instance, then return a list of instances*/
00529 /*                with record definition of record_*/
00530 /*     if what_ = values, then it will return the values*/
00531 /*                for a particular instance*/
00532 /* ------------------------------------------------------------*/
00533 /* */
00534 ret  ::struct::record::Show (type what_ , optional record_ ="") {
00535 
00536     variable _recorddefn
00537     variable _defn
00538     variable _defaults
00539 
00540     ##
00541     ## We just prepend :: to the record_ argument
00542     ##
00543     if {![string match "::*" "$record_"]} {set record_ "::$record_"}
00544 
00545     if {[string match "record*" "$what_"]} {
00546         return [lsort [array names _recorddefn]]
00547     } elseif {[string match "mem*" "$what_"]} {
00548 
00549        if {[string match "" "$record_"] || ![info exists _recorddefn($record_)]} {
00550            error "Bad arguments while accessing members. Bad record name"
00551        }
00552 
00553        set res [list]
00554        set cnt 0
00555        foreach m $_recorddefn($record_) {
00556            set def [lindex $_defaults($record_) $cnt]
00557            if {[regexp -- {([\w]+)::([\w]+)} $m m d i]} {
00558                lappend res [list record $d $i]
00559            } elseif {![string match "" "$def"]} {
00560                lappend res [list $m $def]
00561            } else {
00562                lappend res $m
00563            }
00564 
00565            incr cnt
00566        }
00567 
00568        return $res
00569 
00570     } elseif {[string match "inst*" "$what_"]} {
00571 
00572         if {![info exists ::struct::record${record_}::instances]} {
00573             return [list]
00574         }
00575         return [lsort [set ::struct::record${record_}::instances]]
00576 
00577     } elseif {[string match "val*" "$what_"]} {
00578 
00579            set ns $_defn($record_)
00580 
00581            if {[string match "" "$record_"] || ([lsearch [set [Ns $record_]instances] $record_] < 0)} {
00582 
00583                error "Wrong arguments to values. Bad instance name"
00584            }
00585 
00586            set ret [list]
00587            foreach k $_recorddefn($ns) {
00588 
00589               set v [set [Ns $record_]values($record_,$k)]
00590 
00591               if {[regexp -- {([\w]*)::([\w]*)} $k m def inst]} {
00592                   set v [::struct::record::Show values ${record_}.${inst}]
00593               }
00594 
00595               lappend ret -[namespace tail $k] $v
00596            }
00597            return $ret
00598 
00599     }
00600 
00601     return [list]
00602 
00603 }; /*  end proc ::struct::record::Show*/
00604 
00605 
00606 /* ------------------------------------------------------------*/
00607 /*  ::struct::record::Delete --*/
00608 /* */
00609 /*     Deletes a record instance or a record definition*/
00610 /* */
00611 /*  Arguments:*/
00612 /*     sub_    what to delete. Either 'instance' or 'record'*/
00613 /*     item_   the specific record instance or definition*/
00614 /*             delete.*/
00615 /* */
00616 /*  Returns:*/
00617 /*     none*/
00618 /* */
00619 /* ------------------------------------------------------------*/
00620 /* */
00621 ret  ::struct::record::Delete (type sub_ , type item_) {
00622 
00623     variable _recorddefn
00624     variable _defn
00625     variable _count
00626     variable _defaults
00627 
00628     ##
00629     ## We just semi-blindly prepend :: to the record_ argument
00630     ##
00631     if {![string match "::*" "$item_"]} {set item_ "::$item_"}
00632 
00633     switch -- $sub_ {
00634 
00635         instance -
00636         instances -
00637         inst    {
00638 
00639 
00640             if {[Exists instance $item_]} {
00641         
00642         set ns $_defn($item_)
00643                 foreach A [info commands ${item_}.*] {
00644             Delete inst $A
00645                 }
00646         
00647                 catch {
00648                     foreach {k v} [array get [Ns $item_]values $item_,*] {
00649                         
00650                         unset [Ns $item_]values($k)
00651                     }
00652                     set i [lsearch [set [Ns $item_]instances] $item_]
00653                     set [Ns $item_]instances [lreplace [set [Ns $item_]instances] $i $i]
00654                     unset _defn($item_)
00655                 }
00656 
00657         # Auto-generated id numbers increase monotonically.
00658         # Reverting here causes the next auto to fail, claiming
00659         # that the instance exists.
00660                 # incr _count($ns) -1
00661         
00662             } else {
00663                 #error "$item_ is not a instance"
00664             }
00665         }
00666         record  -
00667         records   {
00668 
00669 
00670             ##
00671             ##  Delete the instances for this
00672             ##  record
00673             ##
00674             foreach I [Show instance "$item_"] {
00675                 catch {Delete instance "$I"}
00676             }
00677 
00678             catch {
00679                 unset _recorddefn($item_)
00680                 unset _defaults($item_)
00681                 unset _count($item_)
00682                 namespace delete ::struct::record${item_}
00683             }
00684 
00685             
00686         }
00687         default   {
00688             error "Wrong arguments to delete"
00689         }
00690 
00691     }; # end switch
00692 
00693     catch { uplevel #0 [list interp alias {} $item_ {}]}
00694 
00695     return
00696 
00697 }; /*  end proc ::struct::record::Delete*/
00698 
00699 
00700 /* ------------------------------------------------------------*/
00701 /*  ::struct::record::Exists --*/
00702 /* */
00703 /*     Tests whether a record definition or record*/
00704 /*     instance exists.*/
00705 /* */
00706 /*  Arguments:*/
00707 /*     sub_    what to test. Either 'instance' or 'record'*/
00708 /*     item_   the specific record instance or definition*/
00709 /*             that needs to be tested.*/
00710 /*     */
00711 /*  Tests to see if a particular instance exists*/
00712 /* */
00713 /* ------------------------------------------------------------*/
00714 /* */
00715 ret  ::struct::record::Exists (type sub_ , type item_) {
00716 
00717 
00718     switch -glob -- $sub_ {
00719         inst*    {
00720     
00721             if {([lsearch ::[Ns $item_]instances $item_] >=0) || [llength [info commands ::${item_}.*]]} {
00722                 return 1
00723             } else {
00724                 return 0
00725             }
00726         }
00727         record  {
00728     
00729             set item_ "::$item_"
00730             if {[info exists _recorddefn($item_)] || [llength [info commands ${item_}]]} {
00731                 return 1
00732             } else {
00733                 return 0
00734             }
00735         }
00736         default  {
00737             error "Wrong arguments. Must be exists record|instance target"
00738         }
00739     }; # end switch
00740 
00741 }; /*  end proc ::struct::record::Exists*/
00742 
00743 
00744 /* ------------------------------------------------------------*/
00745 /*  ::struct::record::Qualify --*/
00746 /* */
00747 /*     Contructs the qualified name of the calling scope. This*/
00748 /*     defaults to 2 levels since there is an extra proc call in*/
00749 /*     between.*/
00750 /* */
00751 /*  Arguments:*/
00752 /*     item_   the command that needs to be qualified*/
00753 /*     level_  how many levels to go up (default = 2)*/
00754 /*     */
00755 /*  Results:*/
00756 /*     the item_ passed in fully qualified*/
00757 /* */
00758 /* ------------------------------------------------------------*/
00759 /* */
00760 ret  ::struct::record::Qualify (type item_ , optional level_ =2) {
00761 
00762     if {![string match "::*" "$item_"]} {
00763         set ns [uplevel $level_ [list namespace current]]
00764 
00765         if {![string match "::" "$ns"]} {
00766             append ns "::"
00767         }
00768      
00769         set item_ "$ns${item_}"
00770     }
00771 
00772     return "$item_"
00773 
00774 }; /*  end proc ::struct::record::Qualify*/
00775 
00776 /*  ### ### ### ######### ######### #########*/
00777 /*  Ready*/
00778 
00779 namespace ::struct {
00780     /*  Get 'record::record' into the general structure namespace.*/
00781     namespace import -force record::record
00782     namespace export record
00783 }
00784 package provide struct::record 1.2.1
00785 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1