namespace eval ::xotcl { # # By setting the variable bootstrap, we can check later, whether we # are in bootstrapping mode # set bootstrap 1 } # # First create the ::xotcl2 object system. # namespace eval xotcl2 { namespace path ::xotcl ::xotcl::createobjectsystem ::xotcl2::Object ::xotcl2::Class # provide the standard command set for ::xotcl2::Object foreach cmd [info command ::xotcl::cmd::Object::*] { set cmdName [namespace tail $cmd] if {$cmdName in [list "instvar"]} continue ::xotcl::alias Object $cmdName $cmd } # provide some Tcl-commands as methods for ::xotcl2::Object #foreach cmd {array append eval incr lappend set subst unset trace} { # ::xotcl::alias Object $cmd -objscope ::$cmd #} ::xotcl::alias Object eval -nonleaf ::eval # provide the standard command set for Class foreach cmd [info command ::xotcl::cmd::Class::*] { set cmdName [namespace tail $cmd] ::xotcl::alias Class $cmdName $cmd } # set a few aliases as protected foreach cmd [list __next cleanup noinit residualargs uplevel upvar] { ::xotcl::methodproperty Object $cmd protected 1 } foreach cmd [list recreate] { ::xotcl::methodproperty Class $cmd protected 1 } # TODO: info methods shows finally "slots" and "slot". Wanted? # protect some methods against redefinition ::xotcl::methodproperty Object destroy redefine-protected true ::xotcl::methodproperty Class alloc redefine-protected true ::xotcl::methodproperty Class dealloc redefine-protected true ::xotcl::methodproperty Class create redefine-protected true # define method "method" for Class and Object ::xotcl::method Class method { name arguments body -precondition -postcondition } { set conditions [list] if {[info exists precondition]} {lappend conditions -precondition $precondition} if {[info exists postcondition]} {lappend conditions -postcondition $postcondition} ::xotcl::method [self] $name $arguments $body {*}$conditions } ::xotcl::method Object method { name arguments body -precondition -postcondition } { set conditions [list] if {[info exists precondition]} {lappend conditions -precondition $precondition} if {[info exists postcondition]} {lappend conditions -postcondition $postcondition} ::xotcl::method [self] -per-object $name $arguments $body {*}$conditions } # define method modifiers "object", "public" and "protected" Class eval { # method-modifier for object specific methos :method object {what args} { if {$what in [list "alias" "forward" "method" "setter"]} { return [::xotcl::dispatch [self] ::xotcl::classes::xotcl2::Object::$what {*}$args] } if {$what in [list "info"]} { return [::xotcl2::objectInfo [lindex $args 0] [self] {*}[lrange $args 1 end]] } if {$what in [list "filter" "mixin"]} { return [:object-$what {*}$args] } if {$what in [list "filterguard" "mixinguard"]} { return [::xotcl::dispatch [self] ::xotcl::cmd::Object::$what {*}$args] } } # define unknown handler for class :method unknown {m args} { error "Method '$m' unknown for [self].\ Consider '[self] create $m $args' instead of '[self] $m $args'" } # protected is not jet defined ::xotcl::methodproperty [self] unknown protected 1 } Object eval { # method modifier "public" :method public {args} { set p [lsearch -regexp $args {^(method|alias|forward|setter)$}] if {$p == -1} {error "$args is not a method defining method"} set r [{*}:$args] ::xotcl::methodproperty [self] $r protected false return $r } # method modifier "protected" :method protected {args} { set p [lsearch -regexp $args {^(method|alias|forward|setter)$}] if {$p == -1} {error "$args is not a method defining command"} set r [{*}:$args] ::xotcl::methodproperty [self] $r [self proc] true return $r } # unknown handler for Object :protected method unknown {m args} { if {![self isnext]} { error "[self]: unable to dispatch method '$m'" } } # "init" must exist on Object. per default it is empty. :protected method init args {} # this method is called on calls to object without a specified method :protected method defaultmethod {} {::xotcl::self} # provide a placeholder for the bootup process. The real definition # is based on slots, which are not available at this point. :protected method objectparameter {} {;} } # define forward methods ::xotcl::forward Object forward ::xotcl::forward %self -per-object ::xotcl::forward Class forward ::xotcl::forward %self # The method __unknown is called in cases, where we try to resolve # an unkown class. one could define a custom resolver with this name # to load the class on the fly. After the call to __unknown, XOTcl # tries to resolve the class again. This meachnism is used e.g. by # the ::ttrace mechanism for partial loading by Zoran. # Class protected object method __unknown {name} {} # Add alias methods. cmdName for XOTcl method can be added via # [... info method name ] # Object public method alias {-objscope:switch methodName cmd} { ::xotcl::alias [self] -per-object $methodName \ {*}[expr {${objscope} ? "-objscope" : ""}] \ $cmd } Class public method alias {-objscope:switch methodName cmd} { ::xotcl::alias [self] $methodName \ {*}[expr {${objscope} ? "-objscope" : ""}] \ $cmd } # Add setter methods. # Object public method setter {methodName value:optional} { if {[info exists value]} { ::xotcl::setter [self] -per-object $methodName $value } else { ::xotcl::setter [self] -per-object $methodName } } Class public method setter {methodName value:optional} { if {[info exists value]} { ::xotcl::setter [self] $methodName $value } else { ::xotcl::setter [self] $methodName } } ######################## # Info definition ######################## Object create ::xotcl2::objectInfo Object create ::xotcl2::classInfo # # It would be nice to do here "objectInfo configure {alias ..}", but # we have no working objectparameter yet due to bootstrapping # objectInfo eval { :alias is ::xotcl::is # info info :public method info {obj} { set methods [list] foreach name [::xotcl::cmd::ObjectInfo::methods [self]] { if {$name eq "unknown"} continue lappend methods $name } return "valid options are: [join [lsort $methods] {, }]" } :method unknown {method obj args} { error "[::xotcl::self] unknown info option \"$method\"; [$obj info info]" } } classInfo eval { :public method mixinof {obj -closure:switch {-scope all} pattern:optional} { # scope eq "all" or "object" returns objects, scope eq "class" returns classes set withClosure [expr {$closure ? "-closure" : ""}] set withPattern [expr {[info exists pattern] ? $pattern : ""}] if {$scope eq "all"} { set r [::xotcl::cmd::ClassInfo::object-mixin-of $obj {*}$withClosure {*}$withPattern] foreach c [::xotcl::cmd::ClassInfo::class-mixin-of $obj {*}$withClosure] { lappend r {*}[$c info instances {*}$withPattern] } return [lsort -unique $r] } else { return [::xotcl::cmd::ClassInfo::$scope-mixin-of $obj {*}$withClosure {*}$withPattern] } } :alias is ::xotcl::is :alias classparent ::xotcl::cmd::ObjectInfo::parent :alias classchildren ::xotcl::cmd::ObjectInfo::children :alias info [::xotcl::cmd::ObjectInfo::method objectInfo name info] :alias unknown [::xotcl::cmd::ObjectInfo::method objectInfo name info] } foreach cmd [info command ::xotcl::cmd::ObjectInfo::*] { ::xotcl::alias ::xotcl2::objectInfo [namespace tail $cmd] $cmd ::xotcl::alias ::xotcl2::classInfo [namespace tail $cmd] $cmd } foreach cmd [info command ::xotcl::cmd::ClassInfo::*] { set cmdName [namespace tail $cmd] if {$cmdName in [list "object-mixin-of" "class-mixin-of"]} continue ::xotcl::alias ::xotcl2::classInfo $cmdName $cmd } unset cmd # register method "info" on Object and Class Object forward info -onerror ::xotcl::infoError ::xotcl2::objectInfo %1 {%@2 %self} Class forward info -onerror ::xotcl::infoError ::xotcl2::classInfo %1 {%@2 %self} proc ::xotcl::infoError msg { #puts stderr "INFO ERROR: <$msg>\n$::errorInfo" regsub -all " " $msg "" msg regsub -all " " $msg "" msg regsub {\"} $msg "\"info " msg error $msg "" } # # definition of "abstract method foo ...." # Object method abstract {methtype -per-object:switch methname arglist} { if {$methtype ne "method"} { error "invalid method type '$methtype', must be 'method'" } set body " if {!\[::xotcl::self isnextcall\]} { error \"Abstract method $methname $arglist called\" } else {::xotcl::next} " if {${per-object}} { :method -per-object $methname $arglist $body } else { :method $methname $arglist $body } } # # exit handlers # proc ::xotcl::unsetExitHandler {} { proc ::xotcl::__exitHandler {} { # clients should append exit handlers to this proc body } } proc ::xotcl::setExitHandler {newbody} {::proc ::xotcl::__exitHandler {} $newbody} proc ::xotcl::getExitHandler {} {::info body ::xotcl::__exitHandler} # initialize exit handler ::xotcl::unsetExitHandler namespace export Object Class } ######################################## # Slot definitions ######################################## namespace eval ::xotcl { # # We are in bootstrap code; we cannot use slots/parameter to define # slots, so the code is a little low level. After the defintion of # the slots, we can use slot-based code such as "-parameter" or # "objectparameter". # ::xotcl2::Class create ::xotcl::MetaSlot ::xotcl::relation ::xotcl::MetaSlot superclass ::xotcl2::Class # ::xotcl::MetaSlot public method new args { # set slotobject [::xotcl::self callingobject]::slot # if {![::xotcl::is $slotobject object]} {::xotcls::Object create $slotobject} # eval next -childof $slotobject $args # } ::xotcl::MetaSlot create ::xotcl::Slot ::xotcl::MetaSlot create ::xotcl::ObjectParameterSlot ::xotcl::relation ::xotcl::ObjectParameterSlot superclass ::xotcl::Slot # We have no working objectparameter yet. So invalidate MetaSlot to # avoid caching. ::xotcl::MetaSlot __invalidateobjectparameter #foreach o {::xotcl::MetaSlot ::xotcl2::ObjectParameterSlot} { # foreach r {object class metaclass} { # puts stderr "$o $r=[::xotcl::is $o $r]" # } #} # Provide the a slot based mechanism for building an object # configuration interface from slot definitions ::xotcl::ObjectParameterSlot method toParameterSyntax {name} { set objparamdefinition $name set methodparamdefinition "" set objopts [list] set methodopts [list] if {[info exists :required] && ${:required}} { lappend objopts required lappend methodopts required } if {[info exists :type]} { if {[string match ::* ${:type}]} { lappend objopts object type=${:type} lappend methodopts object type=${:type} } else { lappend objopts ${:type} lappend methodopts ${:type} } } # TODO: remove multivalued check on relations by handling multivalued # not in relation, but in the converters if {[info exists :multivalued] && ${:multivalued}} { if {!([info exists :type] && ${:type} eq "relation")} { lappend objopts multivalued } else { #puts stderr "ignore multivalued for $name in relation" } } if {[info exists :arg]} { lappend objopts arg=${:arg} lappend methodopts arg=${:arg} } if {[info exists :default]} { set arg ${:default} # deactivated for now: || [string first {$} $arg] > -1 if {[string match {*\[*\]*} $arg]} { lappend objopts substdefault } } elseif {[info exists :initcmd]} { set arg ${:initcmd} lappend objopts initcmd } if {[info exists :methodname]} { if {${:methodname} ne ${:name}} { lappend objopts arg=${:methodname} lappend methodopts arg=${:methodname} #puts stderr "..... setting arg for methodname: $slot has arg arg=${:methodname}" } } if {[llength $objopts] > 0} { append objparamdefinition :[join $objopts ,] } if {[llength $methodopts] > 0} { set methodparamdefinition [join $methodopts ,] } if {[info exists arg]} { lappend objparamdefinition $arg } #puts stderr "[self proc] ${name} returns [list oparam $objparamdefinition mparam $methodparamdefinition]" return [list oparam $objparamdefinition mparam $methodparamdefinition] } proc ::xotcl::parametersFromSlots {obj} { set parameterdefinitions [list] foreach slot [::xotcl2::objectInfo slotobjects $obj] { # Skip some slots for xotcl1; # TODO: maybe different parameterFromSlots for xotcl1? if {[::xotcl::is $obj type ::xotcl::Object] && ([$slot name] eq "mixin" || [$slot name] eq "filter") } continue set name [namespace tail $slot] array set "" [$slot toParameterSyntax $name] lappend parameterdefinitions -$(oparam) } return $parameterdefinitions } ::xotcl2::Object protected method objectparameter {} { set parameterdefinitions [::xotcl::parametersFromSlots [self]] if {[::xotcl::is [self] class]} { lappend parameterdefinitions -parameter:method,optional } lappend parameterdefinitions \ -noinit:method,optional,noarg \ -volatile:method,optional,noarg \ arg:initcmd,optional # for the time being, use: #lappend parameterdefinitions args #puts stderr "*** parameter definition for [self]: $parameterdefinitions" return $parameterdefinitions } # # create class and object for method parameter slots ::xotcl::MetaSlot create ::xotcl::MethodParameterSlot ::xotcl::relation ::xotcl::MethodParameterSlot superclass ::xotcl::Slot foreach cmd [info command ::xotcl::cmd::MethodParameterSlot::*] { ::xotcl::alias ::xotcl::MethodParameterSlot [namespace tail $cmd] $cmd } # create an object for dispatching ::xotcl::MethodParameterSlot create ::xotcl::methodParameterSlot # use low level interface for defining slot values. Normally, this is # done via slot objects, which are defined later. proc createBootstrapAttributeSlots {class definitions} { if {![::xotcl::is ${class}::slot object]} { ::xotcl2::Object create ${class}::slot } foreach att $definitions { if {[llength $att]>1} {foreach {att default} $att break} ::xotcl::ObjectParameterSlot create ${class}::slot::$att if {[info exists default]} { ::xotcl::setinstvar ${class}::slot::$att default $default unset default } ::xotcl::setter $class $att } # do a second round to ensure that the already defined objects # have the appropriate default values foreach att $definitions { if {[llength $att]>1} {foreach {att default} $att break} if {[info exists default]} { # checking subclasses is not required during bootstrap # todo: do we really need $class twice? foreach i [::xotcl::cmd::ClassInfo::instances $class] { if {![$i exists $att]} { if {[string match {*[*]*} $default]} { #set default [$i eval subst $default] set default [::xotcl::dispatch $i -objscope ::eval subst $default] } ::xotcl::setinstvar $i $att $default } } unset default } } #puts stderr "Bootstrapslot for $class calls __invalidateobjectparameter" $class __invalidateobjectparameter } ############################################ # Define slots for slots ############################################ createBootstrapAttributeSlots ::xotcl::Slot { {name} {multivalued false} {required false} default type } createBootstrapAttributeSlots ::xotcl::ObjectParameterSlot { {name "[namespace tail [::xotcl::self]]"} {methodname} {domain "[lindex [regexp -inline {^(.*)::slot::[^:]+$} [::xotcl::self]] 1]"} {defaultmethods {get assign}} {manager "[::xotcl::self]"} {per-object false} } # maybe add the following slots at some later time here # initcmd # valuecmd # valuechangedcmd ::xotcl::alias ::xotcl::ObjectParameterSlot get ::xotcl::setinstvar ::xotcl::alias ::xotcl::ObjectParameterSlot assign ::xotcl::setinstvar ::xotcl::ObjectParameterSlot public method add {obj prop value {pos 0}} { if {![set :multivalued]} { error "Property $prop of [set :domain]->$obj ist not multivalued" } if {[$obj exists $prop]} { ::xotcl::setinstvar $obj $prop [linsert [::xotcl::setinstvar $obj $prop] $pos $value] } else { ::xotcl::setinstvar $obj $prop [list $value] } } ::xotcl::ObjectParameterSlot public method delete {-nocomplain:switch obj prop value} { set old [::xotcl::setinstvar $obj $prop] set p [lsearch -glob $old $value] if {$p>-1} {::xotcl::setinstvar $obj $prop [lreplace $old $p $p]} else { error "$value is not a $prop of $obj (valid are: $old)" } } ::xotcl::ObjectParameterSlot method unknown {method args} { set methods [list] foreach m [:info callable] { if {[::xotcl2::Object info callable $m] ne ""} continue if {[string match __* $m]} continue lappend methods $m } error "Method '$method' unknown for slot [::xotcl::self]; valid are: {[lsort $methods]]}" } ::xotcl::ObjectParameterSlot public method destroy {} { if {${:domain} ne "" && [::xotcl::is ${:domain} object]} { ${:domain} __invalidateobjectparameter } next } ::xotcl::ObjectParameterSlot protected method init {args} { if {${:domain} eq ""} { set :domain [::xotcl::self callingobject] } if {${:domain} ne ""} { if {![info exists :methodname]} { set :methodname ${:name} } ${:domain} __invalidateobjectparameter set cl [expr {${:per-object} ? "Object" : "Class"}] # since the domain object might be xotcl1 or xotcl2, use dispatch ::xotcl::forward ${:domain} ${:name} \ ${:manager} \ [list %1 [${:manager} defaultmethods]] %self \ ${:methodname} } } ############################################ # RelationSlot ############################################ ::xotcl::MetaSlot create ::xotcl::RelationSlot createBootstrapAttributeSlots ::xotcl::RelationSlot { {multivalued true} {type relation} {elementtype ::xotcl2::Class} } ::xotcl::relation ::xotcl::RelationSlot superclass ::xotcl::ObjectParameterSlot ::xotcl::alias ::xotcl::RelationSlot assign ::xotcl::relation ::xotcl::RelationSlot protected method init {} { if {${:type} ne "relation"} { error "RelationSlot requires type == \"relation\"" } next } ::xotcl::RelationSlot protected method delete_value {obj prop old value} { if {[string first * $value] > -1 || [string first \[ $value] > -1} { # value contains globbing meta characters if {${:elementtype} ne "" && ![string match ::* $value]} { # prefix glob pattern with ::, since all object names have leading :: set value ::$value } return [lsearch -all -not -glob -inline $old $value] } elseif {${:elementtype} ne ""} { # value contains no globbing meta characters, but elementtype is given if {[string first :: $value] == -1} { # get fully qualified name if {![::xotcl::is $value object]} { error "$value does not appear to be an object" } set value [::xotcl::dispatch $value -objscope ::xotcl::self] } if {![::xotcl::is ${:elementtype} class]} { error "$value does not appear to be of type ${:elementtype}" } } set p [lsearch -exact $old $value] if {$p > -1} { return [lreplace $old $p $p] } else { error "$value is not a $prop of $obj (valid are: $old)" } } ::xotcl::RelationSlot public method delete {-nocomplain:switch obj prop value} { #puts stderr RelationSlot-delete-[self args] $obj $prop [:delete_value $obj $prop [$obj info $prop] $value] } ::xotcl::RelationSlot public method get {obj prop} { ::xotcl::relation $obj $prop } ::xotcl::RelationSlot public method add {obj prop value {pos 0}} { if {![set :multivalued]} { error "Property $prop of ${:domain}->$obj ist not multivalued" } set oldSetting [::xotcl::relation $obj $prop] # use uplevel to avoid namespace surprises uplevel [list ::xotcl::relation $obj $prop [linsert $oldSetting $pos $value]] } ::xotcl::RelationSlot public method delete {-nocomplain:switch obj prop value} { uplevel [list ::xotcl::relation $obj $prop [:delete_value $obj $prop [::xotcl::relation $obj $prop] $value]] } ############################################ # system slots ############################################ proc ::xotcl::register_system_slots {os} { ${os}::Object alloc ${os}::Class::slot ${os}::Object alloc ${os}::Object::slot ::xotcl::RelationSlot create ${os}::Class::slot::superclass ::xotcl::alias ${os}::Class::slot::superclass assign ::xotcl::relation ::xotcl::RelationSlot create ${os}::Object::slot::class -multivalued false ::xotcl::alias ${os}::Object::slot::class assign ::xotcl::relation ::xotcl::RelationSlot create ${os}::Object::slot::mixin -methodname object-mixin ::xotcl::RelationSlot create ${os}::Object::slot::filter -elementtype "" ::xotcl::RelationSlot create ${os}::Class::slot::mixin -methodname class-mixin ::xotcl::RelationSlot create ${os}::Class::slot::filter -elementtype "" \ -methodname filter-mixin # Create two conveniance slots to allow configuration of # object-slots for classes via object-mixin ::xotcl::RelationSlot create ${os}::Class::slot::object-mixin ::xotcl::RelationSlot create ${os}::Class::slot::object-filter -elementtype "" } ::xotcl::register_system_slots ::xotcl2 proc ::xotcl::register_system_slots {} {} ############################################ # Attribute slots ############################################ ::xotcl::MetaSlot __invalidateobjectparameter ::xotcl::MetaSlot create ::xotcl::Attribute -superclass ::xotcl::ObjectParameterSlot createBootstrapAttributeSlots ::xotcl::Attribute { {value_check once} initcmd valuecmd valuechangedcmd arg } ::xotcl::Attribute object method createFromParameterSyntax {target value default:optional} { set opts [list] set colonPos [string first : $value] if {$colonPos == -1} { set name $value } else { set properties [string range $value [expr {$colonPos+1}] end] set name [string range $value 0 [expr {$colonPos -1}]] foreach property [split $properties ,] { if {$property eq "required"} { lappend opts -required 1 } elseif {$property eq "multivalued"} { lappend opts -multivalued 1 } elseif {[string match type=* $property]} { set type [string range $property 5 end] if {![string match ::* $type]} {set type ::$type} } elseif {[string match arg=* $property]} { set argument [string range $property 4 end] lappend opts -arg $argument } else { set type $property } } } if {[info exists type]} { lappend opts -type $type } if {[info exists default]} { lappend opts -default $default } ::xotcl::Attribute create ${target}::slot::$name {*}$opts } ::xotcl::Attribute method __default_from_cmd {obj cmd var sub op} { #puts "GETVAR [::xotcl::self proc] obj=$obj cmd=$cmd, var=$var, op=$op" $obj trace remove variable $var $op [list [::xotcl::self] [::xotcl::self proc] $obj $cmd] ::xotcl::setinstvar $obj $var [$obj eval $cmd] } ::xotcl::Attribute method __value_from_cmd {obj cmd var sub op} { #puts "GETVAR [::xotcl::self proc] obj=$obj cmd=$cmd, var=$var, op=$op" ::xotcl::setinstvar $obj $var [$obj eval $cmd] } ::xotcl::Attribute method __value_changed_cmd {obj cmd var sub op} { # puts stderr "**************************" # puts "valuechanged obj=$obj cmd=$cmd, var=$var, op=$op, ...\n$obj exists $var -> [::xotcl::setinstvar $obj $var]" eval $cmd } ::xotcl::Attribute method check_single_value { {-keep_old_value:boolean true} value predicate type obj var} { #puts "+++ checking single value '$value' with $predicate ==> [expr $predicate]" if {![expr $predicate]} { if {[$obj exists __oldvalue($var)]} { ::xotcl::setinstvar $obj $var [::xotcl::setinstvar $obj __oldvalue($var)] } else { $obj unset -nocomplain $var } error "'$value' is not of type $type" } if {$keep_old_value} {::xotcl::setinstvar $obj __oldvalue($var) $value} #puts "+++ checking single value done" } ::xotcl::Attribute method check_multiple_values {values predicate type obj var} { foreach value $values { :check_single_value -keep_old_value false $value $predicate $type $obj $var } ::xotcl::setinstvar $obj __oldvalue($var) $value } ::xotcl::Attribute method mk_type_checker {} { puts stderr "[self] [self proc]" set __initcmd "" if {[:exists type]} { if {[::xotcl::is ${:type} class]} { set predicate [subst -nocommands { [::xotcl::is \$value object] && [::xotcl::is \$value type ${:type}] }] } elseif {[llength ${:type}]>1} { set predicate "\[${:type} \$value\]" } else { #set predicate "\[string is ${:type} \$value\]" set predicate "\[:type=${:type} ${:name} \$value\]" } #puts stderr predicate=$predicate append :valuechangedcmd [subst { [expr {${:multivalued} ? ":check_multiple_values" : ":check_single_value" }] \[::xotcl::setinstvar \$obj ${:name}\] \ {$predicate} [list ${:type}] \$obj ${:name} }] append __initcmd [subst -nocommands { if {[:exists ${:name}]} {set :__oldvalue(${:name}) [set :${:name}]}\n }] } return $__initcmd } ::xotcl::Attribute protected method init {} { next ;# do first ordinary slot initialization # there might be already default values registered on the class set __initcmd "" if {[:exists default]} { } elseif [:exists initcmd] { append __initcmd ":trace add variable [list ${:name}] read \ \[list [::xotcl::self] __default_from_cmd \[::xotcl::self\] [list [set :initcmd]]\]\n" } elseif [:exists valuecmd] { append __initcmd ":trace add variable [list ${:name}] read \ \[list [::xotcl::self] __value_from_cmd \[::xotcl::self\] [list [set :valuecmd]]\]" } array set "" [:toParameterSyntax "value"] #puts stderr "Attribute.init valueParam for [self] is $(mparam)" if {$(mparam) ne ""} { if {[info exists :multivalued] && ${:multivalued}} { #puts stderr "adding assign [list obj var value:$(mparam),multivalued] // for [self] with $(mparam)" :method assign [list obj var value:$(mparam),multivalued] {::xotcl::setinstvar $obj $var $value} #puts stderr "adding add method for [self] with value:$(mparam)" :method add [list obj prop value:$(mparam) {pos 0}] {next} } else { #puts stderr "adding assign [list obj var value:$(mparam)] // for [self] with $(mparam)" :method assign [list obj var value:$(mparam)] {::xotcl::setinstvar $obj $var $value} } } #append __initcmd [:mk_type_checker] if {[:exists valuechangedcmd]} { append __initcmd ":trace add variable [list ${:name}] write \ \[list [::xotcl::self] __value_changed_cmd \[::xotcl::self\] [list [set :valuechangedcmd]]\]" } if {$__initcmd ne ""} { set :initcmd $__initcmd } } # mixin class for decativating all value checks in slots ::xotcl2::Class create ::xotcl::Attribute::Nocheck { :method check_single_value args {;} :method check_multiple_values args {;} :method mk_type_checker args {return ""} } # mixin class for optimizing slots ::xotcl2::Class create ::xotcl::Attribute::Optimizer { :method method args {::xotcl::next; :optimize} :method forward args {::xotcl::next; :optimize} :protected method init args {::xotcl::next; :optimize} :public method optimize {} { #puts stderr OPTIMIZER if {[set :multivalued]} return if {[set :defaultmethods] ne {get assign}} return #puts stderr assign=[:info callable -which assign] if {[:info callable -which assign] ne "::xotcl::ObjectParameterSlot alias assign ::xotcl::setinstvar"} return if {[:info callable -which get] ne "::xotcl::ObjectParameterSlot alias get ::xotcl::setinstvar"} return #puts stderr "**** optimizing [${:domain} info method definition ${:name}]" ::xotcl::setter ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] ${:name} } } # register the optimizer per default ::xotcl::Attribute mixin add ::xotcl::Attribute::Optimizer ############################################ # Define method "parameter" for backward # compatibility and convenience ############################################ ::xotcl2::Class public method parameter arglist { # create subobject "slot" if necessary if {![::xotcl::is [::xotcl::self]::slot object]} { ::xotcl2::Object create [::xotcl::self]::slot } foreach arg $arglist { ::xotcl::Attribute createFromParameterSyntax [self] {*}$arg } # todo needed? ::xotcl::setinstvar [::xotcl::self]::slot __parameter $arglist } ################################################################## # now the slots are defined; now we can defines the Objects or # classes with parameters more easily. ################################################################## proc createBootstrapAttributeSlots {} {} } # Create a mixin class to overload method "new", such it does not allocate # new objects in ::xotcl::*, but in the specified object (without # syntactic overhead). # ::xotcl2::Class create ::xotcl::ScopedNew -superclass ::xotcl2::Class -parameter { {withclass ::xotcl2::Object} inobject } ::xotcl::ScopedNew protected method init {} { :public method new {-childof args} { ::xotcl::importvar [::xotcl::self class] {inobject object} withclass if {![::xotcl::is $object object]} { $withclass create $object } eval ::xotcl::next -childof $object $args } } # # The method 'contains' changes the namespace in which objects with # realtive names are created. Therefore, 'contains' provides a # friendly notation for creating nested object structures. Optionally, # creating new objects in the specified scope can be turned off. # ::xotcl2::Object public method contains { {-withnew:boolean true} -object {-class ::xotcl2::Object} cmds } { if {![info exists object]} {set object [::xotcl::self]} if {![::xotcl::is $object object]} {$class create $object} $object requireNamespace if {$withnew} { set m [::xotcl::ScopedNew new \ -inobject $object -withclass $class -volatile] ::xotcl2::Class mixin add $m end namespace eval $object $cmds ::xotcl2::Class mixin delete $m } else { namespace eval $object $cmds } } ::xotcl2::Class forward slots %self contains \ -object {%::xotcl::dispatch [::xotcl::self] -objscope ::subst [::xotcl::self]::slot} # # copy/move implementation # ::xotcl2::Class create ::xotcl::CopyHandler -parameter { {targetList ""} {dest ""} objLength } { :method makeTargetList {t} { lappend :targetList $t # if it is an object without namespace, it is a leaf if {[::xotcl::is $t object]} { if {[$t info hasnamespace]} { # make target list from all children set children [$t info children] } else { # ok, no namespace -> no more children return } } # now append all namespaces that are in the obj, but that # are not objects foreach c [namespace children $t] { if {![::xotcl::is $c object]} { lappend children [namespace children $t] } } # a namespace or an obj with namespace may have children # itself foreach c $children { :makeTargetList $c } } :method copyNSVarsAndCmds {orig dest} { ::xotcl::namespace_copyvars $orig $dest ::xotcl::namespace_copycmds $orig $dest } # construct destination obj name from old qualified ns name :method getDest origin { set tail [string range $origin [set :objLength] end] return ::[string trimleft [set :dest]$tail :] } :method copyTargets {} { #puts stderr "COPY will copy targetList = [set :targetList]" foreach origin [set :targetList] { set dest [:getDest $origin] if {[::xotcl::is $origin object]} { # copy class information if {[::xotcl::is $origin class]} { set cl [[$origin info class] create $dest -noinit] # class object set obj $cl $cl superclass [$origin info superclass] ::xotcl::assertion $cl class-invar [::xotcl::assertion $origin class-invar] ::xotcl::relation $cl class-filter [::xotcl::relation $origin class-filter] ::xotcl::relation $cl class-mixin [::xotcl::relation $origin class-mixin] :copyNSVarsAndCmds ::xotcl::classes$origin ::xotcl::classes$dest } else { # create obj set obj [[$origin info class] create $dest -noinit] } # copy object -> may be a class obj ::xotcl::assertion $obj check [::xotcl::assertion $origin check] ::xotcl::assertion $obj object-invar [::xotcl::assertion $origin object-invar] ::xotcl::relation $obj object-filter [::xotcl::relation $origin object-filter] ::xotcl::relation $obj object-mixin [::xotcl::relation $origin object-mixin] if {[$origin info hasnamespace]} { $obj requireNamespace } } else { namespace eval $dest {} } :copyNSVarsAndCmds $origin $dest foreach i [$origin info forward] { eval [concat $dest forward $i [$origin info forward -definition $i]] } if {[::xotcl::is $origin class]} { foreach i [$origin info instforward] { eval [concat $dest instforward $i [$origin info instforward -definition $i]] } } set traces [list] foreach var [$origin info vars] { set cmds [::xotcl::dispatch $origin -objscope ::trace info variable $var] if {$cmds ne ""} { foreach cmd $cmds { foreach {op def} $cmd break #$origin trace remove variable $var $op $def if {[lindex $def 0] eq $origin} { set def [concat $dest [lrange $def 1 end]] } $dest trace add variable $var $op $def } } } #puts stderr "=====" } # alter 'domain' and 'manager' in slot objects for classes foreach origin [set :targetList] { if {[::xotcl::is $origin class]} { set dest [:getDest $origin] foreach oldslot [$origin info slots] { set newslot ${dest}::slot::[namespace tail $oldslot] if {[$oldslot domain] eq $origin} {$newslot domain $cl} if {[$oldslot manager] eq $oldslot} {$newslot manager $newslot} } } } } :public method copy {obj dest} { #puts stderr "[::xotcl::self] copy <$obj> <$dest>" set :objLength [string length $obj] set :dest $dest :makeTargetList $obj :copyTargets } } ::xotcl2::Object public method copy newName { if {[string compare [string trimleft $newName :] [string trimleft [::xotcl::self] :]]} { [::xotcl::CopyHandler new -volatile] copy [::xotcl::self] $newName } } ::xotcl2::Object public method move newName { if {[string trimleft $newName :] ne [string trimleft [::xotcl::self] :]} { if {$newName ne ""} { :copy $newName } ### let all subclasses get the copied class as superclass if {[::xotcl::is [::xotcl::self] class] && $newName ne ""} { foreach subclass [:info subclass] { set scl [$subclass info superclass] if {[set index [lsearch -exact $scl [::xotcl::self]]] != -1} { set scl [lreplace $scl $index $index $newName] $subclass superclass $scl } } } :destroy } } ####################################################### # some utilities ####################################################### # documentation stub object -> just ignore per default. # if xoDoc is loaded, documentation will be activated ::xotcl2::Object create ::xotcl::@ { :method unknown args {} } ####################################################################### # common code for all xotcl versions namespace eval ::xotcl { # export the contents for all xotcl versions namespace export @ Attribute # if HOME is not set, and ~ is resolved, Tcl chokes on that if {![info exists ::env(HOME)]} {set ::env(HOME) /root} set ::xotcl::confdir ~/.xotcl set ::xotcl::logdir $::xotcl::confdir/log # return platform aware temp directory proc tmpdir {} { foreach e [list TMPDIR TEMP TMP] { if {[info exists ::env($e)] \ && [file isdirectory $::env($e)] \ && [file writable $::env($e)]} { return $::env($e) } } if {$::tcl_platform(platform) eq "windows"} { foreach d [list "C:\\TEMP" "C:\\TMP" "\\TEMP" "\\TMP"] { if {[file isdirectory $d] && [file writable $d]} { return $d } } } return /tmp } proc use {version} { set callingNs [uplevel {namespace current}] switch -exact $version { xotcl1 { package require xotcl1 puts stderr "current=[namespace current], ul=[uplevel {namespace current}]" if {$callingNs ne "::xotcl"} {uplevel {namespace import -force ::xotcl::*}} } default { if {$callingNs ne "::xotcl"} {uplevel {namespace import -force ::xotcl::*}} if {$callingNs ne "::xotcl2"} {uplevel {namespace import -force ::xotcl2::*}} } } } unset bootstrap }