tree_tcl.tcl

Go to the documentation of this file.
00001 /*  tree.tcl --*/
00002 /* */
00003 /*  Implementation of a tree data structure for Tcl.*/
00004 /* */
00005 /*  Copyright (c) 1998-2000 by Ajuba Solutions.*/
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: tree_tcl.tcl,v 1.4 2005/10/27 21:56:04 andreas_kupries Exp $*/
00011 
00012 package require Tcl 8.2
00013 package require struct::list
00014 
00015 namespace ::struct::tree {
00016     /*  Data storage in the tree module*/
00017     /*  -------------------------------*/
00018     /* */
00019     /*  There's a lot of bits to keep track of for each tree:*/
00020     /*  nodes*/
00021     /*  node values*/
00022     /*  node relationships*/
00023     /* */
00024     /*  It would quickly become unwieldy to try to keep these in arrays or lists*/
00025     /*  within the tree namespace itself.  Instead, each tree structure will get*/
00026     /*  its own namespace.  Each namespace contains:*/
00027     /*  children    array mapping nodes to their children list*/
00028     /*  parent      array mapping nodes to their parent node*/
00029     /*  node:$node  array mapping keys to values for the node $node*/
00030 
00031     /*  counter is used to give a unique name for unnamed trees*/
00032     variable counter 0
00033 
00034     /*  Only export one command, the one used to instantiate a new tree*/
00035     namespace export tree_tcl
00036 }
00037 
00038 /*  ::struct::tree::tree_tcl --*/
00039 /* */
00040 /*  Create a new tree with a given name; if no name is given, use*/
00041 /*  treeX, where X is a number.*/
00042 /* */
00043 /*  Arguments:*/
00044 /*  name    Optional name of the tree; if null or not given, generate one.*/
00045 /* */
00046 /*  Results:*/
00047 /*  name    Name of the tree created*/
00048 
00049 ret  ::struct::tree::tree_tcl (type args) {
00050     variable counter
00051 
00052     set src     {}
00053     set srctype {}
00054 
00055     switch -exact -- [llength [info level 0]] {
00056     1 {
00057         # Missing name, generate one.
00058         incr counter
00059         set name "tree${counter}"
00060     }
00061     2 {
00062         # Standard call. New empty tree.
00063         set name [lindex $args 0]
00064     }
00065     4 {
00066         # Copy construction.
00067         foreach {name as src} $args break
00068         switch -exact -- $as {
00069         = - := - as {
00070             set srctype tree
00071         }
00072         deserialize {
00073             set srctype serial
00074         }
00075         default {
00076             return -code error \
00077                 "wrong # args: should be \"tree ?name ?=|:=|as|deserialize source??\""
00078         }
00079         }
00080     }
00081     default {
00082         # Error.
00083         return -code error \
00084             "wrong # args: should be \"tree ?name ?=|:=|as|deserialize source??\""
00085     }
00086     }
00087 
00088     # FIRST, qualify the name.
00089     if {![string match "::*" $name]} {
00090         # Get caller's namespace; append :: if not global namespace.
00091         set ns [uplevel 1 [list namespace current]]
00092         if {"::" != $ns} {
00093             append ns "::"
00094         }
00095 
00096         set name "$ns$name"
00097     }
00098     if {[llength [info commands $name]]} {
00099     return -code error \
00100         "command \"$name\" already exists, unable to create tree"
00101     }
00102 
00103     # Set up the namespace for the object,
00104     # identical to the object command.
00105     namespace eval $name {
00106     variable rootname
00107     set      rootname root
00108 
00109     # Set up root node's child list
00110     variable children
00111     set      children(root) [list]
00112 
00113     # Set root node's parent
00114     variable parent
00115     set      parent(root) [list]
00116 
00117     # Set up the node attribute mapping
00118     variable  attribute
00119     array set attribute {}
00120 
00121     # Set up a counter for use in creating unique node names
00122     variable nextUnusedNode
00123     set      nextUnusedNode 1
00124 
00125     # Set up a counter for use in creating node attribute arrays.
00126     variable nextAttr
00127     set      nextAttr 0
00128     }
00129 
00130     # Create the command to manipulate the tree
00131     interp alias {} $name {} ::struct::tree::TreeProc $name
00132 
00133     # Automatic execution of assignment if a source
00134     # is present.
00135     if {$src != {}} {
00136     switch -exact -- $srctype {
00137         tree   {
00138         set code [catch {_= $name $src} msg]
00139         if {$code} {
00140             namespace delete $name
00141             interp alias {} $name {}
00142             return -code $code -errorinfo $::errorInfo -errorcode $::errorCode $msg
00143         }
00144         }
00145         serial {
00146         set code [catch {_deserialize $name $src} msg]
00147         if {$code} {
00148             namespace delete $name
00149             interp alias {} $name {}
00150             return -code $code -errorinfo $::errorInfo -errorcode $::errorCode $msg
00151         }
00152         }
00153         default {
00154         return -code error \
00155             "Internal error, illegal srctype \"$srctype\""
00156         }
00157     }
00158     }
00159 
00160     # Give object to caller for use.
00161     return $name
00162 }
00163 
00164 /*  ::struct::tree::prune_tcl --*/
00165 /* */
00166 /*  Abort the walk script, and ignore any children of the*/
00167 /*  node we are currently at.*/
00168 /* */
00169 /*  Arguments:*/
00170 /*  None.*/
00171 /* */
00172 /*  Results:*/
00173 /*  None.*/
00174 /* */
00175 /*  Sideeffects:*/
00176 /* */
00177 /*  Stops the execution of the script and throws a signal to the*/
00178 /*  surrounding walker to go to the next node, and ignore the*/
00179 /*  children of the current node.*/
00180 
00181 ret  ::struct::tree::prune_tcl () {
00182     return -code 5
00183 }
00184 
00185 /* */
00186 /*  Private functions follow*/
00187 
00188 /*  ::struct::tree::TreeProc --*/
00189 /* */
00190 /*  Command that processes all tree object commands.*/
00191 /* */
00192 /*  Arguments:*/
00193 /*  name    Name of the tree object to manipulate.*/
00194 /*  cmd Subcommand to invoke.*/
00195 /*  args    Arguments for subcommand.*/
00196 /* */
00197 /*  Results:*/
00198 /*  Varies based on command to perform*/
00199 
00200 ret  ::struct::tree::TreeProc (type name , optional cmd ="" , type args) {
00201     # Do minimal args checks here
00202     if { [llength [info level 0]] == 2 } {
00203     return -code error "wrong # args: should be \"$name option ?arg arg ...?\""
00204     }
00205 
00206     # Split the args into command and args components
00207     set sub _$cmd
00208     if { [llength [info commands ::struct::tree::$sub]] == 0 } {
00209     set optlist [lsort [info commands ::struct::tree::_*]]
00210     set xlist {}
00211     foreach p $optlist {
00212         set p [namespace tail $p]
00213         lappend xlist [string range $p 1 end]
00214     }
00215     set optlist [linsert [join $xlist ", "] "end-1" "or"]
00216     return -code error \
00217         "bad option \"$cmd\": must be $optlist"
00218     }
00219 
00220     set code [catch {uplevel 1 [linsert $args 0 ::struct::tree::$sub $name]} result]
00221 
00222     if {$code == 1} {
00223     return -errorinfo [ErrorInfoAsCaller uplevel $sub]  \
00224         -errorcode $::errorCode -code error $result
00225     } elseif {$code == 2} {
00226     return -code $code $result
00227     }
00228     return $result
00229 }
00230 
00231 /*  ::struct::tree::_:= --*/
00232 /* */
00233 /*  Assignment operator. Copies the source tree into the*/
00234 /*        destination, destroying the original information.*/
00235 /* */
00236 /*  Arguments:*/
00237 /*  name    Name of the tree object we are copying into.*/
00238 /*  source  Name of the tree object providing us with the*/
00239 /*      data to copy.*/
00240 /* */
00241 /*  Results:*/
00242 /*  Nothing.*/
00243 
00244 ret  ::struct::tree::_= (type name , type source) {
00245     _deserialize $name [$source serialize]
00246     return
00247 }
00248 
00249 /*  ::struct::tree::_--> --*/
00250 /* */
00251 /*  Reverse assignment operator. Copies this tree into the*/
00252 /*        destination, destroying the original information.*/
00253 /* */
00254 /*  Arguments:*/
00255 /*  name    Name of the tree object to copy*/
00256 /*  dest    Name of the tree object we are copying to.*/
00257 /* */
00258 /*  Results:*/
00259 /*  Nothing.*/
00260 
00261 ret  ::struct::tree::_--> (type name , type dest) {
00262     $dest deserialize [_serialize $name]
00263     return
00264 }
00265 
00266 /*  ::struct::tree::_ancestors --*/
00267 /* */
00268 /*  Return the list of all parent nodes of a node in a tree.*/
00269 /* */
00270 /*  Arguments:*/
00271 /*  name    Name of the tree.*/
00272 /*  node    Node to look up.*/
00273 /* */
00274 /*  Results:*/
00275 /*  parents List of parents of node $node.*/
00276 /*      Immediate ancestor (parent) first,*/
00277 /*      Root of tree (ancestor of all) last.*/
00278 
00279 ret  ::struct::tree::_ancestors (type name , type node) {
00280     if { ![_exists $name $node] } {
00281     return -code error "node \"$node\" does not exist in tree \"$name\""
00282     }
00283 
00284     variable ${name}::parent
00285     set a {}
00286     while {[info exists parent($node)]} {
00287     set node $parent($node)
00288     if {$node == {}} break
00289     lappend a $node
00290     }
00291     return $a
00292 }
00293 
00294 /*  ::struct::tree::_attr --*/
00295 /* */
00296 /*  Return attribute data for one key and multiple nodes, possibly all.*/
00297 /* */
00298 /*  Arguments:*/
00299 /*  name    Name of the tree object.*/
00300 /*  key Name of the attribute to retrieve.*/
00301 /* */
00302 /*  Results:*/
00303 /*  children    Dictionary mapping nodes to attribute data.*/
00304 
00305 ret  ::struct::tree::_attr (type name , type key , type args) {
00306     # Syntax:
00307     #
00308     # t attr key
00309     # t attr key -nodes {nodelist}
00310     # t attr key -glob nodepattern
00311     # t attr key -regexp nodepattern
00312 
00313     variable ${name}::attribute
00314 
00315     set usage "wrong # args: should be \"[list $name] attr key ?-nodes list|-glob pattern|-regexp pattern?\""
00316     if {([llength $args] != 0) && ([llength $args] != 2)} {
00317     return -code error $usage
00318     } elseif {[llength $args] == 0} {
00319     # This automatically restricts the list
00320     # to nodes which can have the attribute
00321     # in question.
00322 
00323     set nodes [array names attribute]
00324     } else {
00325     # Determine a list of nodes to look at
00326     # based on the chosen restriction.
00327 
00328     foreach {mode value} $args break
00329     switch -exact -- $mode {
00330         -nodes {
00331         # This is the only branch where we have to
00332         # perform an explicit restriction to the
00333         # nodes which have attributes.
00334         set nodes {}
00335         foreach n $value {
00336             if {![info exists attribute($n)]} continue
00337             lappend nodes $n
00338         }
00339         }
00340         -glob {
00341         set nodes [array names attribute $value]
00342         }
00343         -regexp {
00344         set nodes {}
00345         foreach n [array names attribute] {
00346             if {![regexp -- $value $n]} continue
00347             lappend nodes $n
00348         }
00349         }
00350         default {
00351         return -code error $usage
00352         }
00353     }
00354     }
00355 
00356     # Without possibly matching nodes
00357     # the result has to be empty.
00358 
00359     if {![llength $nodes]} {
00360     return {}
00361     }
00362 
00363     # Now locate matching keys and their values.
00364 
00365     set result {}
00366     foreach n $nodes {
00367     upvar ${name}::$attribute($n) data
00368     if {[info exists data($key)]} {
00369         lappend result $n $data($key)
00370     }
00371     }
00372 
00373     return $result
00374 }
00375 
00376 /*  ::struct::tree::_deserialize --*/
00377 /* */
00378 /*  Assignment operator. Copies a serialization into the*/
00379 /*        destination, destroying the original information.*/
00380 /* */
00381 /*  Arguments:*/
00382 /*  name    Name of the tree object we are copying into.*/
00383 /*  serial  Serialized tree to copy from.*/
00384 /* */
00385 /*  Results:*/
00386 /*  Nothing.*/
00387 
00388 ret  ::struct::tree::_deserialize (type name , type serial) {
00389     # As we destroy the original tree as part of
00390     # the copying process we don't have to deal
00391     # with issues like node names from the new tree
00392     # interfering with the old ...
00393 
00394     # I. Get the serialization of the source tree
00395     #    and check it for validity.
00396 
00397     CheckSerialization $serial attr p c rn
00398 
00399     # Get all the relevant data into the scope
00400 
00401     variable ${name}::rootname
00402     variable ${name}::children
00403     variable ${name}::parent
00404     variable ${name}::attribute
00405     variable ${name}::nextAttr
00406 
00407     # Kill the existing parent/children information and insert the new
00408     # data in their place.
00409 
00410     foreach n [array names parent] {
00411     unset parent($n) children($n)
00412     }
00413     array set parent   [array get p]
00414     array set children [array get c]
00415     unset p c
00416 
00417     set nextAttr 0
00418     foreach a [array names attribute] {
00419     unset ${name}::$attribute($a)
00420     }
00421     foreach n [array names attr] {
00422     GenAttributeStorage $name $n
00423     array set ${name}::$attribute($n) $attr($n)
00424     }
00425 
00426     set rootname $rn
00427 
00428     ## Debug ## Dump internals ...
00429     if {0} {
00430     puts "___________________________________ $name"
00431     puts $rootname
00432     parray children
00433     parray parent
00434     parray attribute
00435     puts ___________________________________
00436     }
00437     return
00438 }
00439 
00440 /*  ::struct::tree::_children --*/
00441 /* */
00442 /*  Return the list of children for a given node of a tree.*/
00443 /* */
00444 /*  Arguments:*/
00445 /*  name    Name of the tree object.*/
00446 /*  node    Node to look up.*/
00447 /* */
00448 /*  Results:*/
00449 /*  children    List of children for the node.*/
00450 
00451 ret  ::struct::tree::_children (type name , type args) {
00452     # args := ?-all? node ?filter cmdprefix?
00453 
00454     # '-all' implies that not only the direct children of the
00455     # node, but all their children, and so on, are returned.
00456     #
00457     # 'filter cmd' implies that only those nodes in the result list
00458     # which pass the test 'cmd' are placed into the final result. 
00459 
00460     set usage "wrong # args: should be \"[list $name] children ?-all? node ?filter cmd?\""
00461 
00462     if {([llength $args] < 1) || ([llength $args] > 4)} {
00463     return -code error $usage
00464     }
00465     if {[string equal [lindex $args 0] -all]} {
00466     set all 1
00467     set args [lrange $args 1 end]
00468     } else {
00469     set all 0
00470     }
00471 
00472     # args := node ?filter cmdprefix?
00473 
00474     if {([llength $args] != 1) && ([llength $args] != 3)} {
00475     return -code error $usage
00476     }
00477     if {[llength $args] == 3} {
00478     foreach {node _const_ cmd} $args break
00479     if {![string equal $_const_ filter] || ![llength $cmd]} {
00480         return -code error $usage
00481     }
00482     } else {
00483     set node [lindex $args 0]
00484     set cmd {}
00485     }
00486 
00487     if { ![_exists $name $node] } {
00488     return -code error "node \"$node\" does not exist in tree \"$name\""
00489     }
00490 
00491     if {$all} {
00492     set result [DescendantsCore $name $node]
00493     } else {
00494     variable ${name}::children
00495     set result $children($node)
00496     }
00497 
00498     if {[llength $cmd]} {
00499     lappend cmd $name
00500     set result [uplevel 1 [list ::struct::list filter $result $cmd]]
00501     }
00502 
00503     return $result
00504 }
00505 
00506 /*  ::struct::tree::_cut --*/
00507 /* */
00508 /*  Destroys the specified node of a tree, but not its children.*/
00509 /*  These children are made into children of the parent of the*/
00510 /*  destroyed node at the index of the destroyed node.*/
00511 /* */
00512 /*  Arguments:*/
00513 /*  name    Name of the tree object.*/
00514 /*  node    Node to look up and cut.*/
00515 /* */
00516 /*  Results:*/
00517 /*  None.*/
00518 
00519 ret  ::struct::tree::_cut (type name , type node) {
00520     variable ${name}::rootname
00521 
00522     if { [string equal $node $rootname] } {
00523     # Can't delete the special root node
00524     return -code error "cannot cut root node"
00525     }
00526 
00527     if { ![_exists $name $node] } {
00528     return -code error "node \"$node\" does not exist in tree \"$name\""
00529     }
00530 
00531     variable ${name}::parent
00532     variable ${name}::children
00533 
00534     # Locate our parent, children and our location in the parent
00535     set parentNode $parent($node)
00536     set childNodes $children($node)
00537 
00538     set index [lsearch -exact $children($parentNode) $node]
00539 
00540     # Excise this node from the parent list,
00541     set newChildren [lreplace $children($parentNode) $index $index]
00542 
00543     # Put each of the children of $node into the parent's children list,
00544     # in the place of $node, and update the parent pointer of those nodes.
00545     foreach child $childNodes {
00546     set newChildren [linsert $newChildren $index $child]
00547     set parent($child) $parentNode
00548     incr index
00549     }
00550     set children($parentNode) $newChildren
00551 
00552     KillNode $name $node
00553     return
00554 }
00555 
00556 /*  ::struct::tree::_delete --*/
00557 /* */
00558 /*  Remove a node from a tree, including all of its values.  Recursively*/
00559 /*  removes the node's children.*/
00560 /* */
00561 /*  Arguments:*/
00562 /*  name    Name of the tree.*/
00563 /*  node    Node to delete.*/
00564 /* */
00565 /*  Results:*/
00566 /*  None.*/
00567 
00568 ret  ::struct::tree::_delete (type name , type node) {
00569     variable ${name}::rootname
00570     if { [string equal $node $rootname] } {
00571     # Can't delete the special root node
00572     return -code error "cannot delete root node"
00573     }
00574     if {![_exists $name $node]} {
00575     return -code error "node \"$node\" does not exist in tree \"$name\""
00576     }
00577 
00578     variable ${name}::children
00579     variable ${name}::parent
00580 
00581     # Remove this node from its parent's children list
00582     set parentNode $parent($node)
00583     set index [lsearch -exact $children($parentNode) $node]
00584     ldelete children($parentNode) $index
00585 
00586     # Yes, we could use the stack structure implemented in ::struct::stack,
00587     # but it's slower than inlining it.  Since we don't need a sophisticated
00588     # stack, don't bother.
00589     set st [list]
00590     foreach child $children($node) {
00591     lappend st $child
00592     }
00593 
00594     KillNode $name $node
00595 
00596     while {[llength $st] > 0} {
00597     set node [lindex $st end]
00598     ldelete           st end
00599     foreach child $children($node) {
00600         lappend st $child
00601     }
00602 
00603     KillNode $name $node
00604     }
00605     return
00606 }
00607 
00608 /*  ::struct::tree::_depth --*/
00609 /* */
00610 /*  Return the depth (distance from the root node) of a given node.*/
00611 /* */
00612 /*  Arguments:*/
00613 /*  name    Name of the tree.*/
00614 /*  node    Node to find.*/
00615 /* */
00616 /*  Results:*/
00617 /*  depth   Number of steps from node to the root node.*/
00618 
00619 ret  ::struct::tree::_depth (type name , type node) {
00620     if { ![_exists $name $node] } {
00621     return -code error "node \"$node\" does not exist in tree \"$name\""
00622     }
00623     variable ${name}::parent
00624     variable ${name}::rootname
00625     set depth 0
00626     while { ![string equal $node $rootname] } {
00627     incr depth
00628     set node $parent($node)
00629     }
00630     return $depth
00631 }
00632 
00633 /*  ::struct::tree::_descendants --*/
00634 /* */
00635 /*  Return the list containing all descendants of a node in a tree.*/
00636 /* */
00637 /*  Arguments:*/
00638 /*  name    Name of the tree.*/
00639 /*  node    Node to look at.*/
00640 /* */
00641 /*  Results:*/
00642 /*  desc    (filtered) List of nodes descending from 'node'.*/
00643 
00644 ret  ::struct::tree::_descendants (type name , type node , type args) {
00645     # children -all sucessor, allows filtering.
00646 
00647     set usage "wrong # args: should be \"[list $name] descendants node ?filter cmd?\""
00648 
00649     if {[llength $args] > 2} {
00650     return -code error $usage
00651     } elseif {[llength $args] == 2} {
00652     foreach {_const_ cmd} $args break
00653     if {![string equal $_const_ filter] || ![llength $cmd]} {
00654         return -code error $usage
00655     }
00656     } else {
00657     set cmd {}
00658     }
00659 
00660     if { ![_exists $name $node] } {
00661     return -code error "node \"$node\" does not exist in tree \"$name\""
00662     }
00663 
00664     set result [DescendantsCore $name $node]
00665 
00666     if {[llength $cmd]} {
00667     lappend cmd $name
00668     set result [uplevel 1 [list ::struct::list filter $result $cmd]]
00669     }
00670 
00671     return $result
00672 }
00673 
00674 ret  ::struct::tree::DescendantsCore (type name , type node) {
00675     # CORE for listing of node descendants.
00676     # No checks ...
00677     # No filtering ...
00678 
00679     variable ${name}::children
00680 
00681     # New implementation. Instead of keeping a second, and explicit,
00682     # list of pending nodes to shift through (= copying of array data
00683     # around), we reuse the result list for that, using a counter and
00684     # direct access to list elements to keep track of what nodes have
00685     # not been handled yet. This eliminates a whole lot of array
00686     # copying within the list implementation in the Tcl core. The
00687     # result is unchanged, i.e. the nodes are in the same order as
00688     # before.
00689 
00690     set result  $children($node)
00691     set at      0
00692 
00693     while {$at < [llength $result]} {
00694     set n [lindex $result $at]
00695     incr at
00696     foreach c $children($n) {
00697         lappend result $c
00698     }
00699     }
00700 
00701     return $result
00702 }
00703 
00704 /*  ::struct::tree::_destroy --*/
00705 /* */
00706 /*  Destroy a tree, including its associated command and data storage.*/
00707 /* */
00708 /*  Arguments:*/
00709 /*  name    Name of the tree to destroy.*/
00710 /* */
00711 /*  Results:*/
00712 /*  None.*/
00713 
00714 ret  ::struct::tree::_destroy (type name) {
00715     namespace delete $name
00716     interp alias {} $name {}
00717 }
00718 
00719 /*  ::struct::tree::_exists --*/
00720 /* */
00721 /*  Test for existence of a given node in a tree.*/
00722 /* */
00723 /*  Arguments:*/
00724 /*  name    Name of the tree to query.*/
00725 /*  node    Node to look for.*/
00726 /* */
00727 /*  Results:*/
00728 /*  1 if the node exists, 0 else.*/
00729 
00730 ret  ::struct::tree::_exists (type name , type node) {
00731     return [info exists ${name}::parent($node)]
00732 }
00733 
00734 /*  ::struct::tree::_get --*/
00735 /* */
00736 /*  Get a keyed value from a node in a tree.*/
00737 /* */
00738 /*  Arguments:*/
00739 /*  name    Name of the tree.*/
00740 /*  node    Node to query.*/
00741 /*  key Key to lookup.*/
00742 /* */
00743 /*  Results:*/
00744 /*  value   Value associated with the key given.*/
00745 
00746 ret  ::struct::tree::_get (type name , type node , type key) {
00747     if {![_exists $name $node]} {
00748     return -code error "node \"$node\" does not exist in tree \"$name\""
00749     }
00750 
00751     variable ${name}::attribute
00752     if {![info exists attribute($node)]} {
00753     # No attribute data for this node, key has to be invalid.
00754     return -code error "invalid key \"$key\" for node \"$node\""
00755     }
00756 
00757     upvar ${name}::$attribute($node) data
00758     if {![info exists data($key)]} {
00759     return -code error "invalid key \"$key\" for node \"$node\""
00760     }
00761     return $data($key)
00762 }
00763 
00764 /*  ::struct::tree::_getall --*/
00765 /* */
00766 /*  Get a serialized list of key/value pairs from a node in a tree.*/
00767 /* */
00768 /*  Arguments:*/
00769 /*  name    Name of the tree.*/
00770 /*  node    Node to query.*/
00771 /* */
00772 /*  Results:*/
00773 /*  value   A serialized list of key/value pairs.*/
00774 
00775 ret  ::struct::tree::_getall (type name , type node , optional pattern =*) {
00776     if {![_exists $name $node]} {
00777     return -code error "node \"$node\" does not exist in tree \"$name\""
00778     }
00779 
00780     variable ${name}::attribute
00781     if {![info exists attribute($node)]} {
00782     # No attributes ...
00783     return {}
00784     }
00785 
00786     upvar ${name}::$attribute($node) data
00787     return [array get data $pattern]
00788 }
00789 
00790 /*  ::struct::tree::_height --*/
00791 /* */
00792 /*  Return the height (distance from the given node to its deepest child)*/
00793 /* */
00794 /*  Arguments:*/
00795 /*  name    Name of the tree.*/
00796 /*  node    Node we wish to know the height for..*/
00797 /* */
00798 /*  Results:*/
00799 /*  height  Distance to deepest child of the node.*/
00800 
00801 ret  ::struct::tree::_height (type name , type node) {
00802     if { ![_exists $name $node] } {
00803     return -code error "node \"$node\" does not exist in tree \"$name\""
00804     }
00805 
00806     variable ${name}::children
00807     variable ${name}::parent
00808 
00809     if {[llength $children($node)] == 0} {
00810     # No children, is a leaf, height is 0.
00811     return 0
00812     }
00813 
00814     # New implementation. We iteratively compute the height for each
00815     # node under the specified one, from the bottom up. The previous
00816     # implementation, using recursion will fail if the encountered
00817     # subtree has a height greater than the currently set recursion
00818     # limit.
00819 
00820     array set h {}
00821 
00822     # NOTE: Check out if a for loop doing direct access, i.e. without
00823     #       list reversal, is faster.
00824 
00825     foreach n [struct::list reverse [DescendantsCore $name $node]] {
00826     # Height of leafs
00827     if {![llength $children($n)]} {set h($n) 0}
00828 
00829     # Height of our parent is max of our and previous height.
00830     set p $parent($n)
00831     if {![info exists h($p)] || ($h($n) >= $h($p))} {
00832         set h($p) [expr {$h($n) + 1}]
00833     }
00834     }
00835 
00836     # NOTE: Check out how much we gain by caching the result.
00837     #       For all nodes we have this computed. Use cache here
00838     #       as well to cut the inspection of descendants down.
00839     #       This may degenerate into a recursive solution again
00840     #       however.
00841 
00842     return $h($node)
00843 }
00844 
00845 /*  ::struct::tree::_keys --*/
00846 /* */
00847 /*  Get a list of keys from a node in a tree.*/
00848 /* */
00849 /*  Arguments:*/
00850 /*  name    Name of the tree.*/
00851 /*  node    Node to query.*/
00852 /* */
00853 /*  Results:*/
00854 /*  value   A serialized list of key/value pairs.*/
00855 
00856 ret  ::struct::tree::_keys (type name , type node , optional pattern =*) {
00857     if {![_exists $name $node]} {
00858     return -code error "node \"$node\" does not exist in tree \"$name\""
00859     }
00860 
00861     variable ${name}::attribute
00862     if {![info exists attribute($node)]} {
00863     # No attribute data for this node.
00864     return {}
00865     }
00866 
00867     upvar ${name}::$attribute($node) data
00868     return [array names data $pattern]
00869 }
00870 
00871 /*  ::struct::tree::_keyexists --*/
00872 /* */
00873 /*  Test for existence of a given key for a node in a tree.*/
00874 /* */
00875 /*  Arguments:*/
00876 /*  name    Name of the tree.*/
00877 /*  node    Node to query.*/
00878 /*  key Key to lookup.*/
00879 /* */
00880 /*  Results:*/
00881 /*  1 if the key exists, 0 else.*/
00882 
00883 ret  ::struct::tree::_keyexists (type name , type node , type key) {
00884     if {![_exists $name $node]} {
00885     return -code error "node \"$node\" does not exist in tree \"$name\""
00886     }
00887 
00888     variable ${name}::attribute
00889     if {![info exists attribute($node)]} {
00890     # No attribute data for this node, key cannot exist
00891     return 0
00892     }
00893 
00894     upvar ${name}::$attribute($node) data
00895     return [info exists data($key)]
00896 }
00897 
00898 /*  ::struct::tree::_index --*/
00899 /* */
00900 /*  Determine the index of node with in its parent's list of children.*/
00901 /* */
00902 /*  Arguments:*/
00903 /*  name    Name of the tree.*/
00904 /*  node    Node to look up.*/
00905 /* */
00906 /*  Results:*/
00907 /*  index   The index of the node in its parent*/
00908 
00909 ret  ::struct::tree::_index (type name , type node) {
00910     variable ${name}::rootname
00911     if { [string equal $node $rootname] } {
00912     # The special root node has no parent, thus no index in it either.
00913     return -code error "cannot determine index of root node"
00914     }
00915 
00916     if { ![_exists $name $node] } {
00917     return -code error "node \"$node\" does not exist in tree \"$name\""
00918     }
00919 
00920     variable ${name}::children
00921     variable ${name}::parent
00922 
00923     # Locate the parent and ourself in its list of children
00924     set parentNode $parent($node)
00925 
00926     return [lsearch -exact $children($parentNode) $node]
00927 }
00928 
00929 /*  ::struct::tree::_insert --*/
00930 /* */
00931 /*  Add a node to a tree; if the node(s) specified already exist, they*/
00932 /*  will be moved to the given location.*/
00933 /* */
00934 /*  Arguments:*/
00935 /*  name        Name of the tree.*/
00936 /*  parentNode  Parent to add the node to.*/
00937 /*  index       Index at which to insert.*/
00938 /*  args        Node(s) to insert.  If none is given, the routine*/
00939 /*          will insert a single node with a unique name.*/
00940 /* */
00941 /*  Results:*/
00942 /*  nodes       List of nodes inserted.*/
00943 
00944 ret  ::struct::tree::_insert (type name , type parentNode , type index , type args) {
00945     if { [llength $args] == 0 } {
00946     # No node name was given; generate a unique one
00947     set args [list [GenerateUniqueNodeName $name]]
00948     }
00949     if { ![_exists $name $parentNode] } {
00950     return -code error "parent node \"$parentNode\" does not exist in tree \"$name\""
00951     }
00952 
00953     variable ${name}::parent
00954     variable ${name}::children
00955     variable ${name}::rootname
00956 
00957     # Make sure the index is numeric
00958 
00959     if {[string equal $index "end"]} {
00960     set index [llength $children($parentNode)]
00961     } elseif {[regexp {^end-([0-9]+)$} $index -> n]} {
00962     set index [expr {[llength $children($parentNode)] - $n}]
00963     }
00964 
00965     foreach node $args {
00966     if {[_exists $name $node] } {
00967         # Move the node to its new home
00968         if { [string equal $node $rootname] } {
00969         return -code error "cannot move root node"
00970         }
00971     
00972         # Cannot make a node its own descendant (I'm my own grandpa...)
00973         set ancestor $parentNode
00974         while { ![string equal $ancestor $rootname] } {
00975         if { [string equal $ancestor $node] } {
00976             return -code error "node \"$node\" cannot be its own descendant"
00977         }
00978         set ancestor $parent($ancestor)
00979         }
00980         # Remove this node from its parent's children list
00981         set oldParent $parent($node)
00982         set ind [lsearch -exact $children($oldParent) $node]
00983         ldelete children($oldParent) $ind
00984     
00985         # If the node is moving within its parent, and its old location
00986         # was before the new location, decrement the new location, so that
00987         # it gets put in the right spot
00988         if { [string equal $oldParent $parentNode] && $ind < $index } {
00989         incr index -1
00990         }
00991     } else {
00992         # Set up the new node
00993         set children($node) [list]
00994     }
00995 
00996     # Add this node to its parent's children list
00997     set children($parentNode) [linsert $children($parentNode) $index $node]
00998 
00999     # Update the parent pointer for this node
01000     set parent($node) $parentNode
01001     incr index
01002     }
01003 
01004     return $args
01005 }
01006 
01007 /*  ::struct::tree::_isleaf --*/
01008 /* */
01009 /*  Return whether the given node of a tree is a leaf or not.*/
01010 /* */
01011 /*  Arguments:*/
01012 /*  name    Name of the tree object.*/
01013 /*  node    Node to look up.*/
01014 /* */
01015 /*  Results:*/
01016 /*  isleaf  True if the node is a leaf; false otherwise.*/
01017 
01018 ret  ::struct::tree::_isleaf (type name , type node) {
01019     if { ![_exists $name $node] } {
01020     return -code error "node \"$node\" does not exist in tree \"$name\""
01021     }
01022 
01023     variable ${name}::children
01024     return [expr {[llength $children($node)] == 0}]
01025 }
01026 
01027 /*  ::struct::tree::_move --*/
01028 /* */
01029 /*  Move a node (and all its subnodes) from where ever it is to a new*/
01030 /*  location in the tree.*/
01031 /* */
01032 /*  Arguments:*/
01033 /*  name        Name of the tree*/
01034 /*  parentNode  Parent to add the node to.*/
01035 /*  index       Index at which to insert.*/
01036 /*  node        Node to move; the node must exist in the tree.*/
01037 /*  args        Additional nodes to move; these nodes must exist*/
01038 /*          in the tree.*/
01039 /* */
01040 /*  Results:*/
01041 /*  None.*/
01042 
01043 ret  ::struct::tree::_move (type name , type parentNode , type index , type node , type args) {
01044     set args [linsert $args 0 $node]
01045 
01046     # Can only move a node to a real location in the tree
01047     if { ![_exists $name $parentNode] } {
01048     return -code error "parent node \"$parentNode\" does not exist in tree \"$name\""
01049     }
01050 
01051     variable ${name}::parent
01052     variable ${name}::children
01053     variable ${name}::rootname
01054 
01055     # Make sure the index is numeric
01056 
01057     if {[string equal $index "end"]} {
01058     set index [llength $children($parentNode)]
01059     } elseif {[regexp {^end-([0-9]+)$} $index -> n]} {
01060     set index [expr {[llength $children($parentNode)] - $n}]
01061     }
01062 
01063     # Validate all nodes to move before trying to move any.
01064     foreach node $args {
01065     if { [string equal $node $rootname] } {
01066         return -code error "cannot move root node"
01067     }
01068 
01069     # Can only move real nodes
01070     if { ![_exists $name $node] } {
01071         return -code error "node \"$node\" does not exist in tree \"$name\""
01072     }
01073 
01074     # Cannot move a node to be a descendant of itself
01075     set ancestor $parentNode
01076     while { ![string equal $ancestor $rootname] } {
01077         if { [string equal $ancestor $node] } {
01078         return -code error "node \"$node\" cannot be its own descendant"
01079         }
01080         set ancestor $parent($ancestor)
01081     }
01082     }
01083 
01084     # Remove all nodes from their current parent's children list
01085     foreach node $args {
01086     set oldParent $parent($node)
01087     set ind [lsearch -exact $children($oldParent) $node]
01088 
01089     ldelete children($oldParent) $ind
01090 
01091     # Update the nodes parent value
01092     set parent($node) $parentNode
01093     }
01094 
01095     # Add all nodes to their new parent's children list
01096     set children($parentNode) \
01097     [eval [list linsert $children($parentNode) $index] $args]
01098 
01099     return
01100 }
01101 
01102 /*  ::struct::tree::_next --*/
01103 /* */
01104 /*  Return the right sibling for a given node of a tree.*/
01105 /* */
01106 /*  Arguments:*/
01107 /*  name        Name of the tree object.*/
01108 /*  node        Node to retrieve right sibling for.*/
01109 /* */
01110 /*  Results:*/
01111 /*  sibling     The right sibling for the node, or null if node was*/
01112 /*          the rightmost child of its parent.*/
01113 
01114 ret  ::struct::tree::_next (type name , type node) {
01115     # The 'root' has no siblings.
01116     variable ${name}::rootname
01117     if { [string equal $node $rootname] } {
01118     return {}
01119     }
01120 
01121     if { ![_exists $name $node] } {
01122     return -code error "node \"$node\" does not exist in tree \"$name\""
01123     }
01124 
01125     # Locate the parent and our place in its list of children.
01126     variable ${name}::parent
01127     variable ${name}::children
01128 
01129     set parentNode $parent($node)
01130     set  index [lsearch -exact $children($parentNode) $node]
01131 
01132     # Go to the node to the right and return its name.
01133     return [lindex $children($parentNode) [incr index]]
01134 }
01135 
01136 /*  ::struct::tree::_numchildren --*/
01137 /* */
01138 /*  Return the number of immediate children for a given node of a tree.*/
01139 /* */
01140 /*  Arguments:*/
01141 /*  name        Name of the tree object.*/
01142 /*  node        Node to look up.*/
01143 /* */
01144 /*  Results:*/
01145 /*  numchildren Number of immediate children for the node.*/
01146 
01147 ret  ::struct::tree::_numchildren (type name , type node) {
01148     if { ![_exists $name $node] } {
01149     return -code error "node \"$node\" does not exist in tree \"$name\""
01150     }
01151 
01152     variable ${name}::children
01153     return [llength $children($node)]
01154 }
01155 
01156 /*  ::struct::tree::_nodes --*/
01157 /* */
01158 /*  Return a list containing all nodes known to the tree.*/
01159 /* */
01160 /*  Arguments:*/
01161 /*  name        Name of the tree object.*/
01162 /* */
01163 /*  Results:*/
01164 /*  nodes   List of nodes in the tree.*/
01165 
01166 ret  ::struct::tree::_nodes (type name) {
01167     variable ${name}::children
01168     return [array names children]
01169 }
01170 
01171 /*  ::struct::tree::_parent --*/
01172 /* */
01173 /*  Return the name of the parent node of a node in a tree.*/
01174 /* */
01175 /*  Arguments:*/
01176 /*  name    Name of the tree.*/
01177 /*  node    Node to look up.*/
01178 /* */
01179 /*  Results:*/
01180 /*  parent  Parent of node $node*/
01181 
01182 ret  ::struct::tree::_parent (type name , type node) {
01183     if { ![_exists $name $node] } {
01184     return -code error "node \"$node\" does not exist in tree \"$name\""
01185     }
01186     # FRINK: nocheck
01187     return [set ${name}::parent($node)]
01188 }
01189 
01190 /*  ::struct::tree::_previous --*/
01191 /* */
01192 /*  Return the left sibling for a given node of a tree.*/
01193 /* */
01194 /*  Arguments:*/
01195 /*  name        Name of the tree object.*/
01196 /*  node        Node to look up.*/
01197 /* */
01198 /*  Results:*/
01199 /*  sibling     The left sibling for the node, or null if node was*/
01200 /*          the leftmost child of its parent.*/
01201 
01202 ret  ::struct::tree::_previous (type name , type node) {
01203     # The 'root' has no siblings.
01204     variable ${name}::rootname
01205     if { [string equal $node $rootname] } {
01206     return {}
01207     }
01208 
01209     if { ![_exists $name $node] } {
01210     return -code error "node \"$node\" does not exist in tree \"$name\""
01211     }
01212 
01213     # Locate the parent and our place in its list of children.
01214     variable ${name}::parent
01215     variable ${name}::children
01216 
01217     set parentNode $parent($node)
01218     set  index [lsearch -exact $children($parentNode) $node]
01219 
01220     # Go to the node to the right and return its name.
01221     return [lindex $children($parentNode) [incr index -1]]
01222 }
01223 
01224 /*  ::struct::tree::_rootname --*/
01225 /* */
01226 /*  Query or change the name of the root node.*/
01227 /* */
01228 /*  Arguments:*/
01229 /*  name    Name of the tree.*/
01230 /* */
01231 /*  Results:*/
01232 /*  The name of the root node*/
01233 
01234 ret  ::struct::tree::_rootname (type name) {
01235     variable ${name}::rootname
01236     return $rootname
01237 }
01238 
01239 /*  ::struct::tree::_rename --*/
01240 /* */
01241 /*  Change the name of any node.*/
01242 /* */
01243 /*  Arguments:*/
01244 /*  name    Name of the tree.*/
01245 /*  node    Name of node to be renamed*/
01246 /*  newname New name for the node.*/
01247 /* */
01248 /*  Results:*/
01249 /*  The new name of the node.*/
01250 
01251 ret  ::struct::tree::_rename (type name , type node , type newname) {
01252     if { ![_exists $name $node] } {
01253     return -code error "node \"$node\" does not exist in tree \"$name\""
01254     }
01255     if {[_exists $name $newname]} {
01256     return -code error "unable to rename node to \"$newname\",\
01257         node of that name already present in the tree \"$name\""
01258     }
01259 
01260     set oldname  $node
01261 
01262     # Perform the rename in the internal
01263     # data structures.
01264 
01265     variable ${name}::rootname
01266     variable ${name}::children
01267     variable ${name}::parent
01268     variable ${name}::attribute
01269 
01270     set children($newname) $children($oldname)
01271     unset                   children($oldname)
01272     set parent($newname)     $parent($oldname)
01273     unset                     parent($oldname)
01274 
01275     foreach c $children($newname) {
01276     set parent($c) $newname
01277     }
01278 
01279     if {[string equal $oldname $rootname]} {
01280     set rootname $newname
01281     } else {
01282     set p $parent($newname)
01283     set pos  [lsearch -exact $children($p) $oldname]
01284     lset children($p) $pos $newname
01285     }
01286 
01287     if {[info exists attribute($oldname)]} {
01288     set attribute($newname) $attribute($oldname)
01289     unset                    attribute($oldname)
01290     }
01291 
01292     return $newname
01293 }
01294 
01295 /*  ::struct::tree::_serialize --*/
01296 /* */
01297 /*  Serialize a tree object (partially) into a transportable value.*/
01298 /* */
01299 /*  Arguments:*/
01300 /*  name    Name of the tree.*/
01301 /*  node    Root node of the serialized tree.*/
01302 /* */
01303 /*  Results:*/
01304 /*  A list structure describing the part of the tree which was serialized.*/
01305 
01306 ret  ::struct::tree::_serialize (type name , type args) {
01307     if {[llength $args] > 1} {
01308     return -code error \
01309         "wrong # args: should be \"[list $name] serialize ?node?\""
01310     } elseif {[llength $args] == 1} {
01311     set node [lindex $args 0]
01312 
01313     if {![_exists $name $node]} {
01314         return -code error "node \"$node\" does not exist in tree \"$name\""
01315     }
01316     } else {
01317     variable ${name}::rootname
01318     set node $rootname
01319     }
01320 
01321     set                   tree [list]
01322     Serialize $name $node tree
01323     return               $tree
01324 }
01325 
01326 /*  ::struct::tree::_set --*/
01327 /* */
01328 /*  Set or get a value for a node in a tree.*/
01329 /* */
01330 /*  Arguments:*/
01331 /*  name    Name of the tree.*/
01332 /*  node    Node to modify or query.*/
01333 /*  args    Optional argument specifying a value.*/
01334 /* */
01335 /*  Results:*/
01336 /*  val Value associated with the given key of the given node*/
01337 
01338 ret  ::struct::tree::_set (type name , type node , type key , type args) {
01339     if {[llength $args] > 1} {
01340     return -code error "wrong # args: should be \"$name set node key\
01341         ?value?\""
01342     }
01343     if {![_exists $name $node]} {
01344     return -code error "node \"$node\" does not exist in tree \"$name\""
01345     }
01346 
01347     # Process the arguments ...
01348 
01349     if {[llength $args] > 0} {
01350     # Setting the value. This may have to create
01351     # the attribute array for this particular
01352     # node
01353 
01354     variable ${name}::attribute
01355     if {![info exists attribute($node)]} {
01356         # No attribute data for this node,
01357         # so create it as we need it now.
01358         GenAttributeStorage $name $node
01359     }
01360     upvar ${name}::$attribute($node) data
01361 
01362     return [set data($key) [lindex $args end]]
01363     } else {
01364     # Getting the value
01365 
01366     return [_get $name $node $key]
01367     }
01368 }
01369 
01370 /*  ::struct::tree::_append --*/
01371 /* */
01372 /*  Append a value for a node in a tree.*/
01373 /* */
01374 /*  Arguments:*/
01375 /*  name    Name of the tree.*/
01376 /*  node    Node to modify.*/
01377 /*  key Name of attribute to modify.*/
01378 /*  value   Value to append*/
01379 /* */
01380 /*  Results:*/
01381 /*  val Value associated with the given key of the given node*/
01382 
01383 ret  ::struct::tree::_append (type name , type node , type key , type value) {
01384     if {![_exists $name $node]} {
01385     return -code error "node \"$node\" does not exist in tree \"$name\""
01386     }
01387 
01388     variable ${name}::attribute
01389     if {![info exists attribute($node)]} {
01390     # No attribute data for this node,
01391     # so create it as we need it.
01392     GenAttributeStorage $name $node
01393     }
01394 
01395     upvar ${name}::$attribute($node) data
01396     return [append data($key) $value]
01397 }
01398 
01399 /*  ::struct::tree::_lappend --*/
01400 /* */
01401 /*  lappend a value for a node in a tree.*/
01402 /* */
01403 /*  Arguments:*/
01404 /*  name    Name of the tree.*/
01405 /*  node    Node to modify or query.*/
01406 /*  key Name of attribute to modify.*/
01407 /*  value   Value to append*/
01408 /* */
01409 /*  Results:*/
01410 /*  val Value associated with the given key of the given node*/
01411 
01412 ret  ::struct::tree::_lappend (type name , type node , type key , type value) {
01413     if {![_exists $name $node]} {
01414     return -code error "node \"$node\" does not exist in tree \"$name\""
01415     }
01416 
01417     variable ${name}::attribute
01418     if {![info exists attribute($node)]} {
01419     # No attribute data for this node,
01420     # so create it as we need it.
01421     GenAttributeStorage $name $node
01422     }
01423 
01424     upvar ${name}::$attribute($node) data
01425     return [lappend data($key) $value]
01426 }
01427 
01428 /*  ::struct::tree::_leaves --*/
01429 /* */
01430 /*  Return a list containing all leaf nodes known to the tree.*/
01431 /* */
01432 /*  Arguments:*/
01433 /*  name        Name of the tree object.*/
01434 /* */
01435 /*  Results:*/
01436 /*  nodes   List of leaf nodes in the tree.*/
01437 
01438 ret  ::struct::tree::_leaves (type name) {
01439     variable ${name}::children
01440 
01441     set res {}
01442     foreach n [array names children] {
01443     if {[llength $children($n)]} continue
01444     lappend res $n
01445     }
01446     return $res
01447 }
01448 
01449 /*  ::struct::tree::_size --*/
01450 /* */
01451 /*  Return the number of descendants of a given node.  The default node*/
01452 /*  is the special root node.*/
01453 /* */
01454 /*  Arguments:*/
01455 /*  name    Name of the tree.*/
01456 /*  node    Optional node to start counting from (default is root).*/
01457 /* */
01458 /*  Results:*/
01459 /*  size    Number of descendants of the node.*/
01460 
01461 ret  ::struct::tree::_size (type name , type args) {
01462     variable ${name}::rootname
01463     if {[llength $args] > 1} {
01464     return -code error \
01465         "wrong # args: should be \"[list $name] size ?node?\""
01466     } elseif {[llength $args] == 1} {
01467     set node [lindex $args 0]
01468 
01469     if { ![_exists $name $node] } {
01470         return -code error "node \"$node\" does not exist in tree \"$name\""
01471     }
01472     } else {
01473     # If the node is the root, we can do the cheap thing and just count the
01474     # number of nodes (excluding the root node) that we have in the tree with
01475     # array size.
01476 
01477     return [expr {[array size ${name}::parent] - 1}]
01478     }
01479 
01480     # If the node is the root, we can do the cheap thing and just count the
01481     # number of nodes (excluding the root node) that we have in the tree with
01482     # array size.
01483 
01484     if { [string equal $node $rootname] } {
01485     return [expr {[array size ${name}::parent] - 1}]
01486     }
01487 
01488     # Otherwise we have to do it the hard way and do a full tree search
01489     variable ${name}::children
01490     set size 0
01491     set st [list ]
01492     foreach child $children($node) {
01493     lappend st $child
01494     }
01495     while { [llength $st] > 0 } {
01496     set node [lindex $st end]
01497     ldelete st end
01498     incr size
01499     foreach child $children($node) {
01500         lappend st $child
01501     }
01502     }
01503     return $size
01504 }
01505 
01506 /*  ::struct::tree::_splice --*/
01507 /* */
01508 /*  Add a node to a tree, making a range of children from the given*/
01509 /*  parent children of the new node.*/
01510 /* */
01511 /*  Arguments:*/
01512 /*  name        Name of the tree.*/
01513 /*  parentNode  Parent to add the node to.*/
01514 /*  from        Index at which to insert.*/
01515 /*  to      Optional end of the range of children to replace.*/
01516 /*          Defaults to 'end'.*/
01517 /*  args        Optional node name; if given, must be unique.  If not*/
01518 /*          given, a unique name will be generated.*/
01519 /* */
01520 /*  Results:*/
01521 /*  node        Name of the node added to the tree.*/
01522 
01523 ret  ::struct::tree::_splice (type name , type parentNode , type from , optional to =end , type args) {
01524 
01525     if { ![_exists $name $parentNode] } {
01526     return -code error "node \"$parentNode\" does not exist in tree \"$name\""
01527     }
01528 
01529     if { [llength $args] == 0 } {
01530     # No node name given; generate a unique node name
01531     set node [GenerateUniqueNodeName $name]
01532     } else {
01533     set node [lindex $args 0]
01534     }
01535 
01536     if { [_exists $name $node] } {
01537     return -code error "node \"$node\" already exists in tree \"$name\""
01538     }
01539 
01540     variable ${name}::children
01541     variable ${name}::parent
01542 
01543     if {[string equal $from "end"]} {
01544     set from [expr {[llength $children($parentNode)] - 1}]
01545     } elseif {[regexp {^end-([0-9]+)$} $from -> n]} {
01546     set from [expr {[llength $children($parentNode)] - 1 - $n}]
01547     }
01548     if {[string equal $to "end"]} {
01549     set to [expr {[llength $children($parentNode)] - 1}]
01550     } elseif {[regexp {^end-([0-9]+)$} $to -> n]} {
01551     set to   [expr {[llength $children($parentNode)] - 1 - $n}]
01552     }
01553 
01554     # Save the list of children that are moving
01555     set moveChildren [lrange $children($parentNode) $from $to]
01556 
01557     # Remove those children from the parent
01558     ldelete children($parentNode) $from $to
01559 
01560     # Add the new node
01561     _insert $name $parentNode $from $node
01562 
01563     # Move the children
01564     set children($node) $moveChildren
01565     foreach child $moveChildren {
01566     set parent($child) $node
01567     }
01568 
01569     return $node
01570 }
01571 
01572 /*  ::struct::tree::_swap --*/
01573 /* */
01574 /*  Swap two nodes in a tree.*/
01575 /* */
01576 /*  Arguments:*/
01577 /*  name    Name of the tree.*/
01578 /*  node1   First node to swap.*/
01579 /*  node2   Second node to swap.*/
01580 /* */
01581 /*  Results:*/
01582 /*  None.*/
01583 
01584 ret  ::struct::tree::_swap (type name , type node1 , type node2) {
01585     # Can't swap the magic root node
01586     variable ${name}::rootname
01587     if {[string equal $node1 $rootname] || [string equal $node2 $rootname]} {
01588     return -code error "cannot swap root node"
01589     }
01590 
01591     # Can only swap two real nodes
01592     if {![_exists $name $node1]} {
01593     return -code error "node \"$node1\" does not exist in tree \"$name\""
01594     }
01595     if {![_exists $name $node2]} {
01596     return -code error "node \"$node2\" does not exist in tree \"$name\""
01597     }
01598 
01599     # Can't swap a node with itself
01600     if {[string equal $node1 $node2]} {
01601     return -code error "cannot swap node \"$node1\" with itself"
01602     }
01603 
01604     # Swapping nodes means swapping their labels and values
01605     variable ${name}::children
01606     variable ${name}::parent
01607 
01608     set parent1 $parent($node1)
01609     set parent2 $parent($node2)
01610 
01611     # Replace node1 with node2 in node1's parent's children list, and
01612     # node2 with node1 in node2's parent's children list
01613     set i1 [lsearch -exact $children($parent1) $node1]
01614     set i2 [lsearch -exact $children($parent2) $node2]
01615 
01616     lset children($parent1) $i1 $node2
01617     lset children($parent2) $i2 $node1
01618 
01619     # Make node1 the parent of node2's children, and vis versa
01620     foreach child $children($node2) {
01621     set parent($child) $node1
01622     }
01623     foreach child $children($node1) {
01624     set parent($child) $node2
01625     }
01626 
01627     # Swap the children lists
01628     set children1 $children($node1)
01629     set children($node1) $children($node2)
01630     set children($node2) $children1
01631 
01632     if { [string equal $node1 $parent2] } {
01633     set parent($node1) $node2
01634     set parent($node2) $parent1
01635     } elseif { [string equal $node2 $parent1] } {
01636     set parent($node1) $parent2
01637     set parent($node2) $node1
01638     } else {
01639     set parent($node1) $parent2
01640     set parent($node2) $parent1
01641     }
01642 
01643     # Swap the values
01644     # More complicated now with the possibility that nodes do not have
01645     # attribute storage associated with them.
01646 
01647     variable ${name}::attribute
01648 
01649     if {
01650     [set ia [info exists attribute($node1)]] ||
01651     [set ib [info exists attribute($node2)]]
01652     } {
01653     # At least one of the nodes has attribute data. We simply swap
01654     # the references to the arrays containing them. No need to
01655     # copy the actual data around.
01656 
01657     if {$ia && $ib} {
01658         set tmp               $attribute($node1)
01659         set attribute($node1) $attribute($node2)
01660         set attribute($node2) $tmp
01661     } elseif {$ia} {
01662         set   attribute($node2) $attribute($node1)
01663         unset attribute($node1)
01664     } elseif {$ib} {
01665         set   attribute($node1) $attribute($node2)
01666         unset attribute($node2)
01667     } else {
01668         return -code error "Impossible condition."
01669     }
01670     } ; # else: No attribute storage => Nothing to do {}
01671 
01672     return
01673 }
01674 
01675 /*  ::struct::tree::_unset --*/
01676 /* */
01677 /*  Remove a keyed value from a node.*/
01678 /* */
01679 /*  Arguments:*/
01680 /*  name    Name of the tree.*/
01681 /*  node    Node to modify.*/
01682 /*  key Name of attribute to unset.*/
01683 /* */
01684 /*  Results:*/
01685 /*  None.*/
01686 
01687 ret  ::struct::tree::_unset (type name , type node , type key) {
01688     if {![_exists $name $node]} {
01689     return -code error "node \"$node\" does not exist in tree \"$name\""
01690     }
01691 
01692     variable ${name}::attribute
01693     if {![info exists attribute($node)]} {
01694     # No attribute data for this node,
01695     # nothing to do.
01696     return
01697     }
01698 
01699     upvar ${name}::$attribute($node) data
01700     catch {unset data($key)}
01701 
01702     if {[array size data] == 0} {
01703     # No attributes stored for this node, squash the whole array.
01704     unset attribute($node)
01705     unset data
01706     }
01707     return
01708 }
01709 
01710 /*  ::struct::tree::_walk --*/
01711 /* */
01712 /*  Walk a tree using a pre-order depth or breadth first*/
01713 /*  search. Pre-order DFS is the default.  At each node that is visited,*/
01714 /*  a command will be called with the name of the tree and the node.*/
01715 /* */
01716 /*  Arguments:*/
01717 /*  name    Name of the tree.*/
01718 /*  node    Node at which to start.*/
01719 /*  args    Optional additional arguments specifying the type and order of*/
01720 /*      the tree walk, and the command to execute at each node.*/
01721 /*      Format is*/
01722 /*          ?-type {bfs|dfs}? ?-order {pre|post|in|both}? a n script*/
01723 /* */
01724 /*  Results:*/
01725 /*  None.*/
01726 
01727 ret  ::struct::tree::_walk (type name , type node , type args) {
01728     set usage "$name walk node ?-type {bfs|dfs}? ?-order {pre|post|in|both}? ?--? loopvar script"
01729 
01730     if {[llength $args] > 7 || [llength $args] < 2} {
01731     return -code error "wrong # args: should be \"$usage\""
01732     }
01733 
01734     if { ![_exists $name $node] } {
01735     return -code error "node \"$node\" does not exist in tree \"$name\""
01736     }
01737 
01738     set args [WalkOptions $args 2 $usage]
01739     # Remainder is 'a n script'
01740 
01741     foreach {loopvariables script} $args break
01742 
01743     if {[llength $loopvariables] > 2} {
01744     return -code error "too many loop variables, at most two allowed"
01745     } elseif {[llength $loopvariables] == 2} {
01746     foreach {avar nvar} $loopvariables break
01747     } else {
01748     set nvar [lindex $loopvariables 0]
01749     set avar {}
01750     }
01751 
01752     # Make sure we have a script to run, otherwise what's the point?
01753     if { [string equal $script ""] } {
01754     return -code error "no script specified, or empty"
01755     }
01756 
01757     # Do the walk
01758     variable ${name}::children
01759     set st [list ]
01760     lappend st $node
01761 
01762     # Compute some flags for the possible places of command evaluation
01763     set leave [expr {[string equal $order post] || [string equal $order both]}]
01764     set enter [expr {[string equal $order pre]  || [string equal $order both]}]
01765     set touch [string equal $order in]
01766 
01767     if {$leave} {
01768     set lvlabel leave
01769     } elseif {$touch} {
01770     # in-order does not provide a sense
01771     # of nesting for the parent, hence
01772     # no enter/leave, just 'visit'.
01773     set lvlabel visit
01774     }
01775 
01776     set rcode 0
01777     set rvalue {}
01778 
01779     if {[string equal $type "dfs"]} {
01780     # Depth-first walk, several orders of visiting nodes
01781     # (pre, post, both, in)
01782 
01783     array set visited {}
01784 
01785     while { [llength $st] > 0 } {
01786         set node [lindex $st end]
01787 
01788         if {[info exists visited($node)]} {
01789         # Second time we are looking at this 'node'.
01790         # Pop it, then evaluate the command (post, both, in).
01791 
01792         ldelete st end
01793 
01794         if {$leave || $touch} {
01795             # Evaluate the script at this node
01796             WalkCall $avar $nvar $name $node $lvlabel $script
01797             # prune stops execution of loop here.
01798         }
01799         } else {
01800         # First visit of this 'node'.
01801         # Do *not* pop it from the stack so that we are able
01802         # to visit again after its children
01803 
01804         # Remember it.
01805         set visited($node) .
01806 
01807         if {$enter} {
01808             # Evaluate the script at this node (pre, both).
01809             #
01810             # Note: As this is done before the children are
01811             # looked at the script may change the children of
01812             # this node and thus affect the walk.
01813 
01814             WalkCall $avar $nvar $name $node "enter" $script
01815             # prune stops execution of loop here.
01816         }
01817 
01818         # Add the children of this node to the stack.
01819         # The exact behaviour depends on the chosen
01820         # order. For pre, post, both-order we just
01821         # have to add them in reverse-order so that
01822         # they will be popped left-to-right. For in-order
01823         # we have rearrange the stack so that the parent
01824         # is revisited immediately after the first child.
01825         # (but only if there is ore than one child,)
01826 
01827         set clist        $children($node)
01828         set len [llength $clist]
01829 
01830         if {$touch && ($len > 1)} {
01831             # Pop node from stack, insert into list of children
01832             ldelete st end
01833             set clist [linsert $clist 1 $node]
01834             incr len
01835         }
01836 
01837         for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
01838             lappend st [lindex $clist $i]
01839         }
01840         }
01841     }
01842     } else {
01843     # Breadth first walk (pre, post, both)
01844     # No in-order possible. Already captured.
01845 
01846     if {$leave} {
01847         set backward $st
01848     }
01849 
01850     while { [llength $st] > 0 } {
01851         set node [lindex   $st 0]
01852         ldelete st 0
01853 
01854         if {$enter} {
01855         # Evaluate the script at this node
01856         WalkCall $avar $nvar $name $node "enter" $script
01857         # prune stops execution of loop here.
01858         }
01859 
01860         # Add this node's children
01861         # And create a mirrored version in case of post/both order.
01862 
01863         foreach child $children($node) {
01864         lappend st $child
01865         if {$leave} {
01866             set backward [linsert $backward 0 $child]
01867         }
01868         }
01869     }
01870 
01871     if {$leave} {
01872         foreach node $backward {
01873         # Evaluate the script at this node
01874         WalkCall $avar $nvar $name $node "leave" $script
01875         }
01876     }
01877     }
01878 
01879     if {$rcode != 0} {
01880     return -code $rcode $rvalue
01881     }
01882     return
01883 }
01884 
01885 ret  ::struct::tree::_walkproc (type name , type node , type args) {
01886     set usage "$name walkproc node ?-type {bfs|dfs}? ?-order {pre|post|in|both}? ?--? cmdprefix"
01887 
01888     if {[llength $args] > 6 || [llength $args] < 1} {
01889     return -code error "wrong # args: should be \"$usage\""
01890     }
01891 
01892     if { ![_exists $name $node] } {
01893     return -code error "node \"$node\" does not exist in tree \"$name\""
01894     }
01895 
01896     set args [WalkOptions $args 1 $usage]
01897     # Remainder is 'n cmdprefix'
01898 
01899     set script [lindex $args 0]
01900 
01901     # Make sure we have a script to run, otherwise what's the point?
01902     if { ![llength $script] } {
01903     return -code error "no script specified, or empty"
01904     }
01905 
01906     # Do the walk
01907     variable ${name}::children
01908     set st [list ]
01909     lappend st $node
01910 
01911     # Compute some flags for the possible places of command evaluation
01912     set leave [expr {[string equal $order post] || [string equal $order both]}]
01913     set enter [expr {[string equal $order pre]  || [string equal $order both]}]
01914     set touch [string equal $order in]
01915 
01916     if {$leave} {
01917     set lvlabel leave
01918     } elseif {$touch} {
01919     # in-order does not provide a sense
01920     # of nesting for the parent, hence
01921     # no enter/leave, just 'visit'.
01922     set lvlabel visit
01923     }
01924 
01925     set rcode 0
01926     set rvalue {}
01927 
01928     if {[string equal $type "dfs"]} {
01929     # Depth-first walk, several orders of visiting nodes
01930     # (pre, post, both, in)
01931 
01932     array set visited {}
01933 
01934     while { [llength $st] > 0 } {
01935         set node [lindex $st end]
01936 
01937         if {[info exists visited($node)]} {
01938         # Second time we are looking at this 'node'.
01939         # Pop it, then evaluate the command (post, both, in).
01940 
01941         ldelete st end
01942 
01943         if {$leave || $touch} {
01944             # Evaluate the script at this node
01945             WalkCallProc $name $node $lvlabel $script
01946             # prune stops execution of loop here.
01947         }
01948         } else {
01949         # First visit of this 'node'.
01950         # Do *not* pop it from the stack so that we are able
01951         # to visit again after its children
01952 
01953         # Remember it.
01954         set visited($node) .
01955 
01956         if {$enter} {
01957             # Evaluate the script at this node (pre, both).
01958             #
01959             # Note: As this is done before the children are
01960             # looked at the script may change the children of
01961             # this node and thus affect the walk.
01962 
01963             WalkCallProc $name $node "enter" $script
01964             # prune stops execution of loop here.
01965         }
01966 
01967         # Add the children of this node to the stack.
01968         # The exact behaviour depends on the chosen
01969         # order. For pre, post, both-order we just
01970         # have to add them in reverse-order so that
01971         # they will be popped left-to-right. For in-order
01972         # we have rearrange the stack so that the parent
01973         # is revisited immediately after the first child.
01974         # (but only if there is ore than one child,)
01975 
01976         set clist        $children($node)
01977         set len [llength $clist]
01978 
01979         if {$touch && ($len > 1)} {
01980             # Pop node from stack, insert into list of children
01981             ldelete st end
01982             set clist [linsert $clist 1 $node]
01983             incr len
01984         }
01985 
01986         for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
01987             lappend st [lindex $clist $i]
01988         }
01989         }
01990     }
01991     } else {
01992     # Breadth first walk (pre, post, both)
01993     # No in-order possible. Already captured.
01994 
01995     if {$leave} {
01996         set backward $st
01997     }
01998 
01999     while { [llength $st] > 0 } {
02000         set node [lindex   $st 0]
02001         ldelete st 0
02002 
02003         if {$enter} {
02004         # Evaluate the script at this node
02005         WalkCallProc $name $node "enter" $script
02006         # prune stops execution of loop here.
02007         }
02008 
02009         # Add this node's children
02010         # And create a mirrored version in case of post/both order.
02011 
02012         foreach child $children($node) {
02013         lappend st $child
02014         if {$leave} {
02015             set backward [linsert $backward 0 $child]
02016         }
02017         }
02018     }
02019 
02020     if {$leave} {
02021         foreach node $backward {
02022         # Evaluate the script at this node
02023         WalkCallProc $name $node "leave" $script
02024         }
02025     }
02026     }
02027 
02028     if {$rcode != 0} {
02029     return -code $rcode $rvalue
02030     }
02031     return
02032 }
02033 
02034 ret  ::struct::tree::WalkOptions (type theargs , type n , type usage) {
02035     upvar 1 type type order order
02036 
02037     # Set defaults
02038     set type dfs
02039     set order pre
02040 
02041     while {[llength $theargs]} {
02042     set flag [lindex $theargs 0]
02043     switch -exact -- $flag {
02044         "-type" {
02045         if {[llength $theargs] < 2} {
02046             return -code error "value for \"$flag\" missing"
02047         }
02048         set type [string tolower [lindex $theargs 1]]
02049         set theargs [lrange $theargs 2 end]
02050         }
02051         "-order" {
02052         if {[llength $theargs] < 2} {
02053             return -code error "value for \"$flag\" missing"
02054         }
02055         set order [string tolower [lindex $theargs 1]]
02056         set theargs [lrange $theargs 2 end]
02057         }
02058         "--" {
02059         set theargs [lrange $theargs 1 end]
02060         break
02061         }
02062         default {
02063         break
02064         }
02065     }
02066     }
02067 
02068     if {[llength $theargs] == 0} {
02069     return -code error "wrong # args: should be \"$usage\""
02070     }
02071     if {[llength $theargs] != $n} {
02072     return -code error "unknown option \"$flag\""
02073     }
02074 
02075     # Validate that the given type is good
02076     switch -exact -- $type {
02077     "dfs" - "bfs" {
02078         set type $type
02079     }
02080     default {
02081         return -code error "bad search type \"$type\": must be bfs or dfs"
02082     }
02083     }
02084 
02085     # Validate that the given order is good
02086     switch -exact -- $order {
02087     "pre" - "post" - "in" - "both" {
02088         set order $order
02089     }
02090     default {
02091         return -code error "bad search order \"$order\":\
02092             must be both, in, pre, or post"
02093     }
02094     }
02095 
02096     if {[string equal $order "in"] && [string equal $type "bfs"]} {
02097     return -code error "unable to do a ${order}-order breadth first walk"
02098     }
02099 
02100     return $theargs
02101 }
02102 
02103 /*  ::struct::tree::WalkCall --*/
02104 /* */
02105 /*  Helper command to 'walk' handling the evaluation*/
02106 /*  of the user-specified command. Information about*/
02107 /*  the tree, node and current action are substituted*/
02108 /*  into the command before it evaluation.*/
02109 /* */
02110 /*  Arguments:*/
02111 /*  tree    Tree we are walking*/
02112 /*  node    Node we are at.*/
02113 /*  action  The current action.*/
02114 /*  cmd The command to call, already partially substituted.*/
02115 /* */
02116 /*  Results:*/
02117 /*  None.*/
02118 
02119 ret  ::struct::tree::WalkCall (type avar , type nvar , type tree , type node , type action , type cmd) {
02120 
02121     if {$avar != {}} {
02122     upvar 2 $avar a ; set a $action
02123     }
02124     upvar 2 $nvar n ; set n $node
02125 
02126     set code [catch {uplevel 2 $cmd} result]
02127 
02128     # decide what to do upon the return code:
02129     #
02130     #               0 - the body executed successfully
02131     #               1 - the body raised an error
02132     #               2 - the body invoked [return]
02133     #               3 - the body invoked [break]
02134     #               4 - the body invoked [continue]
02135     #               5 - the body invoked [struct::tree::prune]
02136     # everything else - return and pass on the results
02137     #
02138     switch -exact -- $code {
02139     0 {}
02140     1 {
02141         return -errorinfo [ErrorInfoAsCaller uplevel WalkCall]  \
02142             -errorcode $::errorCode -code error $result
02143     }
02144     3 {
02145         # FRINK: nocheck
02146         return -code break
02147     }
02148     4 {}
02149     5 {
02150         upvar order order
02151         if {[string equal $order post] || [string equal $order in]} {
02152         return -code error "Illegal attempt to prune ${order}-order walking"
02153         }
02154         return -code continue
02155     }
02156     default {
02157         upvar 1 rcode rcode rvalue rvalue
02158         set rcode $code
02159         set rvalue $result
02160         return -code break
02161         #return -code $code $result
02162     }
02163     }
02164     return {}
02165 }
02166 
02167 ret  ::struct::tree::WalkCallProc (type tree , type node , type action , type cmd) {
02168 
02169     lappend cmd $tree $node $action
02170     set code [catch {uplevel 2 $cmd} result]
02171 
02172     # decide what to do upon the return code:
02173     #
02174     #               0 - the body executed successfully
02175     #               1 - the body raised an error
02176     #               2 - the body invoked [return]
02177     #               3 - the body invoked [break]
02178     #               4 - the body invoked [continue]
02179     #               5 - the body invoked [struct::tree::prune]
02180     # everything else - return and pass on the results
02181     #
02182     switch -exact -- $code {
02183     0 {}
02184     1 {
02185         return -errorinfo [ErrorInfoAsCaller uplevel WalkCallProc]  \
02186             -errorcode $::errorCode -code error $result
02187     }
02188     3 {
02189         # FRINK: nocheck
02190         return -code break
02191     }
02192     4 {}
02193     5 {
02194         upvar order order
02195         if {[string equal $order post] || [string equal $order in]} {
02196         return -code error "Illegal attempt to prune ${order}-order walking"
02197         }
02198         return -code continue
02199     }
02200     default {
02201         upvar 1 rcode rcode rvalue rvalue
02202         set rcode $code
02203         set rvalue $result
02204         return -code break
02205     }
02206     }
02207     return {}
02208 }
02209 
02210 ret  ::struct::tree::ErrorInfoAsCaller (type find , type replace) {
02211     set info $::errorInfo
02212     set i [string last "\n    (\"$find" $info]
02213     if {$i == -1} {return $info}
02214     set result [string range $info 0 [incr i 6]]    ;# keep "\n    (\""
02215     append result $replace          ;# $find -> $replace
02216     incr i [string length $find]
02217     set j [string first ) $info [incr i]]   ;# keep rest of parenthetical
02218     append result [string range $info $i $j]
02219     return $result
02220 }
02221 
02222 /*  ::struct::tree::GenerateUniqueNodeName --*/
02223 /* */
02224 /*  Generate a unique node name for the given tree.*/
02225 /* */
02226 /*  Arguments:*/
02227 /*  name    Name of the tree to generate a unique node name for.*/
02228 /* */
02229 /*  Results:*/
02230 /*  node    Name of a node guaranteed to not exist in the tree.*/
02231 
02232 ret  ::struct::tree::GenerateUniqueNodeName (type name) {
02233     variable ${name}::nextUnusedNode
02234     while {[_exists $name "node${nextUnusedNode}"]} {
02235     incr nextUnusedNode
02236     }
02237     return "node${nextUnusedNode}"
02238 }
02239 
02240 /*  ::struct::tree::KillNode --*/
02241 /* */
02242 /*  Delete all data of a node.*/
02243 /* */
02244 /*  Arguments:*/
02245 /*  name    Name of the tree containing the node*/
02246 /*  node    Name of the node to delete.*/
02247 /* */
02248 /*  Results:*/
02249 /*  none*/
02250 
02251 ret  ::struct::tree::KillNode (type name , type node) {
02252     variable ${name}::parent
02253     variable ${name}::children
02254     variable ${name}::attribute
02255 
02256     # Remove all record of $node
02257     unset parent($node)
02258     unset children($node)
02259 
02260     if {[info exists attribute($node)]} {
02261     # FRINK: nocheck
02262     unset ${name}::$attribute($node)
02263     unset attribute($node)
02264     }
02265     return
02266 }
02267 
02268 /*  ::struct::tree::GenAttributeStorage --*/
02269 /* */
02270 /*  Create an array to store the attributes of a node in.*/
02271 /* */
02272 /*  Arguments:*/
02273 /*  name    Name of the tree containing the node*/
02274 /*  node    Name of the node which got attributes.*/
02275 /* */
02276 /*  Results:*/
02277 /*  none*/
02278 
02279 ret  ::struct::tree::GenAttributeStorage (type name , type node) {
02280     variable ${name}::nextAttr
02281     variable ${name}::attribute
02282 
02283     set   attr "a[incr nextAttr]"
02284     set   attribute($node) $attr
02285     return
02286 }
02287 
02288 /*  ::struct::tree::Serialize --*/
02289 /* */
02290 /*  Serialize a tree object (partially) into a transportable value.*/
02291 /* */
02292 /*  Arguments:*/
02293 /*  name    Name of the tree.*/
02294 /*  node    Root node of the serialized tree.*/
02295 /* */
02296 /*  Results:*/
02297 /*  None*/
02298 
02299 ret  ::struct::tree::Serialize (type name , type node , type tvar) {
02300     upvar 1 $tvar tree
02301 
02302     variable ${name}::attribute
02303     variable ${name}::parent
02304 
02305     # 'node' is the root of the tree to serialize. The precondition
02306     # for the call is that this node is already stored in the list
02307     # 'tvar', at index 'rootidx'.
02308 
02309     # The attribute data for 'node' goes immediately after the 'node'
02310     # data. the node information is _not_ yet stored, and this command
02311     # has to do this.
02312 
02313 
02314     array set r {}
02315     set loc($node) 0
02316 
02317     lappend tree $node {}
02318     if {[info exists attribute($node)]} {
02319     upvar ${name}::$attribute($node) data
02320     lappend tree [array get data]
02321     } else {
02322     # Encode nodes without attributes.
02323     lappend tree {}
02324     }
02325 
02326     foreach n [DescendantsCore $name $node] {
02327     set loc($n) [llength $tree]
02328     lappend tree $n $loc($parent($n))
02329 
02330     if {[info exists attribute($n)]} {
02331         upvar ${name}::$attribute($n) data
02332         lappend tree [array get data]
02333     } else {
02334         # Encode nodes without attributes.
02335         lappend tree {}
02336     }
02337     }
02338 
02339     return $tree
02340 }
02341 
02342 
02343 ret  ::struct::tree::CheckSerialization (type ser , type avar , type pvar , type cvar , type rnvar) {
02344     upvar 1 $avar attr $pvar p $cvar ch $rnvar rn
02345 
02346     # Overall length ok ?
02347 
02348     if {[llength $ser] % 3} {
02349     return -code error \
02350         "error in serialization: list length not a multiple of 3."
02351     }
02352 
02353     set rn {}
02354     array set p    {}
02355     array set ch   {}
02356     array set attr {}
02357 
02358     # Basic decoder pass
02359 
02360     foreach {node parent nattr} $ser {
02361 
02362     # Initialize children data, if not already done
02363     if {![info exists ch($node)]} {
02364         set ch($node) {}
02365     }
02366     # Attribute length ok ? Dictionary!
02367     if {[llength $nattr] % 2} {
02368         return -code error \
02369             "error in serialization: malformed attribute dictionary."
02370     }
02371     # Remember attribute data only for non-empty nodes
02372     if {[llength $nattr]} {
02373         set attr($node) $nattr
02374     }
02375     # Remember root
02376     if {$parent == {}} {
02377         lappend rn $node
02378         set p($node) {}
02379         continue
02380     }
02381     # Parent reference ok ?
02382     if {
02383         ![string is integer -strict $parent] ||
02384         ($parent % 3) ||
02385         ($parent < 0) ||
02386         ($parent >= [llength $ser])
02387     } {
02388         return -code error \
02389             "error in serialization: bad parent reference \"$parent\"."
02390     }
02391     # Remember parent, and reconstruct children
02392 
02393     set p($node) [lindex $ser $parent]
02394     lappend ch($p($node)) $node
02395     }
02396 
02397     # Root node information ok ?
02398 
02399     if {[llength $rn] < 1} {
02400     return -code error \
02401         "error in serialization: no root specified."
02402     } elseif {[llength $rn] > 1} {
02403     return -code error \
02404         "error in serialization: multiple root nodes."
02405     }
02406     set rn [lindex $rn 0]
02407 
02408     # Duplicate node names ?
02409 
02410     if {[array size ch] < ([llength $ser] / 3)} {
02411     return -code error \
02412         "error in serialization: duplicate node names."
02413     }
02414 
02415     # Cycles in the parent relationship ?
02416 
02417     array set visited {}
02418     foreach n [array names p] {
02419     if {[info exists visited($n)]} {continue}
02420     array set _ {}
02421     while {$n != {}} {
02422         if {[info exists _($n)]} {
02423         # Node already converted, cycle.
02424         return -code error \
02425             "error in serialization: cycle detected."
02426         }
02427         set _($n)       .
02428         # root ?
02429         if {$p($n) == {}} {break}
02430         set n $p($n)
02431         if {[info exists visited($n)]} {break}
02432         set visited($n) .
02433     }
02434     unset _
02435     }
02436     # Ok. The data is now ready for the caller.
02437 
02438     return
02439 }
02440 
02441 /* */
02442 /*  Private functions follow*/
02443 /* */
02444 /*  Do a compatibility version of [lset] for pre-8.4 versions of Tcl.*/
02445 /*  This version does not do multi-arg [lset]!*/
02446 
02447 ret  ::struct::tree::K ( type x , type y ) { set x }
02448 
02449 if { [package vcompare [package provide Tcl] 8.4] < 0 } {
02450     ret  ::struct::tree::lset ( type var , type index , type arg ) {
02451     upvar 1 $var list
02452     set list [::lreplace [K $list [set list {}]] $index $index $arg]
02453     }
02454 }
02455 
02456 ret  ::struct::tree::ldelete (type var , type index , optional end ={)} {
02457     upvar 1 $var list
02458     if {$end == {}} { end =  $index}
02459      list =  [lreplace [K $list [ list =  {}]] $index $end]
02460     return
02461 }
02462 
02463 /*  ### ### ### ######### ######### #########*/
02464 /*  Ready*/
02465 
02466 namespace ::struct {
02467     /*  Put 'tree::tree' into the general structure namespace*/
02468     /*  for pickup by the main management.*/
02469 
02470     namespace import -force tree::tree_tcl
02471 }
02472 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1