27
27
* the GNU General Public License.
28
28
*/
29
29
30
+ /*
31
+ Package properties is a library for handling maps of hierarchical properties.
32
+ This library is mainly used in the Arduino platform software to handle
33
+ configurations made of key/value pairs stored in files with an INI like
34
+ syntax, for example:
35
+
36
+ ...
37
+ uno.name=Arduino/Genuino Uno
38
+ uno.upload.tool=avrdude
39
+ uno.upload.protocol=arduino
40
+ uno.upload.maximum_size=32256
41
+ uno.upload.maximum_data_size=2048
42
+ uno.upload.speed=115200
43
+ uno.build.mcu=atmega328p
44
+ uno.build.f_cpu=16000000L
45
+ uno.build.board=AVR_UNO
46
+ uno.build.core=arduino
47
+ uno.build.variant=standard
48
+ diecimila.name=Arduino Duemilanove or Diecimila
49
+ diecimila.upload.tool=avrdude
50
+ diecimila.upload.protocol=arduino
51
+ diecimila.build.f_cpu=16000000L
52
+ diecimila.build.board=AVR_DUEMILANOVE
53
+ diecimila.build.core=arduino
54
+ diecimila.build.variant=standard
55
+ ...
56
+
57
+ This library has methods to parse this kind of files into a Map object.
58
+
59
+ The Map object has many helper methods to accomplish some common operation
60
+ on this kind of data like cloning, merging, comparing and also extracting
61
+ a submap or generating a map-of-submaps from the first "level" of the hierarchy.
62
+ */
30
63
package properties
31
64
32
65
import (
@@ -55,6 +88,7 @@ func init() {
55
88
}
56
89
}
57
90
91
+ // Load reads a properties file and makes a Map out of it.
58
92
func Load (filepath string ) (Map , error ) {
59
93
bytes , err := ioutil .ReadFile (filepath )
60
94
if err != nil {
@@ -76,6 +110,8 @@ func Load(filepath string) (Map, error) {
76
110
return properties , nil
77
111
}
78
112
113
+ // LoadFromSlice reads a properties file from an array of string
114
+ // and makes a Map out of it
79
115
func LoadFromSlice (lines []string ) (Map , error ) {
80
116
properties := make (Map )
81
117
@@ -109,6 +145,8 @@ func (m Map) parseLine(line string) error {
109
145
return nil
110
146
}
111
147
148
+ // SafeLoad is like Load, except that it returns an empty Map if the specified
149
+ // file doesn't exists
112
150
func SafeLoad (filepath string ) (Map , error ) {
113
151
_ , err := os .Stat (filepath )
114
152
if os .IsNotExist (err ) {
@@ -122,6 +160,38 @@ func SafeLoad(filepath string) (Map, error) {
122
160
return properties , nil
123
161
}
124
162
163
+ // FirstLevelOf generates a map-of-Maps using the first level of the hierarchy
164
+ // of the current Map. For example the following Map:
165
+ //
166
+ // properties.Map{
167
+ // "uno.name": "Arduino/Genuino Uno",
168
+ // "uno.upload.tool": "avrdude",
169
+ // "uno.upload.protocol": "arduino",
170
+ // "uno.upload.maximum_size": "32256",
171
+ // "diecimila.name": "Arduino Duemilanove or Diecimila",
172
+ // "diecimila.upload.tool": "avrdude",
173
+ // "diecimila.upload.protocol": "arduino",
174
+ // "diecimila.bootloader.tool": "avrdude",
175
+ // "diecimila.bootloader.low_fuses": "0xFF",
176
+ // }
177
+ //
178
+ // is transformed into the following map-of-Maps:
179
+ //
180
+ // map[string]Map{
181
+ // "uno" : properties.Map{
182
+ // "name": "Arduino/Genuino Uno",
183
+ // "upload.tool": "avrdude",
184
+ // "upload.protocol": "arduino",
185
+ // "upload.maximum_size": "32256",
186
+ // },
187
+ // "diecimila" : properties.Map{
188
+ // "name=Arduino Duemilanove or Diecimila
189
+ // "upload.tool": "avrdude",
190
+ // "upload.protocol": "arduino",
191
+ // "bootloader.tool": "avrdude",
192
+ // "bootloader.low_fuses": "0xFF",
193
+ // }
194
+ // }
125
195
func (m Map ) FirstLevelOf () map [string ]Map {
126
196
newMap := make (map [string ]Map )
127
197
for key , value := range m {
@@ -137,10 +207,42 @@ func (m Map) FirstLevelOf() map[string]Map {
137
207
return newMap
138
208
}
139
209
210
+ // SubTree extracts a sub Map from an existing map using the first level
211
+ // of the keys hierarchy as selector.
212
+ // For example the following Map:
213
+ //
214
+ // properties.Map{
215
+ // "uno.name": "Arduino/Genuino Uno",
216
+ // "uno.upload.tool": "avrdude",
217
+ // "uno.upload.protocol": "arduino",
218
+ // "uno.upload.maximum_size": "32256",
219
+ // "diecimila.name": "Arduino Duemilanove or Diecimila",
220
+ // "diecimila.upload.tool": "avrdude",
221
+ // "diecimila.upload.protocol": "arduino",
222
+ // "diecimila.bootloader.tool": "avrdude",
223
+ // "diecimila.bootloader.low_fuses": "0xFF",
224
+ // }
225
+ //
226
+ // after calling SubTree("uno") will be transformed in:
227
+ //
228
+ // properties.Map{
229
+ // "name": "Arduino/Genuino Uno",
230
+ // "upload.tool": "avrdude",
231
+ // "upload.protocol": "arduino",
232
+ // "upload.maximum_size": "32256",
233
+ // },
140
234
func (m Map ) SubTree (key string ) Map {
141
235
return m .FirstLevelOf ()[key ]
142
236
}
143
237
238
+ // ExpandPropsInString use the Map to replace values into a format string.
239
+ // The format string should contains markers between braces, for example:
240
+ //
241
+ // "The selected upload protocol is {upload.protocol}."
242
+ //
243
+ // Each marker is replaced by the corresponding value of the Map.
244
+ // The values in the Map may contains other markers, they are evaluated
245
+ // recursively.
144
246
func (m Map ) ExpandPropsInString (str string ) string {
145
247
replaced := true
146
248
for i := 0 ; i < 10 && replaced ; i ++ {
@@ -154,6 +256,8 @@ func (m Map) ExpandPropsInString(str string) string {
154
256
return str
155
257
}
156
258
259
+ // Merge merges a Map into this one. Each key/value of the merged Maps replaces
260
+ // the key/value present in the original Map.
157
261
func (m Map ) Merge (sources ... Map ) Map {
158
262
for _ , source := range sources {
159
263
for key , value := range source {
@@ -163,16 +267,21 @@ func (m Map) Merge(sources ...Map) Map {
163
267
return m
164
268
}
165
269
270
+ // Clone makes a copy of the Map
166
271
func (m Map ) Clone () Map {
167
272
clone := make (Map )
168
273
clone .Merge (m )
169
274
return clone
170
275
}
171
276
277
+ // Equals returns true if the current Map contains the same key/value pairs of
278
+ // the Map passed as argument.
172
279
func (m Map ) Equals (other Map ) bool {
173
280
return reflect .DeepEqual (m , other )
174
281
}
175
282
283
+ // MergeMapsOfProperties merge the map-of-Maps (obtained from the method FirstLevelOf()) into the
284
+ // target map-of-Maps.
176
285
func MergeMapsOfProperties (target map [string ]Map , sources ... map [string ]Map ) map [string ]Map {
177
286
for _ , source := range sources {
178
287
for key , value := range source {
@@ -182,6 +291,8 @@ func MergeMapsOfProperties(target map[string]Map, sources ...map[string]Map) map
182
291
return target
183
292
}
184
293
294
+ // DeleteUnexpandedPropsFromString removes all the brace markers "{xxx}" that are not expanded
295
+ // into a value using the Map.ExpandPropsInString() method.
185
296
func DeleteUnexpandedPropsFromString (str string ) string {
186
297
rxp := regexp .MustCompile ("\\ {.+?\\ }" )
187
298
return rxp .ReplaceAllString (str , "" )
0 commit comments