@@ -45,7 +45,14 @@ type Properties struct {
45
45
Prefix string
46
46
Postfix string
47
47
48
+ // Stores the key/value pairs
48
49
m map [string ]string
50
+
51
+ // Stores the comments per key.
52
+ c map [string ][]string
53
+
54
+ // Stores the keys in order of appearance.
55
+ k []string
49
56
}
50
57
51
58
// NewProperties creates a new Properties struct with the default
@@ -54,7 +61,9 @@ func NewProperties() *Properties {
54
61
return & Properties {
55
62
Prefix : "${" ,
56
63
Postfix : "}" ,
57
- m : make (map [string ]string ),
64
+ m : map [string ]string {},
65
+ c : map [string ][]string {},
66
+ k : []string {},
58
67
}
59
68
}
60
69
@@ -90,6 +99,53 @@ func (p *Properties) MustGet(key string) string {
90
99
91
100
// ----------------------------------------------------------------------------
92
101
102
+ // ClearComments removes the comments for all keys.
103
+ func (p * Properties ) ClearComments () {
104
+ p .c = map [string ][]string {}
105
+ }
106
+
107
+ // ----------------------------------------------------------------------------
108
+
109
+ // GetComment returns the last comment before the given key or an empty string.
110
+ func (p * Properties ) GetComment (key string ) string {
111
+ comments , ok := p .c [key ]
112
+ if ! ok || len (comments ) == 0 {
113
+ return ""
114
+ }
115
+ return comments [len (comments )- 1 ]
116
+ }
117
+
118
+ // ----------------------------------------------------------------------------
119
+
120
+ // GetComments returns all comments that appeared before the given key or nil.
121
+ func (p * Properties ) GetComments (key string ) []string {
122
+ if comments , ok := p .c [key ]; ok {
123
+ return comments
124
+ }
125
+ return nil
126
+ }
127
+
128
+ // ----------------------------------------------------------------------------
129
+
130
+ // SetComment sets the comment for the key.
131
+ func (p * Properties ) SetComment (key , comment string ) {
132
+ p .c [key ] = []string {comment }
133
+ }
134
+
135
+ // ----------------------------------------------------------------------------
136
+
137
+ // SetComments sets the comments for the key. If the comments are nil then
138
+ // all comments for this key are deleted.
139
+ func (p * Properties ) SetComments (key string , comments []string ) {
140
+ if comments == nil {
141
+ delete (p .c , key )
142
+ return
143
+ }
144
+ p .c [key ] = comments
145
+ }
146
+
147
+ // ----------------------------------------------------------------------------
148
+
93
149
// GetBool checks if the expanded value is one of '1', 'yes',
94
150
// 'true' or 'on' if the key exists. The comparison is case-insensitive.
95
151
// If the key does not exist the default value is returned.
@@ -360,11 +416,11 @@ func (p *Properties) Len() int {
360
416
return len (p .m )
361
417
}
362
418
363
- // Keys returns all keys.
419
+ // Keys returns all keys in the same order as in the input .
364
420
func (p * Properties ) Keys () []string {
365
- keys := make ([]string , 0 , len (p .m ))
366
- for k , _ := range p .m {
367
- keys = append ( keys , k )
421
+ keys := make ([]string , len (p .k ))
422
+ for i , k := range p .k {
423
+ keys [ i ] = k
368
424
}
369
425
return keys
370
426
}
@@ -374,39 +430,107 @@ func (p *Properties) Keys() []string {
374
430
// contains the previous value. If the value contains a
375
431
// circular reference or a malformed expression then
376
432
// an error is returned.
433
+ // An empty key is silently ignored.
377
434
func (p * Properties ) Set (key , value string ) (prev string , ok bool , err error ) {
435
+ if key == "" {
436
+ return "" , false , nil
437
+ }
438
+
439
+ // to check for a circular reference we temporarily need
440
+ // to set the new value. If there is an error then revert
441
+ // to the previous state. Only if all tests are successful
442
+ // then we add the key to the p.k list.
443
+ prev , ok = p .Get (key )
444
+ p .m [key ] = value
445
+
446
+ // now check for a circular reference
378
447
_ , err = p .expand (value )
379
448
if err != nil {
449
+
450
+ // revert to the previous state
451
+ if ok {
452
+ p .m [key ] = prev
453
+ } else {
454
+ delete (p .m , key )
455
+ }
456
+
380
457
return "" , false , err
381
458
}
382
459
383
- v , ok := p .Get (key )
384
- p .m [key ] = value
385
- return v , ok , nil
460
+ if ! ok {
461
+ p .k = append (p .k , key )
462
+ }
463
+
464
+ return prev , ok , nil
465
+ }
466
+
467
+ // MustSet sets the property key to the corresponding value.
468
+ // If a value for key existed before then ok is true and prev
469
+ // contains the previous value. An empty key is silently ignored.
470
+ func (p * Properties ) MustSet (key , value string ) (prev string , ok bool ) {
471
+ prev , ok , err := p .Set (key , value )
472
+ if err != nil {
473
+ ErrorHandler (err )
474
+ }
475
+ return prev , ok
386
476
}
387
477
388
478
// String returns a string of all expanded 'key = value' pairs.
389
479
func (p * Properties ) String () string {
390
480
var s string
391
- for key , _ := range p .m {
481
+ for _ , key := range p .k {
392
482
value , _ := p .Get (key )
393
483
s = fmt .Sprintf ("%s%s = %s\n " , s , key , value )
394
484
}
395
485
return s
396
486
}
397
487
398
488
// Write writes all unexpanded 'key = value' pairs to the given writer.
399
- func (p * Properties ) Write (w io.Writer , enc Encoding ) (int , error ) {
400
- total := 0
401
- for key , value := range p .m {
402
- s := fmt .Sprintf ("%s = %s\n " , encode (key , " :" , enc ), encode (value , "" , enc ))
403
- n , err := w .Write ([]byte (s ))
489
+ // Write returns the number of bytes written and any write error encountered.
490
+ func (p * Properties ) Write (w io.Writer , enc Encoding ) (n int , err error ) {
491
+ return p .WriteComment (w , "" , enc )
492
+ }
493
+
494
+ // WriteComment writes all unexpanced 'key = value' pairs to the given writer.
495
+ // If prefix is not empty then comments are written with a blank line and the
496
+ // given prefix. The prefix should be either "# " or "! " to be compatible with
497
+ // the properties file format. Otherwise, the properties parser will not be
498
+ // able to read the file back in. It returns the number of bytes written and
499
+ // any write error encountered.
500
+ func (p * Properties ) WriteComment (w io.Writer , prefix string , enc Encoding ) (n int , err error ) {
501
+ var x int
502
+
503
+ for _ , key := range p .k {
504
+ value := p .m [key ]
505
+
506
+ if prefix != "" {
507
+ if comments , ok := p .c [key ]; ok {
508
+ // add a blank line between entries but not at the top
509
+ if len (comments ) > 0 && n > 0 {
510
+ x , err = fmt .Fprintln (w )
511
+ if err != nil {
512
+ return
513
+ }
514
+ n += x
515
+ }
516
+
517
+ for _ , c := range comments {
518
+ x , err = fmt .Fprintf (w , "%s%s\n " , prefix , encode (c , "" , enc ))
519
+ if err != nil {
520
+ return
521
+ }
522
+ n += x
523
+ }
524
+ }
525
+ }
526
+
527
+ x , err = fmt .Fprintf (w , "%s = %s\n " , encode (key , " :" , enc ), encode (value , "" , enc ))
404
528
if err != nil {
405
- return total , err
529
+ return
406
530
}
407
- total += n
531
+ n += x
408
532
}
409
- return total , nil
533
+ return
410
534
}
411
535
412
536
// ----------------------------------------------------------------------------
0 commit comments