- Publicity: Public Only All
20-Ordered-Composite-procs.tcl
Handling ordered Composites ::xo::OrderedComposite to create tree structures with aggregated objects. This is similar to object aggregations, but preserves the order. The OrderedComposite supports hierarchical sorting.
- Location:
- packages/xotcl-core/tcl/20-Ordered-Composite-procs.tcl
- Created:
- 2005-11-26
- Author:
- Gustaf Neumann <neumann@wu-wien.ac.at>
- CVS Identification:
$Id: 20-Ordered-Composite-procs.tcl,v 1.29 2024/09/11 06:15:56 gustafn Exp $
Procedures in this file
Detailed information
[ hide source ] | [ make this the default ]Content File Source
::xo::library doc { Handling ordered Composites ::xo::OrderedComposite to create tree structures with aggregated objects. This is similar to object aggregations, but preserves the order. The OrderedComposite supports hierarchical sorting. @author Gustaf Neumann (neumann@wu-wien.ac.at) @creation-date 2005-11-26 @cvs-id $Id: 20-Ordered-Composite-procs.tcl,v 1.29 2024/09/11 06:15:56 gustafn Exp $ } namespace eval ::xo { Class create OrderedComposite OrderedComposite instproc show {} { next foreach child [:children] { $child show } } OrderedComposite instproc orderby {{-order "increasing"} {-type dictionary} attribute} { # # Specify the sorting properties order in OrderedComposites. The # sorting is defined via sorting attribute, sorting order and the # sorting type (defining the comparison operators). # # @param order one of "increasing" or "decreasing" # @param type one of "integer", "real", "index" or "dictionary" (default "dictionary") # #ns_log notice "OrderedComposite called with order '$order' type '$type' attribute '$attribute'" set :__order $order set :__orderby $attribute set :__ordercompare [ad_decode $type real __compare_tcl integer __compare_tcl __compare] if {$type eq "index"} { :mixin add ::xo::OrderedComposite::IndexCompare } } OrderedComposite instproc __compare_tcl {a b} { # # Comparison based on plain Tcl compare. This behaves reasonable # on numbers (integer or real) and in mixed cases of numbers and # strings. # set by ${:__orderby} set x [$a set $by] set y [$b set $by] if {$x < $y} { return -1 } elseif {$x > $y} { return 1 } else { return 0 } } if {[::acs::icanuse "ns_strcoll"]} { OrderedComposite instproc __compare {a b} { set by ${:__orderby} set x [$a set $by] set y [$b set $by] return [ns_strcoll $x $y] } } else { OrderedComposite instproc __compare {a b} { set by ${:__orderby} set x [$a set $by] set y [$b set $by] if {$x < $y} { return -1 } elseif {$x > $y} { return 1 } else { return 0 } } } OrderedComposite instproc children {} { if {![info exists :__children]} { return "" } if {[info exists :__orderby] && [llength ${:__children}] > 0} { set firstChild [lindex ${:__children} 0] if {[$firstChild exists ${:__orderby}]} { set order [expr {[info exists :__order] ? ${:__order} : "increasing"}] set compare [expr {[info exists :__ordercompare] ? ${:__ordercompare} : "__compare"}] #ns_log notice SORT=[list lsort -command :$compare -$order ${:__children}] return [lsort -command :$compare -$order ${:__children}] } else { ad_log warning "ignore invalid sorting criterion '${:__orderby}'" } } return ${:__children} } OrderedComposite instproc add obj { lappend :__children $obj $obj set __parent [self] #:log "-- adding __parent [self] to $obj -- calling after_insert" #$obj __after_insert } OrderedComposite instproc delete obj { set p [lsearch -exact ${:__children} $obj] if {$p == -1} {error "can't delete '$obj' from ${:__children}"} set :__children [lreplace ${:__children} $p $p] $obj destroy } OrderedComposite instproc last_child {} { lindex ${:__children} end } OrderedComposite instproc deep_copy {} { set copy [:copy [::xotcl::Object new]] if {[info exists :__children]} { $copy set __children {} foreach c ${:__children} { $copy add [$c copy [::xotcl::Object new]] } } return $copy } OrderedComposite instproc destroy {} { # destroy all children of the ordered composite if {[info exists :__children]} { #:log "--W destroying children ${:__children}" foreach c ${:__children} { if {[nsf::is object $c]} {$c destroy} } } #show_stack;:log "--W children murdered, now next, chlds=[:info children]" #namespace eval [self] {namespace forget *} ;# for pre 1.4.0 versions next } OrderedComposite instproc contains cmds { :requireNamespace ;# legacy for older XOTcl versions set m [Object info instmixin] if {"[self class]::ChildManager" ni $m} { set insert 1 Object instmixin add [self class]::ChildManager } else { set insert 0 } # [self class]::ChildManager instvar composite # push the active composite lappend composite [self] set errorOccurred 0 # check, if we have Tcl's apply available if {[info procs ::apply] eq ""} { set applyCmd [list ::apply [list {} $cmds [self]]] } else { set applyCmd [list namespace eval [self] $cmds] } try { {*}$applyCmd } on error {errorMsg} { set errorOccurred 1 } finally { # pop the last active composite set composite [lrange $composite 0 end-1] if {$insert} { Object instmixin delete [self class]::ChildManager } } if {$errorOccurred} {error $errorMsg} } if {$::tcl_version < 8.6} { # # It seems that the scripted emulation of "try" in Tcl 8.5 is not # fully compatible with 8.6, so we fall back to the prior # implementation of the method contains, that does NOT use try. # OrderedComposite instproc contains cmds { :requireNamespace ;# legacy for older XOTcl versions set m [Object info instmixin] if {"[self class]::ChildManager" ni $m} { set insert 1 Object instmixin add [self class]::ChildManager } else { set insert 0 } # [self class]::ChildManager instvar composite # push the active composite lappend composite [self] # Check, if we have Tcl's apply cmd available (not the old OpenACS apply proc) if {[info commands ::apply] ne "" && [info procs ::apply] eq ""} { set errorOccurred [catch {::apply [list {} $cmds [self]]} errorMsg] } else { set errorOccurred [catch {namespace eval [self] $cmds} errorMsg] } # pop the last active composite set composite [lrange $composite 0 end-1] if {$insert} { Object instmixin delete [self class]::ChildManager } if {$errorOccurred} {error $errorMsg} } } Class create OrderedComposite::ChildManager -instproc init args { set r [next] #set parent [self callingobject] ;# not a true calling object (ns-eval), but XOTcl 1 honors it #set parent [:info parent] ;# is ok in XOTcl 2, since the namespace is honored correctly #set parent [uplevel 2 self] ;# should work everywhere #puts stderr "-- CONTAINS p=$parent, co=[self callingobject] n=[uplevel 2 self]" # # get the top-most composite context as parent set parent [lindex [[self class] set composite] end] $parent lappend __children [self] set :__parent $parent #:__after_insert #:log "-- adding __parent $parent to [self]" return $r } Class create OrderedComposite::Child -instproc __after_insert {} {;} Class create OrderedComposite::IndexCompare OrderedComposite::IndexCompare instproc __compare {a b} { set by ${:__orderby} set x [$a set $by] set y [$b set $by] #:log "--value compare $x $y] => [:__value_compare $x $y 0]" return [:__value_compare $x $y 0] } OrderedComposite::IndexCompare instproc __value_compare {x y def} { set xp [string first . $x] set yp [string first . $y] if {$xp == -1 && $yp == -1} { if {$x < $y} { return -1 } elseif {$x > $y} { return 1 } else { return $def } } elseif {$xp == -1} { set yh [string range $y 0 $yp-1] return [:__value_compare $x $yh -1] } elseif {$yp == -1} { set xh [string range $x 0 $xp-1] return [:__value_compare $xh $y 1] } else { set xh [string range $x 0 $xp] set yh [string range $y 0 $yp] #:log "xh=$xh yh=$yh" if {$xh < $yh} { return -1 } elseif {$xh > $yh} { return 1 } else { incr xp incr yp #:log "rest [string range $x $xp end] [string range $y $yp end]" return [:__value_compare [string range $x $xp end] [string range $y $yp end] $def] } } } Class create OrderedComposite::MethodCompare OrderedComposite::MethodCompare instproc __compare {a b} { set by ${:__orderby} set x [$a $by] set y [$b $by] if {$x < $y} { return -1 } elseif {$x > $y} { return 1 } else { return 0 } } } ::xo::library source_dependent # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: