OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / magiconair / properties / properties.go
1 // Copyright 2017 Frank Schroeder. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package properties
6
7 // BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.
8 // BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.
9
10 import (
11         "fmt"
12         "io"
13         "log"
14         "os"
15         "regexp"
16         "strconv"
17         "strings"
18         "time"
19         "unicode/utf8"
20 )
21
22 // ErrorHandlerFunc defines the type of function which handles failures
23 // of the MustXXX() functions. An error handler function must exit
24 // the application after handling the error.
25 type ErrorHandlerFunc func(error)
26
27 // ErrorHandler is the function which handles failures of the MustXXX()
28 // functions. The default is LogFatalHandler.
29 var ErrorHandler ErrorHandlerFunc = LogFatalHandler
30
31 // LogHandlerFunc defines the function prototype for logging errors.
32 type LogHandlerFunc func(fmt string, args ...interface{})
33
34 // LogPrintf defines a log handler which uses log.Printf.
35 var LogPrintf LogHandlerFunc = log.Printf
36
37 // LogFatalHandler handles the error by logging a fatal error and exiting.
38 func LogFatalHandler(err error) {
39         log.Fatal(err)
40 }
41
42 // PanicHandler handles the error by panicking.
43 func PanicHandler(err error) {
44         panic(err)
45 }
46
47 // -----------------------------------------------------------------------------
48
49 // A Properties contains the key/value pairs from the properties input.
50 // All values are stored in unexpanded form and are expanded at runtime
51 type Properties struct {
52         // Pre-/Postfix for property expansion.
53         Prefix  string
54         Postfix string
55
56         // DisableExpansion controls the expansion of properties on Get()
57         // and the check for circular references on Set(). When set to
58         // true Properties behaves like a simple key/value store and does
59         // not check for circular references on Get() or on Set().
60         DisableExpansion bool
61
62         // Stores the key/value pairs
63         m map[string]string
64
65         // Stores the comments per key.
66         c map[string][]string
67
68         // Stores the keys in order of appearance.
69         k []string
70 }
71
72 // NewProperties creates a new Properties struct with the default
73 // configuration for "${key}" expressions.
74 func NewProperties() *Properties {
75         return &Properties{
76                 Prefix:  "${",
77                 Postfix: "}",
78                 m:       map[string]string{},
79                 c:       map[string][]string{},
80                 k:       []string{},
81         }
82 }
83
84 // Get returns the expanded value for the given key if exists.
85 // Otherwise, ok is false.
86 func (p *Properties) Get(key string) (value string, ok bool) {
87         v, ok := p.m[key]
88         if p.DisableExpansion {
89                 return v, ok
90         }
91         if !ok {
92                 return "", false
93         }
94
95         expanded, err := p.expand(v)
96
97         // we guarantee that the expanded value is free of
98         // circular references and malformed expressions
99         // so we panic if we still get an error here.
100         if err != nil {
101                 ErrorHandler(fmt.Errorf("%s in %q", err, key+" = "+v))
102         }
103
104         return expanded, true
105 }
106
107 // MustGet returns the expanded value for the given key if exists.
108 // Otherwise, it panics.
109 func (p *Properties) MustGet(key string) string {
110         if v, ok := p.Get(key); ok {
111                 return v
112         }
113         ErrorHandler(invalidKeyError(key))
114         panic("ErrorHandler should exit")
115 }
116
117 // ----------------------------------------------------------------------------
118
119 // ClearComments removes the comments for all keys.
120 func (p *Properties) ClearComments() {
121         p.c = map[string][]string{}
122 }
123
124 // ----------------------------------------------------------------------------
125
126 // GetComment returns the last comment before the given key or an empty string.
127 func (p *Properties) GetComment(key string) string {
128         comments, ok := p.c[key]
129         if !ok || len(comments) == 0 {
130                 return ""
131         }
132         return comments[len(comments)-1]
133 }
134
135 // ----------------------------------------------------------------------------
136
137 // GetComments returns all comments that appeared before the given key or nil.
138 func (p *Properties) GetComments(key string) []string {
139         if comments, ok := p.c[key]; ok {
140                 return comments
141         }
142         return nil
143 }
144
145 // ----------------------------------------------------------------------------
146
147 // SetComment sets the comment for the key.
148 func (p *Properties) SetComment(key, comment string) {
149         p.c[key] = []string{comment}
150 }
151
152 // ----------------------------------------------------------------------------
153
154 // SetComments sets the comments for the key. If the comments are nil then
155 // all comments for this key are deleted.
156 func (p *Properties) SetComments(key string, comments []string) {
157         if comments == nil {
158                 delete(p.c, key)
159                 return
160         }
161         p.c[key] = comments
162 }
163
164 // ----------------------------------------------------------------------------
165
166 // GetBool checks if the expanded value is one of '1', 'yes',
167 // 'true' or 'on' if the key exists. The comparison is case-insensitive.
168 // If the key does not exist the default value is returned.
169 func (p *Properties) GetBool(key string, def bool) bool {
170         v, err := p.getBool(key)
171         if err != nil {
172                 return def
173         }
174         return v
175 }
176
177 // MustGetBool checks if the expanded value is one of '1', 'yes',
178 // 'true' or 'on' if the key exists. The comparison is case-insensitive.
179 // If the key does not exist the function panics.
180 func (p *Properties) MustGetBool(key string) bool {
181         v, err := p.getBool(key)
182         if err != nil {
183                 ErrorHandler(err)
184         }
185         return v
186 }
187
188 func (p *Properties) getBool(key string) (value bool, err error) {
189         if v, ok := p.Get(key); ok {
190                 return boolVal(v), nil
191         }
192         return false, invalidKeyError(key)
193 }
194
195 func boolVal(v string) bool {
196         v = strings.ToLower(v)
197         return v == "1" || v == "true" || v == "yes" || v == "on"
198 }
199
200 // ----------------------------------------------------------------------------
201
202 // GetDuration parses the expanded value as an time.Duration (in ns) if the
203 // key exists. If key does not exist or the value cannot be parsed the default
204 // value is returned. In almost all cases you want to use GetParsedDuration().
205 func (p *Properties) GetDuration(key string, def time.Duration) time.Duration {
206         v, err := p.getInt64(key)
207         if err != nil {
208                 return def
209         }
210         return time.Duration(v)
211 }
212
213 // MustGetDuration parses the expanded value as an time.Duration (in ns) if
214 // the key exists. If key does not exist or the value cannot be parsed the
215 // function panics. In almost all cases you want to use MustGetParsedDuration().
216 func (p *Properties) MustGetDuration(key string) time.Duration {
217         v, err := p.getInt64(key)
218         if err != nil {
219                 ErrorHandler(err)
220         }
221         return time.Duration(v)
222 }
223
224 // ----------------------------------------------------------------------------
225
226 // GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
227 // If key does not exist or the value cannot be parsed the default
228 // value is returned.
229 func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration {
230         s, ok := p.Get(key)
231         if !ok {
232                 return def
233         }
234         v, err := time.ParseDuration(s)
235         if err != nil {
236                 return def
237         }
238         return v
239 }
240
241 // MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
242 // If key does not exist or the value cannot be parsed the function panics.
243 func (p *Properties) MustGetParsedDuration(key string) time.Duration {
244         s, ok := p.Get(key)
245         if !ok {
246                 ErrorHandler(invalidKeyError(key))
247         }
248         v, err := time.ParseDuration(s)
249         if err != nil {
250                 ErrorHandler(err)
251         }
252         return v
253 }
254
255 // ----------------------------------------------------------------------------
256
257 // GetFloat64 parses the expanded value as a float64 if the key exists.
258 // If key does not exist or the value cannot be parsed the default
259 // value is returned.
260 func (p *Properties) GetFloat64(key string, def float64) float64 {
261         v, err := p.getFloat64(key)
262         if err != nil {
263                 return def
264         }
265         return v
266 }
267
268 // MustGetFloat64 parses the expanded value as a float64 if the key exists.
269 // If key does not exist or the value cannot be parsed the function panics.
270 func (p *Properties) MustGetFloat64(key string) float64 {
271         v, err := p.getFloat64(key)
272         if err != nil {
273                 ErrorHandler(err)
274         }
275         return v
276 }
277
278 func (p *Properties) getFloat64(key string) (value float64, err error) {
279         if v, ok := p.Get(key); ok {
280                 value, err = strconv.ParseFloat(v, 64)
281                 if err != nil {
282                         return 0, err
283                 }
284                 return value, nil
285         }
286         return 0, invalidKeyError(key)
287 }
288
289 // ----------------------------------------------------------------------------
290
291 // GetInt parses the expanded value as an int if the key exists.
292 // If key does not exist or the value cannot be parsed the default
293 // value is returned. If the value does not fit into an int the
294 // function panics with an out of range error.
295 func (p *Properties) GetInt(key string, def int) int {
296         v, err := p.getInt64(key)
297         if err != nil {
298                 return def
299         }
300         return intRangeCheck(key, v)
301 }
302
303 // MustGetInt parses the expanded value as an int if the key exists.
304 // If key does not exist or the value cannot be parsed the function panics.
305 // If the value does not fit into an int the function panics with
306 // an out of range error.
307 func (p *Properties) MustGetInt(key string) int {
308         v, err := p.getInt64(key)
309         if err != nil {
310                 ErrorHandler(err)
311         }
312         return intRangeCheck(key, v)
313 }
314
315 // ----------------------------------------------------------------------------
316
317 // GetInt64 parses the expanded value as an int64 if the key exists.
318 // If key does not exist or the value cannot be parsed the default
319 // value is returned.
320 func (p *Properties) GetInt64(key string, def int64) int64 {
321         v, err := p.getInt64(key)
322         if err != nil {
323                 return def
324         }
325         return v
326 }
327
328 // MustGetInt64 parses the expanded value as an int if the key exists.
329 // If key does not exist or the value cannot be parsed the function panics.
330 func (p *Properties) MustGetInt64(key string) int64 {
331         v, err := p.getInt64(key)
332         if err != nil {
333                 ErrorHandler(err)
334         }
335         return v
336 }
337
338 func (p *Properties) getInt64(key string) (value int64, err error) {
339         if v, ok := p.Get(key); ok {
340                 value, err = strconv.ParseInt(v, 10, 64)
341                 if err != nil {
342                         return 0, err
343                 }
344                 return value, nil
345         }
346         return 0, invalidKeyError(key)
347 }
348
349 // ----------------------------------------------------------------------------
350
351 // GetUint parses the expanded value as an uint if the key exists.
352 // If key does not exist or the value cannot be parsed the default
353 // value is returned. If the value does not fit into an int the
354 // function panics with an out of range error.
355 func (p *Properties) GetUint(key string, def uint) uint {
356         v, err := p.getUint64(key)
357         if err != nil {
358                 return def
359         }
360         return uintRangeCheck(key, v)
361 }
362
363 // MustGetUint parses the expanded value as an int if the key exists.
364 // If key does not exist or the value cannot be parsed the function panics.
365 // If the value does not fit into an int the function panics with
366 // an out of range error.
367 func (p *Properties) MustGetUint(key string) uint {
368         v, err := p.getUint64(key)
369         if err != nil {
370                 ErrorHandler(err)
371         }
372         return uintRangeCheck(key, v)
373 }
374
375 // ----------------------------------------------------------------------------
376
377 // GetUint64 parses the expanded value as an uint64 if the key exists.
378 // If key does not exist or the value cannot be parsed the default
379 // value is returned.
380 func (p *Properties) GetUint64(key string, def uint64) uint64 {
381         v, err := p.getUint64(key)
382         if err != nil {
383                 return def
384         }
385         return v
386 }
387
388 // MustGetUint64 parses the expanded value as an int if the key exists.
389 // If key does not exist or the value cannot be parsed the function panics.
390 func (p *Properties) MustGetUint64(key string) uint64 {
391         v, err := p.getUint64(key)
392         if err != nil {
393                 ErrorHandler(err)
394         }
395         return v
396 }
397
398 func (p *Properties) getUint64(key string) (value uint64, err error) {
399         if v, ok := p.Get(key); ok {
400                 value, err = strconv.ParseUint(v, 10, 64)
401                 if err != nil {
402                         return 0, err
403                 }
404                 return value, nil
405         }
406         return 0, invalidKeyError(key)
407 }
408
409 // ----------------------------------------------------------------------------
410
411 // GetString returns the expanded value for the given key if exists or
412 // the default value otherwise.
413 func (p *Properties) GetString(key, def string) string {
414         if v, ok := p.Get(key); ok {
415                 return v
416         }
417         return def
418 }
419
420 // MustGetString returns the expanded value for the given key if exists or
421 // panics otherwise.
422 func (p *Properties) MustGetString(key string) string {
423         if v, ok := p.Get(key); ok {
424                 return v
425         }
426         ErrorHandler(invalidKeyError(key))
427         panic("ErrorHandler should exit")
428 }
429
430 // ----------------------------------------------------------------------------
431
432 // Filter returns a new properties object which contains all properties
433 // for which the key matches the pattern.
434 func (p *Properties) Filter(pattern string) (*Properties, error) {
435         re, err := regexp.Compile(pattern)
436         if err != nil {
437                 return nil, err
438         }
439
440         return p.FilterRegexp(re), nil
441 }
442
443 // FilterRegexp returns a new properties object which contains all properties
444 // for which the key matches the regular expression.
445 func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
446         pp := NewProperties()
447         for _, k := range p.k {
448                 if re.MatchString(k) {
449                         // TODO(fs): we are ignoring the error which flags a circular reference.
450                         // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
451                         pp.Set(k, p.m[k])
452                 }
453         }
454         return pp
455 }
456
457 // FilterPrefix returns a new properties object with a subset of all keys
458 // with the given prefix.
459 func (p *Properties) FilterPrefix(prefix string) *Properties {
460         pp := NewProperties()
461         for _, k := range p.k {
462                 if strings.HasPrefix(k, prefix) {
463                         // TODO(fs): we are ignoring the error which flags a circular reference.
464                         // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
465                         pp.Set(k, p.m[k])
466                 }
467         }
468         return pp
469 }
470
471 // FilterStripPrefix returns a new properties object with a subset of all keys
472 // with the given prefix and the prefix removed from the keys.
473 func (p *Properties) FilterStripPrefix(prefix string) *Properties {
474         pp := NewProperties()
475         n := len(prefix)
476         for _, k := range p.k {
477                 if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
478                         // TODO(fs): we are ignoring the error which flags a circular reference.
479                         // TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
480                         // TODO(fs): this function should probably return an error but the signature is fixed
481                         pp.Set(k[n:], p.m[k])
482                 }
483         }
484         return pp
485 }
486
487 // Len returns the number of keys.
488 func (p *Properties) Len() int {
489         return len(p.m)
490 }
491
492 // Keys returns all keys in the same order as in the input.
493 func (p *Properties) Keys() []string {
494         keys := make([]string, len(p.k))
495         copy(keys, p.k)
496         return keys
497 }
498
499 // Set sets the property key to the corresponding value.
500 // If a value for key existed before then ok is true and prev
501 // contains the previous value. If the value contains a
502 // circular reference or a malformed expression then
503 // an error is returned.
504 // An empty key is silently ignored.
505 func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
506         if key == "" {
507                 return "", false, nil
508         }
509
510         // if expansion is disabled we allow circular references
511         if p.DisableExpansion {
512                 prev, ok = p.Get(key)
513                 p.m[key] = value
514                 if !ok {
515                         p.k = append(p.k, key)
516                 }
517                 return prev, ok, nil
518         }
519
520         // to check for a circular reference we temporarily need
521         // to set the new value. If there is an error then revert
522         // to the previous state. Only if all tests are successful
523         // then we add the key to the p.k list.
524         prev, ok = p.Get(key)
525         p.m[key] = value
526
527         // now check for a circular reference
528         _, err = p.expand(value)
529         if err != nil {
530
531                 // revert to the previous state
532                 if ok {
533                         p.m[key] = prev
534                 } else {
535                         delete(p.m, key)
536                 }
537
538                 return "", false, err
539         }
540
541         if !ok {
542                 p.k = append(p.k, key)
543         }
544
545         return prev, ok, nil
546 }
547
548 // SetValue sets property key to the default string value
549 // as defined by fmt.Sprintf("%v").
550 func (p *Properties) SetValue(key string, value interface{}) error {
551         _, _, err := p.Set(key, fmt.Sprintf("%v", value))
552         return err
553 }
554
555 // MustSet sets the property key to the corresponding value.
556 // If a value for key existed before then ok is true and prev
557 // contains the previous value. An empty key is silently ignored.
558 func (p *Properties) MustSet(key, value string) (prev string, ok bool) {
559         prev, ok, err := p.Set(key, value)
560         if err != nil {
561                 ErrorHandler(err)
562         }
563         return prev, ok
564 }
565
566 // String returns a string of all expanded 'key = value' pairs.
567 func (p *Properties) String() string {
568         var s string
569         for _, key := range p.k {
570                 value, _ := p.Get(key)
571                 s = fmt.Sprintf("%s%s = %s\n", s, key, value)
572         }
573         return s
574 }
575
576 // Write writes all unexpanded 'key = value' pairs to the given writer.
577 // Write returns the number of bytes written and any write error encountered.
578 func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {
579         return p.WriteComment(w, "", enc)
580 }
581
582 // WriteComment writes all unexpanced 'key = value' pairs to the given writer.
583 // If prefix is not empty then comments are written with a blank line and the
584 // given prefix. The prefix should be either "# " or "! " to be compatible with
585 // the properties file format. Otherwise, the properties parser will not be
586 // able to read the file back in. It returns the number of bytes written and
587 // any write error encountered.
588 func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {
589         var x int
590
591         for _, key := range p.k {
592                 value := p.m[key]
593
594                 if prefix != "" {
595                         if comments, ok := p.c[key]; ok {
596                                 // don't print comments if they are all empty
597                                 allEmpty := true
598                                 for _, c := range comments {
599                                         if c != "" {
600                                                 allEmpty = false
601                                                 break
602                                         }
603                                 }
604
605                                 if !allEmpty {
606                                         // add a blank line between entries but not at the top
607                                         if len(comments) > 0 && n > 0 {
608                                                 x, err = fmt.Fprintln(w)
609                                                 if err != nil {
610                                                         return
611                                                 }
612                                                 n += x
613                                         }
614
615                                         for _, c := range comments {
616                                                 x, err = fmt.Fprintf(w, "%s%s\n", prefix, encode(c, "", enc))
617                                                 if err != nil {
618                                                         return
619                                                 }
620                                                 n += x
621                                         }
622                                 }
623                         }
624                 }
625
626                 x, err = fmt.Fprintf(w, "%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
627                 if err != nil {
628                         return
629                 }
630                 n += x
631         }
632         return
633 }
634
635 // Map returns a copy of the properties as a map.
636 func (p *Properties) Map() map[string]string {
637         m := make(map[string]string)
638         for k, v := range p.m {
639                 m[k] = v
640         }
641         return m
642 }
643
644 // FilterFunc returns a copy of the properties which includes the values which passed all filters.
645 func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties {
646         pp := NewProperties()
647 outer:
648         for k, v := range p.m {
649                 for _, f := range filters {
650                         if !f(k, v) {
651                                 continue outer
652                         }
653                         pp.Set(k, v)
654                 }
655         }
656         return pp
657 }
658
659 // ----------------------------------------------------------------------------
660
661 // Delete removes the key and its comments.
662 func (p *Properties) Delete(key string) {
663         delete(p.m, key)
664         delete(p.c, key)
665         newKeys := []string{}
666         for _, k := range p.k {
667                 if k != key {
668                         newKeys = append(newKeys, k)
669                 }
670         }
671         p.k = newKeys
672 }
673
674 // Merge merges properties, comments and keys from other *Properties into p
675 func (p *Properties) Merge(other *Properties) {
676         for k, v := range other.m {
677                 p.m[k] = v
678         }
679         for k, v := range other.c {
680                 p.c[k] = v
681         }
682
683 outer:
684         for _, otherKey := range other.k {
685                 for _, key := range p.k {
686                         if otherKey == key {
687                                 continue outer
688                         }
689                 }
690                 p.k = append(p.k, otherKey)
691         }
692 }
693
694 // ----------------------------------------------------------------------------
695
696 // check expands all values and returns an error if a circular reference or
697 // a malformed expression was found.
698 func (p *Properties) check() error {
699         for _, value := range p.m {
700                 if _, err := p.expand(value); err != nil {
701                         return err
702                 }
703         }
704         return nil
705 }
706
707 func (p *Properties) expand(input string) (string, error) {
708         // no pre/postfix -> nothing to expand
709         if p.Prefix == "" && p.Postfix == "" {
710                 return input, nil
711         }
712
713         return expand(input, make(map[string]bool), p.Prefix, p.Postfix, p.m)
714 }
715
716 // expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
717 // The function keeps track of the keys that were already expanded and stops if it
718 // detects a circular reference or a malformed expression of the form '(prefix)key'.
719 func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) {
720         start := strings.Index(s, prefix)
721         if start == -1 {
722                 return s, nil
723         }
724
725         keyStart := start + len(prefix)
726         keyLen := strings.Index(s[keyStart:], postfix)
727         if keyLen == -1 {
728                 return "", fmt.Errorf("malformed expression")
729         }
730
731         end := keyStart + keyLen + len(postfix) - 1
732         key := s[keyStart : keyStart+keyLen]
733
734         // fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
735
736         if _, ok := keys[key]; ok {
737                 return "", fmt.Errorf("circular reference")
738         }
739
740         val, ok := values[key]
741         if !ok {
742                 val = os.Getenv(key)
743         }
744
745         // remember that we've seen the key
746         keys[key] = true
747
748         return expand(s[:start]+val+s[end+1:], keys, prefix, postfix, values)
749 }
750
751 // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
752 func encode(s string, special string, enc Encoding) string {
753         switch enc {
754         case UTF8:
755                 return encodeUtf8(s, special)
756         case ISO_8859_1:
757                 return encodeIso(s, special)
758         default:
759                 panic(fmt.Sprintf("unsupported encoding %v", enc))
760         }
761 }
762
763 func encodeUtf8(s string, special string) string {
764         v := ""
765         for pos := 0; pos < len(s); {
766                 r, w := utf8.DecodeRuneInString(s[pos:])
767                 pos += w
768                 v += escape(r, special)
769         }
770         return v
771 }
772
773 func encodeIso(s string, special string) string {
774         var r rune
775         var w int
776         var v string
777         for pos := 0; pos < len(s); {
778                 switch r, w = utf8.DecodeRuneInString(s[pos:]); {
779                 case r < 1<<8: // single byte rune -> escape special chars only
780                         v += escape(r, special)
781                 case r < 1<<16: // two byte rune -> unicode literal
782                         v += fmt.Sprintf("\\u%04x", r)
783                 default: // more than two bytes per rune -> can't encode
784                         v += "?"
785                 }
786                 pos += w
787         }
788         return v
789 }
790
791 func escape(r rune, special string) string {
792         switch r {
793         case '\f':
794                 return "\\f"
795         case '\n':
796                 return "\\n"
797         case '\r':
798                 return "\\r"
799         case '\t':
800                 return "\\t"
801         default:
802                 if strings.ContainsRune(special, r) {
803                         return "\\" + string(r)
804                 }
805                 return string(r)
806         }
807 }
808
809 func invalidKeyError(key string) error {
810         return fmt.Errorf("unknown property: %s", key)
811 }