namespace eval ::next { # # By setting the variable bootstrap, we can check later, whether we # are in bootstrapping mode # set bootstrap 1 #namespace path ::xotcl # # First create the ::next object system. # ::next::core::createobjectsystem ::next::Object ::next::Class { -class.alloc alloc -class.create create -class.dealloc dealloc -class.recreate recreate -class.requireobject __unknown -object.configure configure -object.defaultmethod defaultmethod -object.destroy destroy -object.init init -object.move move -object.objectparameter objectparameter -object.residualargs residualargs -object.unknown unknown } # # get frequenly used primitiva into the ::next namespace # namespace eval ::next::core { namespace export next self \ my is relation interp } namespace import ::next::core::next ::next::core::self # # provide the standard command set for ::next::Object # foreach cmd [info command ::next::core::cmd::Object::*] { set cmdName [namespace tail $cmd] if {$cmdName in [list "instvar"]} continue ::next::core::alias Object $cmdName $cmd } # provide ::eval as method for ::next::Object ::next::core::alias Object eval -nonleaf ::eval # provide the standard command set for Class foreach cmd [info command ::next::core::cmd::Class::*] { set cmdName [namespace tail $cmd] ::next::core::alias Class $cmdName $cmd } # set a few aliases as protected foreach cmd [list __next cleanup noinit residualargs uplevel upvar] { ::next::core::methodproperty Object $cmd protected 1 } foreach cmd [list recreate] { ::next::core::methodproperty Class $cmd protected 1 } # TODO: info methods shows finally "slots" and "slot". Wanted? # protect some methods against redefinition ::next::core::methodproperty Object destroy redefine-protected true ::next::core::methodproperty Class alloc redefine-protected true ::next::core::methodproperty Class dealloc redefine-protected true ::next::core::methodproperty Class create redefine-protected true # define method "method" for Class and Object ::next::core::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} ::next::core::method [::next::core::current object] $name $arguments $body {*}$conditions } ::next::core::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} ::next::core::method [::next::core::current object] -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" "attribute" "forward" "method" "setter"]} { return [::next::core::dispatch [::next::core::current object] ::next::core::classes::next::Object::$what {*}$args] } if {$what in [list "info"]} { return [::next::objectInfo [lindex $args 0] [::next::core::current object] {*}[lrange $args 1 end]] } if {$what in [list "filter" "mixin"]} { return [:object-$what {*}$args] } if {$what in [list "filterguard" "mixinguard"]} { return [::next::core::dispatch [::next::core::current object] ::next::core::cmd::Object::$what {*}$args] } } # define unknown handler for class :method unknown {m args} { error "Method '$m' unknown for [::next::core::current object].\ Consider '[::next::core::current object] create $m $args' instead of '[::next::core::current object] $m $args'" } # protected is not jet defined ::next::core::methodproperty [::next::core::current object] unknown protected 1 } Object eval { # method modifier "public" :method public {args} { set p [lsearch -regexp $args {^(method|alias|attribute|forward|setter)$}] if {$p == -1} {error "$args is not a method defining method"} set r [{*}:$args] ::next::core::methodproperty [::next::core::current object] $r protected false return $r } # method modifier "protected" :method protected {args} { set p [lsearch -regexp $args {^(method|alias|attribute|forward|setter)$}] if {$p == -1} {error "$args is not a method defining command"} set r [{*}:$args] ::next::core::methodproperty [::next::core::current object] $r [::next::core::current method] true return $r } # unknown handler for Object :protected method unknown {m args} { if {![::next::core::current isnext]} { error "[::next::core::current object]: 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 {} {::next::core::current object} # 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 ::next::core::forward Object forward ::next::core::forward %self -per-object ::next::core::forward Class forward ::next::core::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 ] # # -nonleaf and -objscope make only sense for c-defined cmds, # -objscope implies -nonleaf # Object public method alias {-nonleaf:switch -objscope:switch methodName cmd} { ::next::core::alias [::next::core::current object] -per-object $methodName \ {*}[expr {${objscope} ? "-objscope" : ""}] \ {*}[expr {${nonleaf} ? "-nonleaf" : ""}] \ $cmd } Class public method alias {-nonleaf:switch -objscope:switch methodName cmd} { ::next::core::alias [::next::core::current object] $methodName \ {*}[expr {${objscope} ? "-objscope" : ""}] \ {*}[expr {${nonleaf} ? "-nonleaf" : ""}] \ $cmd } # Add setter methods. # Object public method setter {methodName} { ::next::core::setter [::next::core::current object] -per-object $methodName } Class public method setter {methodName} { ::next::core::setter [::next::core::current object] $methodName } ######################## # Info definition ######################## Object create ::next::objectInfo Object create ::next::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 ::next::core::objectproperty # info info :public method info {obj} { set methods [list] foreach name [::next::core::cmd::ObjectInfo::methods [::next::core::current object]] { if {$name eq "unknown"} continue lappend methods $name } return "valid options are: [join [lsort $methods] {, }]" } :method unknown {method obj args} { error "[::next::core::current object] unknown info option \"$method\"; [$obj info info]" } } classInfo eval { :alias is ::next::core::objectproperty :alias classparent ::next::core::cmd::ObjectInfo::parent :alias classchildren ::next::core::cmd::ObjectInfo::children :alias info [::next::core::cmd::ObjectInfo::method objectInfo name info] :alias unknown [::next::core::cmd::ObjectInfo::method objectInfo name info] } foreach cmd [info command ::next::core::cmd::ObjectInfo::*] { ::next::core::alias ::next::objectInfo [namespace tail $cmd] $cmd ::next::core::alias ::next::classInfo [namespace tail $cmd] $cmd } foreach cmd [info command ::next::core::cmd::ClassInfo::*] { set cmdName [namespace tail $cmd] if {$cmdName in [list "object-mixin-of" "class-mixin-of"]} continue ::next::core::alias ::next::classInfo $cmdName $cmd } unset cmd # register method "info" on Object and Class Object forward info -onerror ::next::core::infoError ::next::objectInfo %1 {%@2 %self} Class forward info -onerror ::next::core::infoError ::next::classInfo %1 {%@2 %self} proc ::next::core::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 {!\[::next::core::current isnextcall\]} { error \"Abstract method $methname $arglist called\" } else {::next::core::next} " if {${per-object}} { :method -per-object $methname $arglist $body } else { :method $methname $arglist $body } } # # exit handlers # proc ::next::core::unsetExitHandler {} { proc ::next::core::__exitHandler {} { # clients should append exit handlers to this proc body } } proc ::next::core::setExitHandler {newbody} {::proc ::next::core::__exitHandler {} $newbody} proc ::next::core::getExitHandler {} {::info body ::next::core::__exitHandler} # initialize exit handler ::next::core::unsetExitHandler namespace export Object Class next self } ######################################## # Slot definitions ######################################## namespace eval ::next { # # 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". # ::next::Class create ::next::MetaSlot ::next::core::relation ::next::MetaSlot superclass ::next::Class ::next::MetaSlot public method slotName {name baseObject} { # Create slot parent object if needed set slotParent ${baseObject}::slot if {![::next::core::objectproperty ${slotParent} object]} { ::next::Object create ${slotParent} } return ${slotParent}::$name } ::next::MetaSlot method createFromParameterSyntax {target -per-object:switch {-initblock ""} 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 } if {${per-object}} { lappend opts -per-object true set info ObjectInfo } else { set info ClassInfo } :create [:slotName $name $target] {*}$opts $initblock return [::next::core::cmd::${info}::method $target name $name] } ::next::MetaSlot create ::next::Slot ::next::MetaSlot create ::next::ObjectParameterSlot ::next::core::relation ::next::ObjectParameterSlot superclass ::next::Slot # # create class and object for method parameter slots ::next::MetaSlot create ::next::MethodParameterSlot ::next::core::relation ::next::MethodParameterSlot superclass ::next::Slot # create an object for dispatching ::next::MethodParameterSlot create ::next::methodParameterSlot # use low level interface for defining slot values. Normally, this is # done via slot objects, which are defined later. proc createBootstrapAttributeSlots {class definitions} { foreach att $definitions { if {[llength $att]>1} {foreach {att default} $att break} set slotObj [::next::ObjectParameterSlot slotName $att $class] ::next::ObjectParameterSlot create $slotObj if {[info exists default]} { ::next::core::setvar $slotObj default $default unset default } ::next::core::setter $class $att } # # Perform a second round to set default values for already defined # objects. # foreach att $definitions { if {[llength $att]>1} {foreach {att default} $att break} if {[info exists default]} { # checking subclasses is not required during bootstrap foreach i [::next::core::cmd::ClassInfo::instances $class] { if {![$i exists $att]} { if {[string match {*\[*\]*} $default]} { set value [::next::core::dispatch $i -objscope ::eval subst $default] } else { set value $default } ::next::core::setvar $i $att $value } } unset default } } #puts stderr "Bootstrapslot for $class calls __invalidateobjectparameter" $class __invalidateobjectparameter } ############################################ # Define slots for slots ############################################ createBootstrapAttributeSlots ::next::Slot { {name} {multivalued false} {required false} default type } createBootstrapAttributeSlots ::next::ObjectParameterSlot { {name "[namespace tail [::next::core::current object]]"} {methodname} {domain "[lindex [regexp -inline {^(.*)::slot::[^:]+$} [::next::core::current object]] 1]"} {defaultmethods {get assign}} {manager "[::next::core::current object]"} {per-object false} } # maybe add the following slots at some later time here # initcmd # valuecmd # valuechangedcmd ::next::core::alias ::next::ObjectParameterSlot get ::next::core::setvar ::next::core::alias ::next::ObjectParameterSlot assign ::next::core::setvar ::next::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]} { ::next::core::setvar $obj $prop [linsert [::next::core::setvar $obj $prop] $pos $value] } else { ::next::core::setvar $obj $prop [list $value] } } ::next::ObjectParameterSlot public method delete {-nocomplain:switch obj prop value} { set old [::next::core::setvar $obj $prop] set p [lsearch -glob $old $value] if {$p>-1} {::next::core::setvar $obj $prop [lreplace $old $p $p]} else { error "$value is not a $prop of $obj (valid are: $old)" } } ::next::ObjectParameterSlot method unknown {method args} { set methods [list] foreach m [:info callable] { if {[::next::Object info callable $m] ne ""} continue if {[string match __* $m]} continue lappend methods $m } error "Method '$method' unknown for slot [::next::core::current object]; valid are: {[lsort $methods]}" } ::next::ObjectParameterSlot public method destroy {} { if {${:domain} ne "" && [::next::core::objectproperty ${:domain} class]} { ${:domain} __invalidateobjectparameter } ::next::core::next } ::next::ObjectParameterSlot protected method init {args} { if {${:domain} eq ""} { set :domain [::next::core::current callingobject] } if {${:domain} ne ""} { if {![info exists :methodname]} { set :methodname ${:name} } if {[::next::core::objectproperty ${:domain} class]} { ${:domain} __invalidateobjectparameter } if {${:per-object} && [info exists :default] } { ::next::core::setvar ${:domain} ${:name} ${:default} } set cl [expr {${:per-object} ? "Object" : "Class"}] #puts stderr "Slot [::next::core::current object] init, forwarder on ${:domain}" ::next::core::forward ${:domain} ${:name} \ ${:manager} \ [list %1 [${:manager} defaultmethods]] %self \ ${:methodname} } } ################################################################# # We have no working objectparameter yet, since it requires a # minimal slot infrastructure to build object parameters from # slots. The above definitions should be sufficient. We provide the # definition here before we refine the slot definitions. # # Invalidate previously defined object parameter. ::next::MetaSlot __invalidateobjectparameter # Provide the a slot based mechanism for building an object # configuration interface from slot definitions ::next::ObjectParameterSlot method toParameterSyntax {{name:substdefault ${:name}}} { set objparamdefinition $name set methodparamdefinition "" set objopts [list] set methodopts [list] set type "" if {[info exists :required] && ${:required}} { lappend objopts required lappend methodopts required } if {[info exists :type]} { if {[string match ::* ${:type}]} { set type [expr {[::next::core::objectproperty ${:type} metaclass] ? "class" : "object"}] lappend objopts type=${:type} lappend methodopts type=${:type} } else { set type ${: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]} { set prefix [expr {$type eq "object" || $type eq "class" ? "type" : "arg"}] lappend objopts $prefix=${:arg} lappend methodopts $prefix=${: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: [::next::core::current object] has arg arg=${:methodname}" } } if {$type ne ""} { set objopts [linsert $objopts 0 $type] set methodopts [linsert $methodopts 0 $type] } lappend objopts slot=[::next::core::current object] 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 "[::next::core::current method] ${name} returns [list oparam $objparamdefinition mparam $methodparamdefinition]" return [list oparam $objparamdefinition mparam $methodparamdefinition] } proc ::next::core::parametersFromSlots {obj} { set parameterdefinitions [list] foreach slot [::next::objectInfo slotobjects $obj] { # Skip some slots for xotcl; # TODO: maybe different parameterFromSlots for xotcl? if {[::next::core::objectproperty $obj type ::xotcl::Object] && ([$slot name] eq "mixin" || [$slot name] eq "filter") } continue array set "" [$slot toParameterSyntax] lappend parameterdefinitions -$(oparam) } return $parameterdefinitions } ::next::Object protected method objectparameter {{lastparameter __initcmd:initcmd,optional}} { #puts stderr "... objectparameter [::next::core::current object]" set parameterdefinitions [::next::core::parametersFromSlots [::next::core::current object]] if {[::next::core::objectproperty [::next::core::current object] class]} { lappend parameterdefinitions -parameter:method,optional } lappend parameterdefinitions \ -noinit:method,optional,noarg \ -volatile:method,optional,noarg \ {*}$lastparameter #puts stderr "*** parameter definition for [::next::core::current object]: $parameterdefinitions" return $parameterdefinitions } ############################################ # RelationSlot ############################################ ::next::MetaSlot create ::next::RelationSlot createBootstrapAttributeSlots ::next::RelationSlot { {multivalued true} {type relation} {elementtype ::next::Class} } ::next::core::relation ::next::RelationSlot superclass ::next::ObjectParameterSlot ::next::core::alias ::next::RelationSlot assign ::next::core::relation ::next::RelationSlot protected method init {} { if {${:type} ne "relation"} { error "RelationSlot requires type == \"relation\"" } ::next::core::next } ::next::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 {![::next::core::objectproperty $value object]} { error "$value does not appear to be an object" } set value [::next::core::dispatch $value -objscope ::next::core::current object] } if {![::next::core::objectproperty ${: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)" } } ::next::RelationSlot public method delete {-nocomplain:switch obj prop value} { #puts stderr RelationSlot-delete-[::next::core::current args] $obj $prop [:delete_value $obj $prop [$obj info $prop] $value] } ::next::RelationSlot public method get {obj prop} { ::next::core::relation $obj $prop } ::next::RelationSlot public method add {obj prop value {pos 0}} { if {![set :multivalued]} { error "Property $prop of ${:domain}->$obj ist not multivalued" } set oldSetting [::next::core::relation $obj $prop] # use uplevel to avoid namespace surprises uplevel [list ::next::core::relation $obj $prop [linsert $oldSetting $pos $value]] } ::next::RelationSlot public method delete {-nocomplain:switch obj prop value} { uplevel [list ::next::core::relation $obj $prop [:delete_value $obj $prop [::next::core::relation $obj $prop] $value]] } ############################################ # system slots ############################################ proc ::next::core::register_system_slots {os} { ${os}::Object alloc ${os}::Class::slot ${os}::Object alloc ${os}::Object::slot ::next::RelationSlot create ${os}::Class::slot::superclass ::next::core::alias ${os}::Class::slot::superclass assign ::next::core::relation ::next::RelationSlot create ${os}::Object::slot::class -multivalued false ::next::core::alias ${os}::Object::slot::class assign ::next::core::relation ::next::RelationSlot create ${os}::Object::slot::mixin -methodname object-mixin ::next::RelationSlot create ${os}::Object::slot::filter -elementtype "" ::next::RelationSlot create ${os}::Class::slot::mixin -methodname class-mixin ::next::RelationSlot create ${os}::Class::slot::filter -elementtype "" \ -methodname class-filter # Create two conveniance slots to allow configuration of # object-slots for classes via object-mixin ::next::RelationSlot create ${os}::Class::slot::object-mixin ::next::RelationSlot create ${os}::Class::slot::object-filter -elementtype "" } ::next::core::register_system_slots ::next proc ::next::core::register_system_slots {} {} ############################################ # Attribute slots ############################################ ::next::MetaSlot __invalidateobjectparameter ::next::MetaSlot create ::next::Attribute -superclass ::next::ObjectParameterSlot createBootstrapAttributeSlots ::next::Attribute { {value_check once} incremental initcmd valuecmd valuechangedcmd arg } ::next::Attribute method __default_from_cmd {obj cmd var sub op} { #puts "GETVAR [::next::core::current method] obj=$obj cmd=$cmd, var=$var, op=$op" $obj trace remove variable $var $op [list [::next::core::current object] [::next::core::current method] $obj $cmd] ::next::core::setvar $obj $var [$obj eval $cmd] } ::next::Attribute method __value_from_cmd {obj cmd var sub op} { #puts "GETVAR [::next::core::current method] obj=$obj cmd=$cmd, var=$var, op=$op" ::next::core::setvar $obj $var [$obj eval $cmd] } ::next::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 -> [::next::core::setvar $obj $var]" eval $cmd } ::next::Attribute protected method init {} { ::next::core::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 [::next::core::current object] __default_from_cmd \[::next::core::current object\] [list [set :initcmd]]\]\n" } elseif [:exists valuecmd] { append __initcmd ":trace add variable [list ${:name}] read \ \[list [::next::core::current object] __value_from_cmd \[::next::core::current object\] [list [set :valuecmd]]\]" } array set "" [:toParameterSyntax ${:name}] #puts stderr "Attribute.init valueParam for [::next::core::current object] is $(mparam)" if {$(mparam) ne ""} { if {[info exists :multivalued] && ${:multivalued}} { #puts stderr "adding assign [list obj var value:$(mparam),multivalued] // for [::next::core::current object] with $(mparam)" :method assign [list obj var value:$(mparam),multivalued,slot=[::next::core::current object]] { ::next::core::setvar $obj $var $value } #puts stderr "adding add method for [::next::core::current object] with value:$(mparam)" :method add [list obj prop value:$(mparam),slot=[::next::core::current object] {pos 0}] { ::next::core::next } } else { #puts stderr "SV adding assign [list obj var value:$(mparam)] // for [::next::core::current object] with $(mparam)" :method assign [list obj var value:$(mparam),slot=[::next::core::current object]] { ::next::core::setvar $obj $var $value } } } if {[:exists valuechangedcmd]} { append __initcmd ":trace add variable [list ${:name}] write \ \[list [::next::core::current object] __value_changed_cmd \[::next::core::current object\] [list [set :valuechangedcmd]]\]" } if {$__initcmd ne ""} { set :initcmd $__initcmd } } # mixin class for optimizing slots ::next::Class create ::next::Attribute::Optimizer { :method method args {::next::core::next; :optimize} :method forward args {::next::core::next; :optimize} :protected method init args {::next::core::next; :optimize} :public method optimize {} { #puts stderr OPTIMIZER-[info exists :incremental] if {![info exists :methodname]} {return} set object [expr {${:per-object} ? {object} : {}}] if {${:per-object}} { set perObject -per-object set infokind Object } else { set perObject "" set infokind Class } if {[::next::core::cmd::${infokind}Info::method ${:domain} name ${:name}] ne ""} { #puts stderr "OPTIMIZER RESETTING ${:domain} slot ${:name}" ::next::core::forward ${:domain} {*}$perObject ${:name} \ ${:manager} \ [list %1 [${:manager} defaultmethods]] %self \ ${:methodname} } #puts stderr "OPTIMIZER incremental [info exists :incremental] def '[set :defaultmethods]'" if {[info exists :incremental] && ${:incremental}} return if {[set :defaultmethods] ne {get assign}} return set assignInfo [:info callable -which assign] #puts stderr "OPTIMIZER assign=$assignInfo//[lindex $assignInfo {end 0}]//[:info precedence]" if {$assignInfo ne "::next::ObjectParameterSlot alias assign ::next::core::setvar" && [lindex $assignInfo {end 0}] ne "::next::core::setvar" } return if {[:info callable -which get] ne "::next::ObjectParameterSlot alias get ::next::core::setvar"} return array set "" [:toParameterSyntax ${:name}] if {$(mparam) ne ""} { set setterParam [lindex $(oparam) 0] #puts stderr "setterParam=$setterParam, op=$(oparam)" } else { set setterParam ${:name} } ::next::core::setter ${:domain} {*}$perObject $setterParam #puts stderr "::next::core::setter ${:domain} {*}$perObject $setterParam" } } # register the optimizer per default ::next::Attribute mixin add ::next::Attribute::Optimizer ############################################ # Define method "attribute" for convenience ############################################ ::next::Class method attribute {spec {-slotclass ::next::Attribute} {initblock ""}} { $slotclass createFromParameterSyntax [::next::core::current object] -initblock $initblock {*}$spec } ::next::Object method attribute {spec {-slotclass ::next::Attribute} {initblock ""}} { $slotclass createFromParameterSyntax [::next::core::current object] -per-object -initblock $initblock {*}$spec } ############################################ # Define method "parameter" for backward # compatibility and convenience ############################################ ::next::Class public method parameter arglist { foreach arg $arglist { ::next::Attribute createFromParameterSyntax [::next::core::current object] {*}$arg } # todo needed? set slot [::next::core::current object]::slot if {![::next::core::objectproperty $slot object]} {::next::Object create $slot} ::next::core::setvar $slot __parameter $arglist } ################################################################## # now the slots are defined; now we can defines the Objects or # classes with parameters more easily than above. ################################################################## # remove helper proc proc createBootstrapAttributeSlots {} {} ################################################################## # create user-level converter/checker based on ::next::core primitves ################################################################## ::next::Slot method type=hasmixin {name value arg} { if {![::next::core::objectproperty $value hasmixin $arg]} { error "expected object with mixin $arg but got \"$value\" for parameter $name" } return $value } ::next::Slot method type=baseclass {name value} { if {![::next::core::objectproperty $value baseclass]} { error "expected baseclass but got \"$value\" for parameter $name" } return $value } ::next::Slot method type=metaclass {name value} { if {![::next::core::objectproperty $value metaclass]} { error "expected metaclass but got \"$value\" for parameter $name" } return $value } } ################################################################## # Create a mixin class to overload method "new" such it does not # allocate new objects in ::next::*, but in the specified object # (without syntactic overhead). ################################################################## ::next::Class create ::next::ScopedNew -superclass ::next::Class { :attribute {withclass ::next::Object} :attribute container :protected method init {} { :public method new {-childof args} { ::next::core::importvar [::next::core::current class] {container object} withclass if {![::next::core::objectproperty $object object]} { $withclass create $object } eval ::next::core::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. ################################################################## ::next::Object public method contains { {-withnew:boolean true} -object {-class ::next::Object} cmds } { if {![info exists object]} {set object [::next::core::current object]} if {![::next::core::objectproperty $object object]} {$class create $object} $object requireNamespace if {$withnew} { set m [::next::ScopedNew new -volatile \ -container $object -withclass $class] ::next::Class mixin add $m end # TODO: the following is not pretty; however, contains might build xotcl1 and next objects. if {[::next::core::objectproperty ::xotcl::Class class]} {::xotcl::Class instmixin add $m end} namespace eval $object $cmds ::next::Class mixin delete $m if {[::next::core::objectproperty ::xotcl::Class class]} {::xotcl::Class instmixin delete $m} } else { namespace eval $object $cmds } } ::next::Class forward slots %self contains \ -object {%::next::core::dispatch [::next::core::current object] -objscope ::subst [::next::core::current object]::slot} ################################################################## # copy/move implementation ################################################################## ::next::Class create ::next::CopyHandler { :attribute {targetList ""} :attribute {dest ""} :attribute objLength :method makeTargetList {t} { lappend :targetList $t #puts stderr "COPY makeTargetList $t target= ${:targetList}" # if it is an object without namespace, it is a leaf if {[::next::core::objectproperty $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 {![::next::core::objectproperty $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} { ::next::core::namespace_copyvars $orig $dest ::next::core::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 {[::next::core::objectproperty $origin object]} { # copy class information if {[::next::core::objectproperty $origin class]} { set cl [[$origin info class] create $dest -noinit] # class object set obj $cl $cl superclass [$origin info superclass] ::next::core::assertion $cl class-invar [::next::core::assertion $origin class-invar] ::next::core::relation $cl class-filter [::next::core::relation $origin class-filter] ::next::core::relation $cl class-mixin [::next::core::relation $origin class-mixin] :copyNSVarsAndCmds ::next::core::classes$origin ::next::core::classes$dest } else { # create obj set obj [[$origin info class] create $dest -noinit] } # copy object -> may be a class obj ::next::core::assertion $obj check [::next::core::assertion $origin check] ::next::core::assertion $obj object-invar [::next::core::assertion $origin object-invar] ::next::core::relation $obj object-filter [::next::core::relation $origin object-filter] ::next::core::relation $obj object-mixin [::next::core::relation $origin object-mixin] if {[$origin info hasnamespace]} { $obj requireNamespace } } else { namespace eval $dest {} } :copyNSVarsAndCmds $origin $dest foreach i [::next::core::cmd::ObjectInfo::forward $origin] { eval [concat ::next::core::forward $dest -per-object $i [::next::core::cmd::ObjectInfo::forward $origin -definition $i]] } if {[::next::core::objectproperty $origin class]} { foreach i [::next::core::cmd::ClassInfo::forward $origin] { eval [concat ::next::core::forward $dest $i [::next::core::cmd::ClassInfo::forward $origin -definition $i]] } } set traces [list] foreach var [$origin info vars] { set cmds [::next::core::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 {[::next::core::objectproperty $origin class]} { set dest [:getDest $origin] foreach oldslot [$origin info slots] { set newslot [::next::Slot slotName [namespace tail $oldslot] $dest] if {[$oldslot domain] eq $origin} {$newslot domain $cl} if {[$oldslot manager] eq $oldslot} {$newslot manager $newslot} } } } } :public method copy {obj dest} { #puts stderr "[::next::core::current object] copy <$obj> <$dest>" set :objLength [string length $obj] set :dest $dest :makeTargetList $obj :copyTargets } } ::next::Object public method copy newName { if {[string compare [string trimleft $newName :] [string trimleft [::next::core::current object] :]]} { [::next::CopyHandler new -volatile] copy [::next::core::current object] $newName } } ::next::Object public method move newName { if {[string trimleft $newName :] ne [string trimleft [::next::core::current object] :]} { if {$newName ne ""} { :copy $newName } ### let all subclasses get the copied class as superclass if {[::next::core::objectproperty [::next::core::current object] class] && $newName ne ""} { foreach subclass [:info subclass] { set scl [$subclass info superclass] if {[set index [lsearch -exact $scl [::next::core::current object]]] != -1} { set scl [lreplace $scl $index $index $newName] $subclass superclass $scl } } } :destroy } } ####################################################### # some utilities ####################################################### namespace eval ::next::core { # # determine 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 { xotcl - xotcl1 { package require XOTcl #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 "::next"} {uplevel {namespace import -force ::next::*}} } } } namespace export tmpdir use } ####################################################################### # common code for all xotcl versions namespace eval ::next { # export the contents for all xotcl versions namespace export Attribute current # if HOME is not set, and ~ is resolved, Tcl chokes on that if {![info exists ::env(HOME)]} {set ::env(HOME) /root} set ::next::confdir ~/.xotcl set ::next::logdir $::next::confdir/log unset bootstrap } # # The following will go away # #namespace eval ::xotcl { # namespace import ::next::core::use #} #foreach ns {::next ::next::core} { # puts stderr "$ns exports [namespace eval $ns {lsort [namespace export]}]" #}