Changeset 11 for trunk/tcl/scripts
- Timestamp:
- May 30, 2005 9:33:49 PM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/tcl/scripts/library.tcl
r9 r11 11 11 package require Itcl 12 12 13 namespace eval Rappture { # forward declaration } 14 15 # ---------------------------------------------------------------------- 16 # USAGE: library ?-std? <file> 13 namespace eval Rappture { 14 variable stdlib "" 15 } 16 17 # ---------------------------------------------------------------------- 18 # USAGE: library <file> 19 # USAGE: library standard 17 20 # USAGE: library isvalid <object> 18 21 # … … 21 24 # file that represents it. 22 25 # 23 # If the -std flag is included, then the file is treated as the 24 # name of a standard file, which is part of the Rappture installation. 26 # If you use the word "standard" in place of the file name, this 27 # function returns the standard Rappture library object, which 28 # contains material definitions. 25 29 # 26 30 # The isvalid operation checks an <object> to see if it is a valid … … 40 44 } 41 45 42 # handle the open operation... 43 set stdfile 0 44 while {[llength $args] > 1} { 45 set switch [lindex $args 0] 46 set args [lrange $args 1 end] 47 if {$switch == "-std"} { 48 set stdfile 1 49 } else { 50 error "bad option \"$switch\": should be -std" 51 } 46 if {[llength $args] != 1} { 47 error "wrong # args: should be \"library file\" or \"library isvalid object\"" 52 48 } 53 49 set fname [lindex $args 0] 54 50 55 if {$stdfile && [file pathtype $fname] != "absolute"} { 56 set fname [file join $Rappture::installdir lib $fname] 51 if {$fname == "standard"} { 52 variable stdlib 53 if {$stdlib != ""} { 54 return $stdlib 55 } 56 set fname [file join $Rappture::installdir lib library.xml] 57 58 set fid [::open $fname r] 59 set info [read $fid] 60 close $fid 61 62 set stdlib [Rappture::LibraryObj ::#auto $info] 63 return $stdlib 57 64 } 58 65 … … 71 78 72 79 # ---------------------------------------------------------------------- 80 # USAGE: entities ?-as <fval>? <object> <path> 81 # 82 # Used to sift through an XML <object> for "entities" within the 83 # Rappture description. Entities are things like strings, numbers, 84 # etc., which show up in the GUI as controls. 85 # 86 # Returns a list of all entities found beneath <path>. 87 # 88 # By default, this method returns the component name "type(id)". 89 # This is changed by setting the -as argument to "id" (for name 90 # of the tail element), to "type" (for the type of the tail element), 91 # to "object" (for an object representing the DOM node referenced by 92 # the path. 93 # ---------------------------------------------------------------------- 94 proc Rappture::entities {args} { 95 array set params { 96 -as component 97 } 98 while {[llength $args] > 1} { 99 set first [lindex $args 0] 100 if {[string index $first 0] == "-"} { 101 set choices [array names params] 102 if {[lsearch $choices $first] < 0} { 103 error "bad option \"$first\": should be [join [lsort $choices] {, }]" 104 } 105 set params($first) [lindex $args 1] 106 set args [lrange $args 2 end] 107 } else { 108 break 109 } 110 } 111 if {[llength $args] > 2} { 112 error "wrong # args: should be \"entities ?-as fval? obj ?path?\"" 113 } 114 set xmlobj [lindex $args 0] 115 set path [lindex $args 1] 116 117 set rlist "" 118 lappend queue $path 119 while {[llength $queue] > 0} { 120 set path [lindex $queue 0] 121 set queue [lrange $queue 1 end] 122 123 foreach cpath [$xmlobj children -as path $path] { 124 switch -- [$xmlobj element -as type $cpath] { 125 group { 126 lappend queue $cpath 127 } 128 structure { 129 if {[$xmlobj element $cpath.current.parameters] != ""} { 130 lappend queue $cpath.current.parameters 131 } 132 } 133 default { 134 # add this to the return list with the right flavor 135 if {$params(-as) == "component"} { 136 lappend rlist $cpath 137 } else { 138 lappend rlist [$xmlobj element -as $params(-as) $cpath] 139 } 140 141 # if this element has embedded groups, add them to the queue 142 foreach ccpath [$xmlobj children -as path $cpath] { 143 if {[$xmlobj element -as type $ccpath] == "group"} { 144 lappend queue $ccpath 145 } 146 } 147 } 148 } 149 } 150 } 151 return $rlist 152 } 153 154 # ---------------------------------------------------------------------- 73 155 itcl::class Rappture::LibraryObj { 74 156 constructor {info} { # defined below } … … 76 158 77 159 public method element {args} 160 public method parent {args} 78 161 public method children {args} 79 162 public method get {{path ""}} … … 82 165 public method xml {} 83 166 167 public method diff {libobj} 168 public proc value {libobj path} 169 84 170 protected method find {path} 85 171 protected method path2list {path} 86 172 protected method node2name {node} 87 173 protected method node2comp {node} 174 protected method node2path {node} 175 protected method childnodes {node type} 88 176 89 177 private variable _root 0 ;# non-zero => this obj owns document … … 119 207 120 208 # ---------------------------------------------------------------------- 121 # USAGE: element ?- flavor<fval>? ?<path>?209 # USAGE: element ?-as <fval>? ?<path>? 122 210 # 123 211 # Clients use this to query a particular element within the entire … … 128 216 # 129 217 # By default, this method returns the component name "type(id)". 130 # This is changed by setting the - flavorargument to "id" (for name218 # This is changed by setting the -as argument to "id" (for name 131 219 # of the tail element), to "type" (for the type of the tail element), 132 220 # to "object" (for an object representing the DOM node referenced by 133 # the path .221 # the path). 134 222 # ---------------------------------------------------------------------- 135 223 itcl::body Rappture::LibraryObj::element {args} { 136 224 array set params { 137 - flavorcomponent225 -as component 138 226 } 139 227 while {[llength $args] > 1} { … … 151 239 } 152 240 if {[llength $args] > 1} { 153 error "wrong # args: should be \"element ?- flavorfval? ?path?\""241 error "wrong # args: should be \"element ?-as fval? ?path?\"" 154 242 } 155 243 set path [lindex $args 0] … … 160 248 } 161 249 162 switch -- $params(- flavor) {250 switch -- $params(-as) { 163 251 object { 164 252 return [::Rappture::LibraryObj ::#auto $node] … … 170 258 return [node2name $node] 171 259 } 260 path { 261 return [node2path $node] 262 } 172 263 type { 173 264 return [$node nodeName] 174 265 } 175 266 default { 176 error "bad flavor \"$params(-flavor)\": should be object, id, type, component" 177 } 178 } 179 } 180 181 # ---------------------------------------------------------------------- 182 # USAGE: children ?-flavor <fval>? ?-type <name>? ?<path>? 183 # 184 # Clients use this to query the children of a particular element 185 # within the entire data structure. This is just like the "element" 186 # method, but it returns the children of the element instead of the 187 # element itself. If the optional -type argument is specified, then 188 # the return list is restricted to children of the specified type. 267 error "bad flavor \"$params(-as)\": should be component, id, object, path, type" 268 } 269 } 270 } 271 272 # ---------------------------------------------------------------------- 273 # USAGE: parent ?-as <fval>? ?<path>? 274 # 275 # Clients use this to query the parent of a particular element. 276 # This is just like the "element" method, but it returns the parent 277 # of the element instead of the element itself. 189 278 # 190 279 # By default, this method returns a list of component names "type(id)". 191 # This is changed by setting the - flavorargument to "id" (for tail280 # This is changed by setting the -as argument to "id" (for tail 192 281 # names of all children), to "type" (for the types of all children), 193 282 # to "object" (for a list of objects representing the DOM nodes for 194 283 # all children). 195 284 # ---------------------------------------------------------------------- 196 itcl::body Rappture::LibraryObj:: children{args} {285 itcl::body Rappture::LibraryObj::parent {args} { 197 286 array set params { 198 -flavor component 199 -type "" 287 -as component 200 288 } 201 289 while {[llength $args] > 1} { … … 213 301 } 214 302 if {[llength $args] > 1} { 215 error "wrong # args: should be \"children ?-flavor fval? ?-type name? ?path?\"" 303 error "wrong # args: should be \"parent ?-as fval? ?path?\"" 304 } 305 set path [lindex $args 0] 306 307 set node [find $path] 308 if {$node == ""} { 309 return "" 310 } 311 set node [$node parentNode] 312 313 switch -- $params(-as) { 314 object { 315 return [::Rappture::LibraryObj ::#auto $node] 316 } 317 component { 318 return [node2comp $node] 319 } 320 id { 321 return [node2name $node] 322 } 323 path { 324 return [node2path $node] 325 } 326 type { 327 return [$node nodeName] 328 } 329 default { 330 error "bad flavor \"$params(-as)\": should be component, id, object, path, type" 331 } 332 } 333 } 334 335 # ---------------------------------------------------------------------- 336 # USAGE: children ?-as <fval>? ?-type <name>? ?<path>? 337 # 338 # Clients use this to query the children of a particular element 339 # within the entire data structure. This is just like the "element" 340 # method, but it returns the children of the element instead of the 341 # element itself. If the optional -type argument is specified, then 342 # the return list is restricted to children of the specified type. 343 # 344 # By default, this method returns a list of component names "type(id)". 345 # This is changed by setting the -as argument to "id" (for tail 346 # names of all children), to "type" (for the types of all children), 347 # to "object" (for a list of objects representing the DOM nodes for 348 # all children). 349 # ---------------------------------------------------------------------- 350 itcl::body Rappture::LibraryObj::children {args} { 351 array set params { 352 -as component 353 -type "" 354 } 355 while {[llength $args] > 1} { 356 set first [lindex $args 0] 357 if {[string index $first 0] == "-"} { 358 set choices [array names params] 359 if {[lsearch $choices $first] < 0} { 360 error "bad option \"$first\": should be [join [lsort $choices] {, }]" 361 } 362 set params($first) [lindex $args 1] 363 set args [lrange $args 2 end] 364 } else { 365 break 366 } 367 } 368 if {[llength $args] > 1} { 369 error "wrong # args: should be \"children ?-as fval? ?-type name? ?path?\"" 216 370 } 217 371 set path [lindex $args 0] … … 235 389 236 390 set rlist "" 237 switch -- $params(- flavor) {391 switch -- $params(-as) { 238 392 object { 239 393 foreach n $nlist { … … 251 405 } 252 406 } 407 path { 408 foreach n $nlist { 409 lappend rlist [node2path $n] 410 } 411 } 253 412 type { 254 413 foreach n $nlist { … … 257 416 } 258 417 default { 259 error "bad flavor \"$params(- flavor)\": should be object, id, type, component"418 error "bad flavor \"$params(-as)\": should be component, id, object, type" 260 419 } 261 420 } … … 344 503 345 504 if {[Rappture::library isvalid $str]} { 346 error "not yet implemented" 505 foreach n [[$str info variable _node -value] childNodes] { 506 $node appendXML [$n asXML] 507 } 347 508 } else { 348 509 set n [$_document createText $str] … … 376 537 itcl::body Rappture::LibraryObj::xml {} { 377 538 return [$_node asXML] 539 } 540 541 # ---------------------------------------------------------------------- 542 # USAGE: diff <libobj> 543 # 544 # Compares the entities in this object to those in another and 545 # returns a list of differences. The result is a list of the form: 546 # {op1 path1 oldval1 newval1 ...} where each "op" is +/-/c for 547 # added/subtracted/changed, "path" is the path within the library 548 # that is different, and "oldval"/"newval" give the values for the 549 # object at the path. 550 # ---------------------------------------------------------------------- 551 itcl::body Rappture::LibraryObj::diff {libobj} { 552 set rlist "" 553 554 # query the values for all entities in both objects 555 set thisv [Rappture::entities $this input] 556 set otherv [Rappture::entities $libobj input] 557 558 # scan through values for this object, and compare against other one 559 foreach path $thisv { 560 set i [lsearch -exact $otherv $path] 561 if {$i < 0} { 562 foreach {raw norm} [value $this $path] break 563 lappend rlist - $path $raw "" 564 } else { 565 foreach {traw tnorm} [value $this $path] break 566 foreach {oraw onorm} [value $libobj $path] break 567 if {![string equal $tnorm $onorm]} { 568 lappend rlist c $path $traw $oraw 569 } 570 set otherv [lreplace $otherv $i $i] 571 } 572 } 573 574 # add any values left over in the other object 575 foreach path $otherv { 576 foreach {oraw onorm} [value $libobj $path] break 577 lappend rlist + $path "" $oraw 578 } 579 return $rlist 580 } 581 582 # ---------------------------------------------------------------------- 583 # USAGE: value <object> <path> 584 # 585 # Used to query the "value" associated with the <path> in an XML 586 # <object>. This is a little more complicated than the object's 587 # "get" method. It handles things like structures and values 588 # with normalized units. 589 # 590 # Returns a list of two items: {raw norm} where "raw" is the raw 591 # value from the "get" method and "norm" is the normalized value 592 # produced by this routine. Example: {300K 300} 593 # 594 # Right now, it is a handy little utility used by the "diff" method. 595 # Eventually, it should be moved to a better object-oriented 596 # implementation, where each Rappture type could overload the 597 # various bits of processing below. So we leave it as a "proc" 598 # now instead of a method, since it should be deprecated soon. 599 # ---------------------------------------------------------------------- 600 itcl::body Rappture::LibraryObj::value {libobj path} { 601 switch -- [$libobj element -as type $path] { 602 structure { 603 set raw $path 604 # try to find a label to represent the structure 605 set val [$libobj get $path.about.label] 606 if {"" == $val} { 607 set val [$libobj get $path.current.about.label] 608 } 609 if {"" == $val} { 610 if {[$libobj element $path.current] != ""} { 611 set comps [$libobj children $path.current.components] 612 set val "<structure> with [llength $comps] components" 613 } else { 614 set val "<structure>" 615 } 616 } 617 return [list $raw $val] 618 } 619 number { 620 # get the usual value... 621 set raw "" 622 if {"" != [$libobj element $path.current]} { 623 set raw [$libobj get $path.current] 624 } elseif {"" != [$libobj element $path.default]} { 625 set raw [$libobj get $path.default] 626 } 627 if {"" != $raw} { 628 set val $raw 629 # then normalize to default units 630 set units [$libobj get $path.units] 631 if {"" != $units} { 632 set val [Rappture::Units::convert $val \ 633 -context $units -to $units -units off] 634 } 635 } 636 return [list $raw $val] 637 } 638 } 639 640 # for all other types, get the value (current, or maybe default) 641 set raw "" 642 if {"" != [$libobj element $path.current]} { 643 set raw [$libobj get $path.current] 644 } elseif {"" != [$libobj element $path.default]} { 645 set raw [$libobj get $path.default] 646 } 647 return [list $raw $raw] 378 648 } 379 649 … … 440 710 set index 0 441 711 } 442 set nlist [ $node getElementsByTagName $type]712 set nlist [childnodes $node $type] 443 713 set node [lindex $nlist $index] 444 714 } else { … … 450 720 # 451 721 if {$type != ""} { 452 set nlist [ $node getElementsByTagName $type]722 set nlist [childnodes $node $type] 453 723 } else { 454 724 set nlist [$node childNodes] … … 554 824 } 555 825 set type [$node nodeName] 556 set siblings [ $pnode getElementsByTagName $type]826 set siblings [childnodes $pnode $type] 557 827 set index [lsearch $siblings $node] 558 828 if {$index == 0} { … … 581 851 return "" 582 852 } 583 set siblings [ $pnode getElementsByTagName $type]853 set siblings [childnodes $pnode $type] 584 854 set index [lsearch $siblings $node] 585 855 if {$index == 0} { … … 593 863 return $name 594 864 } 865 866 # ---------------------------------------------------------------------- 867 # USAGE: node2path <node> 868 # 869 # Used internally to create a full path name for the specified node. 870 # The path is relative to the current object, so it stops when the 871 # parent is the root node for this object. 872 # ---------------------------------------------------------------------- 873 itcl::body Rappture::LibraryObj::node2path {node} { 874 set path [node2comp $node] 875 set node [$node parentNode] 876 while {$node != "" && $node != $_node} { 877 set path "[node2comp $node].$path" 878 set node [$node parentNode] 879 } 880 return $path 881 } 882 883 # ---------------------------------------------------------------------- 884 # USAGE: childnodes <node> <type> 885 # 886 # Used internally to return a list of children for the given <node> 887 # that match a specified <type>. Similar to XML getElementsByTagName, 888 # but returns only direct children of the <node>. 889 # ---------------------------------------------------------------------- 890 itcl::body Rappture::LibraryObj::childnodes {node type} { 891 set rlist "" 892 foreach cnode [$node childNodes] { 893 if {[$cnode nodeName] == $type} { 894 lappend rlist $cnode 895 } 896 } 897 return $rlist 898 }
Note: See TracChangeset
for help on using the changeset viewer.