1# map.tcl --
2# Copyright (c) 2009-2019 Andreas Kupries <andreas_kupries@sourceforge.net>
3#
4# Object wrapper around array/dict. Useful as key/value store in
5# larger systems.
6#
7# Examples:
8# - configuration mgmt in doctools v2 import/export managers
9# - pt import/export managers
10#
11# Each object manages a key/value map.
12
13# ### ### ### ######### ######### #########
14## Requisites
15
16package require Tcl 8.4
17package require snit
18
19# ### ### ### ######### ######### #########
20## API
21
22# ATTENTION:
23##
24# From an API point of view the code below is equivalent to the much
25# shorter `snit::type struct::map { ... }`.
26#
27# Then why the more complex form ?
28#
29# When snit compiles the class to Tcl code, and later on when methods
30# are executed it will happen in the `struct` namespace. The moment
31# this package is used together with `struct::set` all unqualified
32# `set` statements will go bonkers, eiter in snit, or, here, in method
33# `set`, because they get resolved to the `struct::set` dispatcher
34# instead of `::set`. Moving the implementation a level deeper makes
35# the `struct::map` namespace the context, with no conflict.
36
37# Future / TODO: Convert all the OO stuff here over to TclOO, as much
38# as possible (snit configure/cget support is currently still better,
39# ditto hierarchical methods).
40
41namespace eval ::struct {}
42
43proc ::struct::map {args} {
44    uplevel 1 [linsert $args 0 struct::map::I]
45}
46
47snit::type ::struct::map::I {
48
49    # ### ### ### ######### ######### #########
50    ## Options :: None
51
52    # ### ### ### ######### ######### #########
53    ## Creating, destruction
54
55    # Default constructor.
56    # Default destructor.
57
58    # ### ### ### ######### ######### #########
59    ## Public methods. Reading and writing the map.
60
61    method names {} {
62	return [array names mymap]
63    }
64
65    method get {} {
66	return [array get mymap]
67    }
68
69    method set {name {value {}}} {
70	# 7 instead of 3 in the condition below, because of the 4
71	# implicit arguments snit is providing to each method.
72	if {[llength [info level 0]] == 7} {
73	    ::set mymap($name) $value
74	} elseif {![info exists mymap($name)]} {
75	    return -code error "can't read \"$name\": no such variable"
76	}
77	return $mymap($name)
78    }
79
80    method unset {args} {
81	if {![llength $args]} { lappend args * }
82	foreach pattern $args {
83	    array unset mymap $pattern
84	}
85	return
86    }
87
88    # ### ### ### ######### ######### #########
89    ## Internal methods :: None.
90
91    # ### ### ### ######### ######### #########
92    ## State :: Map data, Tcl array
93
94    variable mymap -array {}
95
96    ##
97    # ### ### ### ######### ######### #########
98}
99
100# ### ### ### ######### ######### #########
101## Ready
102
103package provide struct::map 1
104return
105