registry/registry.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  (C) 2006 Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00003 /** 
00004  * ###
00005  */
00006 
00007 package require Tcl 8.3
00008 package require snit
00009 package require tie
00010 
00011 /*  ###*/
00012 
00013 snit::type pregistry {
00014 
00015     /*  API*/
00016     /*  delete key ?attribute?*/
00017     /*  mtime  key ?attribute?*/
00018     /*  get    key attribute*/
00019     /*  keys   key ?pattern?/**/
00020     /*  set    key ?attribute value?*/
00021     /*  attrs  key ?pattern?*/
00022 
00023     option -tie -default {} -configureret  TIE ; # Persistence
00024 
00025     constructor (type args) {
00026     $self configurelist $args
00027     $self INIT
00028     return
00029     }
00030 
00031     /*  ###*/
00032 
00033     ret  delete (type key , type args) {
00034     #puts DEL|$key|
00035 
00036     if {[llength $args] > 1} {return -code error "wrong\#args"}
00037 
00038     if {[catch {NODE $key} n]} return
00039     if {[llength $args]} {
00040         # Delete attribute
00041 
00042         set attr    [lindex $args 0]
00043         set pattern [list A $n $attr *]
00044         set km      [list N $n M]
00045 
00046         array unset data $pattern
00047         set         data($km) [clock seconds]
00048     } else {
00049         # Delete key and children.
00050         #puts N|$n|
00051 
00052         if {![llength $key]} {
00053         return -code error "cannot delete root"
00054         }
00055 
00056         # Children first
00057         foreach c [array names data [list C $n *]] {
00058         set c [lindex $c end]
00059         #puts _|$c|
00060         $self delete [linsert $key end $c]
00061         }
00062 
00063         # And now the node itself. Modify the parent as well,
00064         # remove this node as a child.
00065 
00066         set self [lindex $key end]
00067         set pidx [list N $n P]
00068         set npat [list N $n *]
00069         set apat [list A $n * *]
00070 
00071         set pid  $data($pidx)
00072         set cidx [list C $pid $self]
00073         set midx [list N $pid M]
00074 
00075         array unset data $apat
00076         array unset data $npat
00077         unset -nocomplain data($cidx)
00078         set data($midx) [clock seconds]
00079 
00080         unset -nocomplain ncache($key)
00081     }
00082     return
00083     }
00084 
00085     ret  mtime (type key , type args) {
00086     if {[llength $args] > 1} {return -code error "wrong\#args"}
00087     set n [NODE $key]
00088     if {[llength $args]} {
00089         set attr [lindex $args 0]
00090         set idx  [list A $n $attr M]
00091         if {![info exists data($idx)]} {
00092         return -code error "Unknown attribute \"$attr\" in key \"$key\""
00093         }
00094     } else {
00095         set idx [list N $n M]
00096     }
00097     return $data($idx)
00098     }
00099 
00100     ret  exists (type key , type args) {
00101     if {[llength $args] > 1} {
00102         return -code error "wrong\#args"
00103     } elseif {[catch {NODE $key} n]} {
00104         return 0
00105     } elseif {![llength $args]} {
00106         return 1
00107     }
00108 
00109     set attr [lindex $args 0]
00110     set idx  [list A $n $attr V]
00111     return   [info exist data($idx)]
00112     }
00113 
00114     ret  get (type key , type attr) {
00115     set n   [NODE $key]
00116     set idx [list A $n $attr V]
00117     if {![info exists data($idx)]} {
00118         return -code error "Unknown attribute \"$attr\" in key \"$key\""
00119     }
00120     return $data($idx)
00121     }
00122 
00123     ret  get||default (type key , type attr , type default) {
00124     if {[catch {NODE $key} n]} {
00125         return $default
00126     }
00127     set idx [list A $n $attr V]
00128     if {![info exists data($idx)]} {
00129         return $default
00130     }
00131     return $data($idx)
00132     }
00133 
00134     ret  keys (type key , optional pattern =*) {
00135     set n       [NODE $key]
00136     set pattern [list C $n $pattern]
00137     set res {}
00138     foreach c [array names data $pattern] {
00139         lappend res [linsert $key end $c]
00140     }
00141     return $res
00142     }
00143 
00144     ret  attrs (type key , optional pattern =*) {
00145     set n       [NODE $key]
00146     set pattern [list A $n $pattern V]
00147     set res {}
00148     foreach c [array names data $pattern] {
00149         lappend res [lindex $c end-1]
00150     }
00151     return $res
00152     }
00153 
00154     ret  lappend (type key , type attr , type value) {
00155     set     list [$self get||default $key $attr {}]
00156     lappend list $value
00157     $self set $key $attr $list
00158     return
00159     }
00160 
00161     ret  set (type key , type args) {
00162     set n [NODE $key 1]
00163     if {![llength $args]} return
00164     if {[llength  $args] != 2} {return -code error "wrong\#args"}
00165     foreach {attr value} $args break
00166 
00167     # Ignore calls which do not change the contents of the
00168     # database.
00169 
00170     set aidx [list A $n $attr V]
00171     if {
00172         [info exists   data($aidx)] &&
00173         [string equal $data($aidx) $value]
00174     } return ; # {}
00175 
00176     #puts stderr "$n $attr | $key | ($value)"
00177 
00178     set aids [list A $n $attr M]
00179     set data($aidx) $value
00180     set data($aids) [clock seconds]
00181     return
00182     }
00183 
00184     /*  ### state*/
00185 
00186     variable data -array {}
00187 
00188     /*  Tree of keys. Each keys can have multiple attributes.*/
00189     /*  Each key, and attribute, have a modification timestamp.*/
00190 
00191     /*  Each node in the tree is identified by a numeric id. Children*/
00192     /*  refer to their parents. Parent id + name refers to unique child.*/
00193 
00194     /*  Array contents*/
00195 
00196     /*  (I)           -> number     id counter*/
00197     /*  (C id name)   -> id     parent id x name => child id*/
00198     /*  (N id P)      -> id     node id => parent id, empty for root*/
00199     /*  (N id M)      -> timestamp  node id => last modification*/
00200     /*  (A id name V) -> string     node id x attribute name => value*/
00201     /*  (A id name M) -> timestamp  s.a => last modification*/
00202 
00203     /*  This structure is less memory/space intensive than the setup of*/
00204     /*  1registry. It is also more difficult to query as it is less*/
00205     /*  tabular, less redundant.*/
00206 
00207     /*  Another thing becoming more complex is the deletion of a*/
00208     /*  subtree. It is now necessary to walk the the tree, instead of*/
00209     /*  just deleting all keys in the array matching a certain*/
00210     /*  pattern. That at least can be done at the C level (array unset).*/
00211 
00212     /*  The conversion from key list to node is also linear in key*/
00213     /*  length, and an operation done often. Better cache it. However*/
00214     /*  only internally, or the space savingsare gone too as the space*/
00215     /*  is then taken by the conversion cache. Hm. Still less than*/
00216     /*  before, as each key is listed at most once. In 1registry it was*/
00217     /*  repeated for each of its attributes as well. This would regain*/
00218     /*  speed for searches, as the conversion cache now is a tabular*/
00219     /*  representation of the tree, and easily globbed.*/
00220 
00221     /*  ### configure -tie (persistence)*/
00222 
00223     ret  TIE (type option , type value) {
00224     if {[string equal $options(-tie) $value]} return
00225     tie::untie [myvar data]
00226     # 8.5 - tie::tie [myvar data] {expand}$value
00227     eval [linsert $value 0 tie::tie [myvar data]]
00228     set options(-tie) $value
00229     return
00230     }
00231 
00232     ret  INIT () {
00233     if {![info exists data(I)]} {
00234         set anchor {C {} {}}
00235         set rootp  {N 0 P}
00236         set roots  {N 0 M}
00237 
00238         set data(I) 0
00239         set data($anchor) 0
00240         set data($rootp)  {}
00241         set data($roots)  [clock seconds]
00242     }
00243     return
00244     }
00245 
00246     variable ncache -array {}
00247 
00248     ret  NODE (type key , optional create =0) {
00249     upvar 1 ncache ncache data data
00250     if {[info exist ncache($key)]} {
00251         # Cached, shortcut
00252         return $ncache($key)
00253     }
00254     if {![llength $key]} {
00255         # Root, shortcut
00256         set id 0
00257     } else {
00258         # Recursively convert, possibly create
00259         set parent [lrange $key 0 end-1]
00260         set self   [lindex $key end]
00261         set pid    [NODE $parent $create]
00262         set idx    [list C $pid $self]
00263 
00264         if {[info exists data($idx)]} {
00265         set id $data($idx)
00266         } elseif {!$create} {
00267         return -code error "Unknown key \"$key\""
00268         } else {
00269         set id   [incr data(I)]
00270         set idxp [list N $id P]
00271         set idxm [list N $id M]
00272 
00273         set data($idx)  $id
00274         set data($idxp) $pid
00275         set data($idxm) [clock seconds]
00276         }
00277     }
00278     set ncache($key) $id
00279     return $id
00280     }
00281 
00282     /*  ###*/
00283 }
00284 
00285 /** 
00286  * ###
00287  */
00288 
00289 package provide pregistry 0.1
00290 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1