1# 2# tk/namespace.rb : methods to manipulate Tcl/Tk namespace 3# by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp> 4# 5require 'tk' 6 7class TkNamespace < TkObject 8 extend Tk 9 10 TkCommandNames = [ 11 'namespace'.freeze, 12 ].freeze 13 14 Tk_Namespace_ID_TBL = TkCore::INTERP.create_table 15 16 (Tk_Namespace_ID = ["ns".freeze, TkUtil.untrust("00000")]).instance_eval{ 17 @mutex = Mutex.new 18 def mutex; @mutex; end 19 freeze 20 } 21 22 Tk_NsCode_RetObjID_TBL = TkCore::INTERP.create_table 23 24 TkCore::INTERP.init_ip_env{ 25 Tk_Namespace_ID_TBL.mutex.synchronize{ Tk_Namespace_ID_TBL.clear } 26 Tk_NsCode_RetObjID_TBL.mutex.synchronize{ Tk_NsCode_RetObjID_TBL.clear } 27 } 28 29 def TkNamespace.id2obj(id) 30 Tk_Namespace_ID_TBL.mutex.synchronize{ 31 Tk_Namespace_ID_TBL[id]? Tk_Namespace_ID_TBL[id]: id 32 } 33 end 34 35 ##################################### 36 37 class Ensemble < TkObject 38 def __cget_cmd 39 ['namespace', 'ensemble', 'configure', self.path] 40 end 41 private :__cget_cmd 42 43 def __config_cmd 44 ['namespace', 'ensemble', 'configure', self.path] 45 end 46 private :__config_cmd 47 48 def __configinfo_struct 49 {:key=>0, :alias=>nil, :db_name=>nil, :db_class=>nil, 50 :default_value=>nil, :current_value=>2} 51 end 52 private :__configinfo_struct 53 54 def __boolval_optkeys 55 ['prefixes'] 56 end 57 private :__boolval_optkeys 58 59 def __listval_optkeys 60 ['map', 'subcommands', 'unknown'] 61 end 62 private :__listval_optkeys 63 64 def self.exist?(ensemble) 65 bool(tk_call('namespace', 'ensemble', 'exists', ensemble)) 66 end 67 68 def initialize(keys = {}) 69 @ensemble = @path = tk_call('namespace', 'ensemble', 'create', keys) 70 end 71 72 def cget(slot) 73 if slot == :namespace || slot == 'namespace' 74 ns = super(slot) 75 Tk_Namespace_ID_TBL.mutex.synchronize{ 76 if TkNamespace::Tk_Namespace_ID_TBL.key?(ns) 77 TkNamespace::Tk_Namespace_ID_TBL[ns] 78 else 79 ns 80 end 81 } 82 else 83 super(slot) 84 end 85 end 86 def cget_strict(slot) 87 if slot == :namespace || slot == 'namespace' 88 ns = super(slot) 89 Tk_Namespace_ID_TBL.mutex.synchronize{ 90 if TkNamespace::Tk_Namespace_ID_TBL.key?(ns) 91 TkNamespace::Tk_Namespace_ID_TBL[ns] 92 else 93 ns 94 end 95 } 96 else 97 super(slot) 98 end 99 end 100 101 def configinfo(slot = nil) 102 if slot 103 if slot == :namespace || slot == 'namespace' 104 val = super(slot) 105 Tk_Namespace_ID_TBL.mutex.synchronize{ 106 if TkNamespace::Tk_Namespace_ID_TBL.key?(val) 107 val = TkNamespace::Tk_Namespace_ID_TBL[val] 108 end 109 } 110 else 111 val = super(slot) 112 end 113 114 if TkComm::GET_CONFIGINFO_AS_ARRAY 115 [slot.to_s, val] 116 else # ! TkComm::GET_CONFIGINFO_AS_ARRAY 117 {slot.to_s => val} 118 end 119 120 else 121 info = super() 122 123 if TkComm::GET_CONFIGINFO_AS_ARRAY 124 Tk_Namespace_ID_TBL.mutex.synchronize{ 125 info.map!{|inf| 126 if inf[0] == 'namespace' && 127 TkNamespace::Tk_Namespace_ID_TBL.key?(inf[-1]) 128 [inf[0], TkNamespace::Tk_Namespace_ID_TBL[inf[-1]]] 129 else 130 inf 131 end 132 } 133 } 134 else # ! TkComm::GET_CONFIGINFO_AS_ARRAY 135 val = info['namespace'] 136 Tk_Namespace_ID_TBL.mutex.synchronize{ 137 if TkNamespace::Tk_Namespace_ID_TBL.key?(val) 138 info['namespace'] = TkNamespace::Tk_Namespace_ID_TBL[val] 139 end 140 } 141 end 142 143 info 144 end 145 end 146 147 def exists? 148 bool(tk_call('namespace', 'ensemble', 'exists', @path)) 149 end 150 end 151 152 ##################################### 153 154 class ScopeArgs < Array 155 include Tk 156 157 # alias __tk_call tk_call 158 # alias __tk_call_without_enc tk_call_without_enc 159 # alias __tk_call_with_enc tk_call_with_enc 160 def tk_call(*args) 161 #super('namespace', 'eval', @namespace, *args) 162 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 163 super('namespace', 'eval', @namespace, 164 TkCore::INTERP._merge_tklist(*args)) 165 end 166 def tk_call_without_enc(*args) 167 #super('namespace', 'eval', @namespace, *args) 168 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 169 super('namespace', 'eval', @namespace, 170 TkCore::INTERP._merge_tklist(*args)) 171 end 172 def tk_call_with_enc(*args) 173 #super('namespace', 'eval', @namespace, *args) 174 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 175 super('namespace', 'eval', @namespace, 176 TkCore::INTERP._merge_tklist(*args)) 177 end 178 179 def initialize(namespace, *args) 180 @namespace = namespace 181 super(args.size) 182 self.replace(args) 183 end 184 end 185 186 ##################################### 187 188 class NsCode < TkObject 189 def initialize(scope, use_obj_id = false) 190 @scope = scope + ' ' 191 @use_obj_id = use_obj_id 192 end 193 def path 194 @scope 195 end 196 def to_eval 197 @scope 198 end 199 def call(*args) 200 ret = TkCore::INTERP._eval_without_enc(@scope + array2tk_list(args)) 201 if @use_obj_id 202 ret = TkNamespace::Tk_NsCode_RetObjID_TBL.delete(ret.to_i) 203 end 204 ret 205 end 206 end 207 208 ##################################### 209 210 def install_cmd(cmd) 211 lst = tk_split_simplelist(super(cmd), false, false) 212 if lst[1] =~ /^::/ 213 lst[1] = @fullname 214 else 215 lst.insert(1, @fullname) 216 end 217 TkCore::INTERP._merge_tklist(*lst) 218 end 219 220 alias __tk_call tk_call 221 alias __tk_call_without_enc tk_call_without_enc 222 alias __tk_call_with_enc tk_call_with_enc 223 def tk_call(*args) 224 #super('namespace', 'eval', @fullname, *args) 225 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 226 super('namespace', 'eval', @fullname, 227 TkCore::INTERP._merge_tklist(*args)) 228 end 229 def tk_call_without_enc(*args) 230 #super('namespace', 'eval', @fullname, *args) 231 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 232 super('namespace', 'eval', @fullname, 233 TkCore::INTERP._merge_tklist(*args)) 234 end 235 def tk_call_with_enc(*args) 236 #super('namespace', 'eval', @fullname, *args) 237 args = args.collect{|arg| (s = _get_eval_string(arg, true))? s: ''} 238 super('namespace', 'eval', @fullname, 239 TkCore::INTERP._merge_tklist(*args)) 240 end 241 alias ns_tk_call tk_call 242 alias ns_tk_call_without_enc tk_call_without_enc 243 alias ns_tk_call_with_enc tk_call_with_enc 244 245 def initialize(name = nil, parent = nil) 246 unless name 247 Tk_Namespace_ID.mutex.synchronize{ 248 # name = Tk_Namespace_ID.join('') 249 name = Tk_Namespace_ID.join(TkCore::INTERP._ip_id_) 250 Tk_Namespace_ID[1].succ! 251 } 252 end 253 name = __tk_call('namespace', 'current') if name == '' 254 if parent 255 if parent =~ /^::/ 256 if name =~ /^::/ 257 @fullname = parent + name 258 else 259 @fullname = parent +'::'+ name 260 end 261 else 262 ancestor = __tk_call('namespace', 'current') 263 ancestor = '' if ancestor == '::' 264 if name =~ /^::/ 265 @fullname = ancestor + '::' + parent + name 266 else 267 @fullname = ancestor + '::'+ parent +'::'+ name 268 end 269 end 270 else # parent == nil 271 ancestor = __tk_call('namespace', 'current') 272 ancestor = '' if ancestor == '::' 273 if name =~ /^::/ 274 @fullname = name 275 else 276 @fullname = ancestor + '::' + name 277 end 278 end 279 @path = @fullname 280 @parent = __tk_call('namespace', 'qualifiers', @fullname) 281 @name = __tk_call('namespace', 'tail', @fullname) 282 283 # create namespace 284 __tk_call('namespace', 'eval', @fullname, '') 285 286 Tk_Namespace_ID_TBL.mutex.synchronize{ 287 Tk_Namespace_ID_TBL[@fullname] = self 288 } 289 end 290 291 def self.children(*args) 292 # args ::= [<namespace>] [<pattern>] 293 # <pattern> must be glob-style pattern 294 tk_split_simplelist(tk_call('namespace', 'children', *args)).collect{|ns| 295 # ns is fullname 296 Tk_Namespace_ID_TBL.mutex.synchronize{ 297 if Tk_Namespace_ID_TBL.key?(ns) 298 Tk_Namespace_ID_TBL[ns] 299 else 300 ns 301 end 302 } 303 } 304 end 305 def children(pattern=None) 306 TkNamespace.children(@fullname, pattern) 307 end 308 309 def self.code(script = Proc.new) 310 TkNamespace.new('').code(script) 311 end 312=begin 313 def code(script = Proc.new) 314 if script.kind_of?(String) 315 cmd = proc{|*args| ScopeArgs.new(@fullname,*args).instance_eval(script)} 316 elsif script.kind_of?(Proc) 317 cmd = proc{|*args| ScopeArgs.new(@fullname,*args).instance_eval(&script)} 318 else 319 fail ArgumentError, "String or Proc is expected" 320 end 321 TkNamespace::NsCode.new(tk_call_without_enc('namespace', 'code', 322 _get_eval_string(cmd, false))) 323 end 324=end 325 def code(script = Proc.new) 326 if script.kind_of?(String) 327 cmd = proc{|*args| 328 ret = ScopeArgs.new(@fullname,*args).instance_eval(script) 329 id = ret.object_id 330 TkNamespace::Tk_NsCode_RetObjID_TBL[id] = ret 331 id 332 } 333 elsif script.kind_of?(Proc) 334 cmd = proc{|*args| 335 if TkCore::WITH_RUBY_VM ### Ruby 1.9 !!!! 336 obj = ScopeArgs.new(@fullname,*args) 337 ret = obj.instance_exec(obj, &script) 338 else 339 ret = ScopeArgs.new(@fullname,*args).instance_eval(&script) 340 end 341 id = ret.object_id 342 TkNamespace::Tk_NsCode_RetObjID_TBL[id] = ret 343 id 344 } 345 else 346 fail ArgumentError, "String or Proc is expected" 347 end 348 TkNamespace::NsCode.new(tk_call_without_enc('namespace', 'code', 349 _get_eval_string(cmd, false)), 350 true) 351 end 352 353 def self.current_path 354 tk_call('namespace', 'current') 355 end 356 def current_path 357 @fullname 358 end 359 360 def self.current 361 ns = self.current_path 362 Tk_Namespace_ID_TBL.mutex.synchronize{ 363 if Tk_Namespace_ID_TBL.key?(ns) 364 Tk_Namespace_ID_TBL[ns] 365 else 366 ns 367 end 368 } 369 end 370 def current_namespace 371 # ns_tk_call('namespace', 'current') 372 # @fullname 373 self 374 end 375 alias current current_namespace 376 377 def self.delete(*ns_list) 378 tk_call('namespace', 'delete', *ns_list) 379 ns_list.each{|ns| 380 Tk_Namespace_ID_TBL.mutex.synchronize{ 381 if ns.kind_of?(TkNamespace) 382 Tk_Namespace_ID_TBL.delete(ns.path) 383 else 384 Tk_Namespace_ID_TBL.delete(ns.to_s) 385 end 386 } 387 } 388 end 389 def delete 390 TkNamespece.delete(@fullname) 391 end 392 393 def self.ensemble_create(*keys) 394 tk_call('namespace', 'ensemble', 'create', *hash_kv(keys)) 395 end 396 def self.ensemble_configure(cmd, slot, value=None) 397 if slot.kind_of?(Hash) 398 tk_call('namespace', 'ensemble', 'configure', cmd, *hash_kv(slot)) 399 else 400 tk_call('namespace', 'ensemble', 'configure', cmd, '-'+slot.to_s, value) 401 end 402 end 403 def self.ensemble_configinfo(cmd, slot = nil) 404 if slot 405 tk_call('namespace', 'ensemble', 'configure', cmd, '-' + slot.to_s) 406 else 407 inf = {} 408 Hash(*tk_split_simplelist(tk_call('namespace', 'ensemble', 'configure', cmd))).each{|k, v| inf[k[1..-1]] = v} 409 inf 410 end 411 end 412 def self.ensemble_exist?(cmd) 413 bool(tk_call('namespace', 'ensemble', 'exists', cmd)) 414 end 415 416 def self.eval(namespace, cmd = Proc.new, *args) 417 #tk_call('namespace', 'eval', namespace, cmd, *args) 418 TkNamespace.new(namespace).eval(cmd, *args) 419 end 420=begin 421 def eval(cmd = Proc.new, *args) 422 #TkNamespace.eval(@fullname, cmd, *args) 423 #ns_tk_call(cmd, *args) 424 code_obj = code(cmd) 425 ret = code_obj.call(*args) 426 # uninstall_cmd(TkCore::INTERP._split_tklist(code_obj.path)[-1]) 427 uninstall_cmd(_fromUTF8(TkCore::INTERP._split_tklist(_toUTF8(code_obj.path))[-1])) 428 tk_tcl2ruby(ret) 429 end 430=end 431 def eval(cmd = Proc.new, *args) 432 code_obj = code(cmd) 433 ret = code_obj.call(*args) 434 uninstall_cmd(_fromUTF8(TkCore::INTERP._split_tklist(_toUTF8(code_obj.path))[-1])) 435 ret 436 end 437 438 def self.exist?(ns) 439 bool(tk_call('namespace', 'exists', ns)) 440 end 441 def exist? 442 TkNamespece.exist?(@fullname) 443 end 444 445 def self.export(*patterns) 446 tk_call('namespace', 'export', *patterns) 447 end 448 def self.export_with_clear(*patterns) 449 tk_call('namespace', 'export', '-clear', *patterns) 450 end 451 def export 452 TkNamespace.export(@fullname) 453 end 454 def export_with_clear 455 TkNamespace.export_with_clear(@fullname) 456 end 457 458 def self.forget(*patterns) 459 tk_call('namespace', 'forget', *patterns) 460 end 461 def forget 462 TkNamespace.forget(@fullname) 463 end 464 465 def self.import(*patterns) 466 tk_call('namespace', 'import', *patterns) 467 end 468 def self.force_import(*patterns) 469 tk_call('namespace', 'import', '-force', *patterns) 470 end 471 def import 472 TkNamespace.import(@fullname) 473 end 474 def force_import 475 TkNamespace.force_import(@fullname) 476 end 477 478 def self.inscope(namespace, script, *args) 479 tk_call('namespace', 'inscope', namespace, script, *args) 480 end 481 def inscope(script, *args) 482 TkNamespace.inscope(@fullname, script, *args) 483 end 484 485 def self.origin(cmd) 486 tk_call('namespace', 'origin', cmd) 487 end 488 489 def self.parent(namespace=None) 490 ns = tk_call('namespace', 'parent', namespace) 491 Tk_Namespace_ID_TBL.mutex.synchronize{ 492 if Tk_Namespace_ID_TBL.key?(ns) 493 Tk_Namespace_ID_TBL[ns] 494 else 495 ns 496 end 497 } 498 end 499 def parent 500 tk_call('namespace', 'parent', @fullname) 501 end 502 503 def self.get_path 504 tk_call('namespace', 'path') 505 end 506 def self.set_path(*namespace_list) 507 tk_call('namespace', 'path', array2tk_list(namespace_list)) 508 end 509 def set_path 510 tk_call('namespace', 'path', @fullname) 511 end 512 513 def self.qualifiers(str) 514 tk_call('namespace', 'qualifiers', str) 515 end 516 517 def self.tail(str) 518 tk_call('namespace', 'tail', str) 519 end 520 521 def self.upvar(namespace, *var_pairs) 522 tk_call('namespace', 'upvar', namespace, *(var_pairs.flatten)) 523 end 524 def upvar(*var_pairs) 525 TkNamespace.inscope(@fullname, *(var_pairs.flatten)) 526 end 527 528 def self.get_unknown_handler 529 tk_tcl2ruby(tk_call('namespace', 'unknown')) 530 end 531 def self.set_unknown_handler(cmd = Proc.new) 532 tk_call('namespace', 'unknown', cmd) 533 end 534 535 def self.which(name) 536 tk_call('namespace', 'which', name) 537 end 538 def self.which_command(name) 539 tk_call('namespace', 'which', '-command', name) 540 end 541 def self.which_variable(name) 542 tk_call('namespace', 'which', '-variable', name) 543 end 544end 545 546TkNamespace::Global = TkNamespace.new('::') 547