mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-27 12:31:02 +00:00 
			
		
		
		
	* Use vendored go-swagger * vendor go-swagger * revert un wanteed change * remove un-needed GO111MODULE * Update Makefile Co-Authored-By: techknowlogick <matti@mdranta.net>
		
			
				
	
	
		
			833 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			833 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| // Copyright 2018 Frank Schroeder. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package properties
 | |
| 
 | |
| // BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.
 | |
| // BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 	"unicode/utf8"
 | |
| )
 | |
| 
 | |
| const maxExpansionDepth = 64
 | |
| 
 | |
| // ErrorHandlerFunc defines the type of function which handles failures
 | |
| // of the MustXXX() functions. An error handler function must exit
 | |
| // the application after handling the error.
 | |
| type ErrorHandlerFunc func(error)
 | |
| 
 | |
| // ErrorHandler is the function which handles failures of the MustXXX()
 | |
| // functions. The default is LogFatalHandler.
 | |
| var ErrorHandler ErrorHandlerFunc = LogFatalHandler
 | |
| 
 | |
| // LogHandlerFunc defines the function prototype for logging errors.
 | |
| type LogHandlerFunc func(fmt string, args ...interface{})
 | |
| 
 | |
| // LogPrintf defines a log handler which uses log.Printf.
 | |
| var LogPrintf LogHandlerFunc = log.Printf
 | |
| 
 | |
| // LogFatalHandler handles the error by logging a fatal error and exiting.
 | |
| func LogFatalHandler(err error) {
 | |
| 	log.Fatal(err)
 | |
| }
 | |
| 
 | |
| // PanicHandler handles the error by panicking.
 | |
| func PanicHandler(err error) {
 | |
| 	panic(err)
 | |
| }
 | |
| 
 | |
| // -----------------------------------------------------------------------------
 | |
| 
 | |
| // A Properties contains the key/value pairs from the properties input.
 | |
| // All values are stored in unexpanded form and are expanded at runtime
 | |
| type Properties struct {
 | |
| 	// Pre-/Postfix for property expansion.
 | |
| 	Prefix  string
 | |
| 	Postfix string
 | |
| 
 | |
| 	// DisableExpansion controls the expansion of properties on Get()
 | |
| 	// and the check for circular references on Set(). When set to
 | |
| 	// true Properties behaves like a simple key/value store and does
 | |
| 	// not check for circular references on Get() or on Set().
 | |
| 	DisableExpansion bool
 | |
| 
 | |
| 	// Stores the key/value pairs
 | |
| 	m map[string]string
 | |
| 
 | |
| 	// Stores the comments per key.
 | |
| 	c map[string][]string
 | |
| 
 | |
| 	// Stores the keys in order of appearance.
 | |
| 	k []string
 | |
| }
 | |
| 
 | |
| // NewProperties creates a new Properties struct with the default
 | |
| // configuration for "${key}" expressions.
 | |
| func NewProperties() *Properties {
 | |
| 	return &Properties{
 | |
| 		Prefix:  "${",
 | |
| 		Postfix: "}",
 | |
| 		m:       map[string]string{},
 | |
| 		c:       map[string][]string{},
 | |
| 		k:       []string{},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Load reads a buffer into the given Properties struct.
 | |
| func (p *Properties) Load(buf []byte, enc Encoding) error {
 | |
| 	l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion}
 | |
| 	newProperties, err := l.LoadBytes(buf)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	p.Merge(newProperties)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Get returns the expanded value for the given key if exists.
 | |
| // Otherwise, ok is false.
 | |
| func (p *Properties) Get(key string) (value string, ok bool) {
 | |
| 	v, ok := p.m[key]
 | |
| 	if p.DisableExpansion {
 | |
| 		return v, ok
 | |
| 	}
 | |
| 	if !ok {
 | |
| 		return "", false
 | |
| 	}
 | |
| 
 | |
| 	expanded, err := p.expand(key, v)
 | |
| 
 | |
| 	// we guarantee that the expanded value is free of
 | |
| 	// circular references and malformed expressions
 | |
| 	// so we panic if we still get an error here.
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(fmt.Errorf("%s in %q", err, key+" = "+v))
 | |
| 	}
 | |
| 
 | |
| 	return expanded, true
 | |
| }
 | |
| 
 | |
| // MustGet returns the expanded value for the given key if exists.
 | |
| // Otherwise, it panics.
 | |
| func (p *Properties) MustGet(key string) string {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		return v
 | |
| 	}
 | |
| 	ErrorHandler(invalidKeyError(key))
 | |
| 	panic("ErrorHandler should exit")
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // ClearComments removes the comments for all keys.
 | |
| func (p *Properties) ClearComments() {
 | |
| 	p.c = map[string][]string{}
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetComment returns the last comment before the given key or an empty string.
 | |
| func (p *Properties) GetComment(key string) string {
 | |
| 	comments, ok := p.c[key]
 | |
| 	if !ok || len(comments) == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return comments[len(comments)-1]
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetComments returns all comments that appeared before the given key or nil.
 | |
| func (p *Properties) GetComments(key string) []string {
 | |
| 	if comments, ok := p.c[key]; ok {
 | |
| 		return comments
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // SetComment sets the comment for the key.
 | |
| func (p *Properties) SetComment(key, comment string) {
 | |
| 	p.c[key] = []string{comment}
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // SetComments sets the comments for the key. If the comments are nil then
 | |
| // all comments for this key are deleted.
 | |
| func (p *Properties) SetComments(key string, comments []string) {
 | |
| 	if comments == nil {
 | |
| 		delete(p.c, key)
 | |
| 		return
 | |
| 	}
 | |
| 	p.c[key] = comments
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetBool checks if the expanded value is one of '1', 'yes',
 | |
| // 'true' or 'on' if the key exists. The comparison is case-insensitive.
 | |
| // If the key does not exist the default value is returned.
 | |
| func (p *Properties) GetBool(key string, def bool) bool {
 | |
| 	v, err := p.getBool(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // MustGetBool checks if the expanded value is one of '1', 'yes',
 | |
| // 'true' or 'on' if the key exists. The comparison is case-insensitive.
 | |
| // If the key does not exist the function panics.
 | |
| func (p *Properties) MustGetBool(key string) bool {
 | |
| 	v, err := p.getBool(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (p *Properties) getBool(key string) (value bool, err error) {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		return boolVal(v), nil
 | |
| 	}
 | |
| 	return false, invalidKeyError(key)
 | |
| }
 | |
| 
 | |
| func boolVal(v string) bool {
 | |
| 	v = strings.ToLower(v)
 | |
| 	return v == "1" || v == "true" || v == "yes" || v == "on"
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetDuration parses the expanded value as an time.Duration (in ns) if the
 | |
| // key exists. If key does not exist or the value cannot be parsed the default
 | |
| // value is returned. In almost all cases you want to use GetParsedDuration().
 | |
| func (p *Properties) GetDuration(key string, def time.Duration) time.Duration {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return time.Duration(v)
 | |
| }
 | |
| 
 | |
| // MustGetDuration parses the expanded value as an time.Duration (in ns) if
 | |
| // the key exists. If key does not exist or the value cannot be parsed the
 | |
| // function panics. In almost all cases you want to use MustGetParsedDuration().
 | |
| func (p *Properties) MustGetDuration(key string) time.Duration {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return time.Duration(v)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned.
 | |
| func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration {
 | |
| 	s, ok := p.Get(key)
 | |
| 	if !ok {
 | |
| 		return def
 | |
| 	}
 | |
| 	v, err := time.ParseDuration(s)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| func (p *Properties) MustGetParsedDuration(key string) time.Duration {
 | |
| 	s, ok := p.Get(key)
 | |
| 	if !ok {
 | |
| 		ErrorHandler(invalidKeyError(key))
 | |
| 	}
 | |
| 	v, err := time.ParseDuration(s)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetFloat64 parses the expanded value as a float64 if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned.
 | |
| func (p *Properties) GetFloat64(key string, def float64) float64 {
 | |
| 	v, err := p.getFloat64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // MustGetFloat64 parses the expanded value as a float64 if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| func (p *Properties) MustGetFloat64(key string) float64 {
 | |
| 	v, err := p.getFloat64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (p *Properties) getFloat64(key string) (value float64, err error) {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		value, err = strconv.ParseFloat(v, 64)
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		return value, nil
 | |
| 	}
 | |
| 	return 0, invalidKeyError(key)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetInt parses the expanded value as an int if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned. If the value does not fit into an int the
 | |
| // function panics with an out of range error.
 | |
| func (p *Properties) GetInt(key string, def int) int {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return intRangeCheck(key, v)
 | |
| }
 | |
| 
 | |
| // MustGetInt parses the expanded value as an int if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| // If the value does not fit into an int the function panics with
 | |
| // an out of range error.
 | |
| func (p *Properties) MustGetInt(key string) int {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return intRangeCheck(key, v)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetInt64 parses the expanded value as an int64 if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned.
 | |
| func (p *Properties) GetInt64(key string, def int64) int64 {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // MustGetInt64 parses the expanded value as an int if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| func (p *Properties) MustGetInt64(key string) int64 {
 | |
| 	v, err := p.getInt64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (p *Properties) getInt64(key string) (value int64, err error) {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		value, err = strconv.ParseInt(v, 10, 64)
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		return value, nil
 | |
| 	}
 | |
| 	return 0, invalidKeyError(key)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetUint parses the expanded value as an uint if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned. If the value does not fit into an int the
 | |
| // function panics with an out of range error.
 | |
| func (p *Properties) GetUint(key string, def uint) uint {
 | |
| 	v, err := p.getUint64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return uintRangeCheck(key, v)
 | |
| }
 | |
| 
 | |
| // MustGetUint parses the expanded value as an int if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| // If the value does not fit into an int the function panics with
 | |
| // an out of range error.
 | |
| func (p *Properties) MustGetUint(key string) uint {
 | |
| 	v, err := p.getUint64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return uintRangeCheck(key, v)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetUint64 parses the expanded value as an uint64 if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the default
 | |
| // value is returned.
 | |
| func (p *Properties) GetUint64(key string, def uint64) uint64 {
 | |
| 	v, err := p.getUint64(key)
 | |
| 	if err != nil {
 | |
| 		return def
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // MustGetUint64 parses the expanded value as an int if the key exists.
 | |
| // If key does not exist or the value cannot be parsed the function panics.
 | |
| func (p *Properties) MustGetUint64(key string) uint64 {
 | |
| 	v, err := p.getUint64(key)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func (p *Properties) getUint64(key string) (value uint64, err error) {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		value, err = strconv.ParseUint(v, 10, 64)
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		return value, nil
 | |
| 	}
 | |
| 	return 0, invalidKeyError(key)
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // GetString returns the expanded value for the given key if exists or
 | |
| // the default value otherwise.
 | |
| func (p *Properties) GetString(key, def string) string {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		return v
 | |
| 	}
 | |
| 	return def
 | |
| }
 | |
| 
 | |
| // MustGetString returns the expanded value for the given key if exists or
 | |
| // panics otherwise.
 | |
| func (p *Properties) MustGetString(key string) string {
 | |
| 	if v, ok := p.Get(key); ok {
 | |
| 		return v
 | |
| 	}
 | |
| 	ErrorHandler(invalidKeyError(key))
 | |
| 	panic("ErrorHandler should exit")
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // Filter returns a new properties object which contains all properties
 | |
| // for which the key matches the pattern.
 | |
| func (p *Properties) Filter(pattern string) (*Properties, error) {
 | |
| 	re, err := regexp.Compile(pattern)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return p.FilterRegexp(re), nil
 | |
| }
 | |
| 
 | |
| // FilterRegexp returns a new properties object which contains all properties
 | |
| // for which the key matches the regular expression.
 | |
| func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
 | |
| 	pp := NewProperties()
 | |
| 	for _, k := range p.k {
 | |
| 		if re.MatchString(k) {
 | |
| 			// TODO(fs): we are ignoring the error which flags a circular reference.
 | |
| 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
 | |
| 			pp.Set(k, p.m[k])
 | |
| 		}
 | |
| 	}
 | |
| 	return pp
 | |
| }
 | |
| 
 | |
| // FilterPrefix returns a new properties object with a subset of all keys
 | |
| // with the given prefix.
 | |
| func (p *Properties) FilterPrefix(prefix string) *Properties {
 | |
| 	pp := NewProperties()
 | |
| 	for _, k := range p.k {
 | |
| 		if strings.HasPrefix(k, prefix) {
 | |
| 			// TODO(fs): we are ignoring the error which flags a circular reference.
 | |
| 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
 | |
| 			pp.Set(k, p.m[k])
 | |
| 		}
 | |
| 	}
 | |
| 	return pp
 | |
| }
 | |
| 
 | |
| // FilterStripPrefix returns a new properties object with a subset of all keys
 | |
| // with the given prefix and the prefix removed from the keys.
 | |
| func (p *Properties) FilterStripPrefix(prefix string) *Properties {
 | |
| 	pp := NewProperties()
 | |
| 	n := len(prefix)
 | |
| 	for _, k := range p.k {
 | |
| 		if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
 | |
| 			// TODO(fs): we are ignoring the error which flags a circular reference.
 | |
| 			// TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
 | |
| 			// TODO(fs): this function should probably return an error but the signature is fixed
 | |
| 			pp.Set(k[n:], p.m[k])
 | |
| 		}
 | |
| 	}
 | |
| 	return pp
 | |
| }
 | |
| 
 | |
| // Len returns the number of keys.
 | |
| func (p *Properties) Len() int {
 | |
| 	return len(p.m)
 | |
| }
 | |
| 
 | |
| // Keys returns all keys in the same order as in the input.
 | |
| func (p *Properties) Keys() []string {
 | |
| 	keys := make([]string, len(p.k))
 | |
| 	copy(keys, p.k)
 | |
| 	return keys
 | |
| }
 | |
| 
 | |
| // Set sets the property key to the corresponding value.
 | |
| // If a value for key existed before then ok is true and prev
 | |
| // contains the previous value. If the value contains a
 | |
| // circular reference or a malformed expression then
 | |
| // an error is returned.
 | |
| // An empty key is silently ignored.
 | |
| func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
 | |
| 	if key == "" {
 | |
| 		return "", false, nil
 | |
| 	}
 | |
| 
 | |
| 	// if expansion is disabled we allow circular references
 | |
| 	if p.DisableExpansion {
 | |
| 		prev, ok = p.Get(key)
 | |
| 		p.m[key] = value
 | |
| 		if !ok {
 | |
| 			p.k = append(p.k, key)
 | |
| 		}
 | |
| 		return prev, ok, nil
 | |
| 	}
 | |
| 
 | |
| 	// to check for a circular reference we temporarily need
 | |
| 	// to set the new value. If there is an error then revert
 | |
| 	// to the previous state. Only if all tests are successful
 | |
| 	// then we add the key to the p.k list.
 | |
| 	prev, ok = p.Get(key)
 | |
| 	p.m[key] = value
 | |
| 
 | |
| 	// now check for a circular reference
 | |
| 	_, err = p.expand(key, value)
 | |
| 	if err != nil {
 | |
| 
 | |
| 		// revert to the previous state
 | |
| 		if ok {
 | |
| 			p.m[key] = prev
 | |
| 		} else {
 | |
| 			delete(p.m, key)
 | |
| 		}
 | |
| 
 | |
| 		return "", false, err
 | |
| 	}
 | |
| 
 | |
| 	if !ok {
 | |
| 		p.k = append(p.k, key)
 | |
| 	}
 | |
| 
 | |
| 	return prev, ok, nil
 | |
| }
 | |
| 
 | |
| // SetValue sets property key to the default string value
 | |
| // as defined by fmt.Sprintf("%v").
 | |
| func (p *Properties) SetValue(key string, value interface{}) error {
 | |
| 	_, _, err := p.Set(key, fmt.Sprintf("%v", value))
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // MustSet sets the property key to the corresponding value.
 | |
| // If a value for key existed before then ok is true and prev
 | |
| // contains the previous value. An empty key is silently ignored.
 | |
| func (p *Properties) MustSet(key, value string) (prev string, ok bool) {
 | |
| 	prev, ok, err := p.Set(key, value)
 | |
| 	if err != nil {
 | |
| 		ErrorHandler(err)
 | |
| 	}
 | |
| 	return prev, ok
 | |
| }
 | |
| 
 | |
| // String returns a string of all expanded 'key = value' pairs.
 | |
| func (p *Properties) String() string {
 | |
| 	var s string
 | |
| 	for _, key := range p.k {
 | |
| 		value, _ := p.Get(key)
 | |
| 		s = fmt.Sprintf("%s%s = %s\n", s, key, value)
 | |
| 	}
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // Write writes all unexpanded 'key = value' pairs to the given writer.
 | |
| // Write returns the number of bytes written and any write error encountered.
 | |
| func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {
 | |
| 	return p.WriteComment(w, "", enc)
 | |
| }
 | |
| 
 | |
| // WriteComment writes all unexpanced 'key = value' pairs to the given writer.
 | |
| // If prefix is not empty then comments are written with a blank line and the
 | |
| // given prefix. The prefix should be either "# " or "! " to be compatible with
 | |
| // the properties file format. Otherwise, the properties parser will not be
 | |
| // able to read the file back in. It returns the number of bytes written and
 | |
| // any write error encountered.
 | |
| func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {
 | |
| 	var x int
 | |
| 
 | |
| 	for _, key := range p.k {
 | |
| 		value := p.m[key]
 | |
| 
 | |
| 		if prefix != "" {
 | |
| 			if comments, ok := p.c[key]; ok {
 | |
| 				// don't print comments if they are all empty
 | |
| 				allEmpty := true
 | |
| 				for _, c := range comments {
 | |
| 					if c != "" {
 | |
| 						allEmpty = false
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if !allEmpty {
 | |
| 					// add a blank line between entries but not at the top
 | |
| 					if len(comments) > 0 && n > 0 {
 | |
| 						x, err = fmt.Fprintln(w)
 | |
| 						if err != nil {
 | |
| 							return
 | |
| 						}
 | |
| 						n += x
 | |
| 					}
 | |
| 
 | |
| 					for _, c := range comments {
 | |
| 						x, err = fmt.Fprintf(w, "%s%s\n", prefix, encode(c, "", enc))
 | |
| 						if err != nil {
 | |
| 							return
 | |
| 						}
 | |
| 						n += x
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		x, err = fmt.Fprintf(w, "%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		n += x
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Map returns a copy of the properties as a map.
 | |
| func (p *Properties) Map() map[string]string {
 | |
| 	m := make(map[string]string)
 | |
| 	for k, v := range p.m {
 | |
| 		m[k] = v
 | |
| 	}
 | |
| 	return m
 | |
| }
 | |
| 
 | |
| // FilterFunc returns a copy of the properties which includes the values which passed all filters.
 | |
| func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties {
 | |
| 	pp := NewProperties()
 | |
| outer:
 | |
| 	for k, v := range p.m {
 | |
| 		for _, f := range filters {
 | |
| 			if !f(k, v) {
 | |
| 				continue outer
 | |
| 			}
 | |
| 			pp.Set(k, v)
 | |
| 		}
 | |
| 	}
 | |
| 	return pp
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // Delete removes the key and its comments.
 | |
| func (p *Properties) Delete(key string) {
 | |
| 	delete(p.m, key)
 | |
| 	delete(p.c, key)
 | |
| 	newKeys := []string{}
 | |
| 	for _, k := range p.k {
 | |
| 		if k != key {
 | |
| 			newKeys = append(newKeys, k)
 | |
| 		}
 | |
| 	}
 | |
| 	p.k = newKeys
 | |
| }
 | |
| 
 | |
| // Merge merges properties, comments and keys from other *Properties into p
 | |
| func (p *Properties) Merge(other *Properties) {
 | |
| 	for k, v := range other.m {
 | |
| 		p.m[k] = v
 | |
| 	}
 | |
| 	for k, v := range other.c {
 | |
| 		p.c[k] = v
 | |
| 	}
 | |
| 
 | |
| outer:
 | |
| 	for _, otherKey := range other.k {
 | |
| 		for _, key := range p.k {
 | |
| 			if otherKey == key {
 | |
| 				continue outer
 | |
| 			}
 | |
| 		}
 | |
| 		p.k = append(p.k, otherKey)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ----------------------------------------------------------------------------
 | |
| 
 | |
| // check expands all values and returns an error if a circular reference or
 | |
| // a malformed expression was found.
 | |
| func (p *Properties) check() error {
 | |
| 	for key, value := range p.m {
 | |
| 		if _, err := p.expand(key, value); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Properties) expand(key, input string) (string, error) {
 | |
| 	// no pre/postfix -> nothing to expand
 | |
| 	if p.Prefix == "" && p.Postfix == "" {
 | |
| 		return input, nil
 | |
| 	}
 | |
| 
 | |
| 	return expand(input, []string{key}, p.Prefix, p.Postfix, p.m)
 | |
| }
 | |
| 
 | |
| // expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
 | |
| // The function keeps track of the keys that were already expanded and stops if it
 | |
| // detects a circular reference or a malformed expression of the form '(prefix)key'.
 | |
| func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) {
 | |
| 	if len(keys) > maxExpansionDepth {
 | |
| 		return "", fmt.Errorf("expansion too deep")
 | |
| 	}
 | |
| 
 | |
| 	for {
 | |
| 		start := strings.Index(s, prefix)
 | |
| 		if start == -1 {
 | |
| 			return s, nil
 | |
| 		}
 | |
| 
 | |
| 		keyStart := start + len(prefix)
 | |
| 		keyLen := strings.Index(s[keyStart:], postfix)
 | |
| 		if keyLen == -1 {
 | |
| 			return "", fmt.Errorf("malformed expression")
 | |
| 		}
 | |
| 
 | |
| 		end := keyStart + keyLen + len(postfix) - 1
 | |
| 		key := s[keyStart : keyStart+keyLen]
 | |
| 
 | |
| 		// 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)
 | |
| 
 | |
| 		for _, k := range keys {
 | |
| 			if key == k {
 | |
| 				return "", fmt.Errorf("circular reference")
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		val, ok := values[key]
 | |
| 		if !ok {
 | |
| 			val = os.Getenv(key)
 | |
| 		}
 | |
| 		new_val, err := expand(val, append(keys, key), prefix, postfix, values)
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		s = s[:start] + new_val + s[end+1:]
 | |
| 	}
 | |
| 	return s, nil
 | |
| }
 | |
| 
 | |
| // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
 | |
| func encode(s string, special string, enc Encoding) string {
 | |
| 	switch enc {
 | |
| 	case UTF8:
 | |
| 		return encodeUtf8(s, special)
 | |
| 	case ISO_8859_1:
 | |
| 		return encodeIso(s, special)
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("unsupported encoding %v", enc))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func encodeUtf8(s string, special string) string {
 | |
| 	v := ""
 | |
| 	for pos := 0; pos < len(s); {
 | |
| 		r, w := utf8.DecodeRuneInString(s[pos:])
 | |
| 		pos += w
 | |
| 		v += escape(r, special)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func encodeIso(s string, special string) string {
 | |
| 	var r rune
 | |
| 	var w int
 | |
| 	var v string
 | |
| 	for pos := 0; pos < len(s); {
 | |
| 		switch r, w = utf8.DecodeRuneInString(s[pos:]); {
 | |
| 		case r < 1<<8: // single byte rune -> escape special chars only
 | |
| 			v += escape(r, special)
 | |
| 		case r < 1<<16: // two byte rune -> unicode literal
 | |
| 			v += fmt.Sprintf("\\u%04x", r)
 | |
| 		default: // more than two bytes per rune -> can't encode
 | |
| 			v += "?"
 | |
| 		}
 | |
| 		pos += w
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func escape(r rune, special string) string {
 | |
| 	switch r {
 | |
| 	case '\f':
 | |
| 		return "\\f"
 | |
| 	case '\n':
 | |
| 		return "\\n"
 | |
| 	case '\r':
 | |
| 		return "\\r"
 | |
| 	case '\t':
 | |
| 		return "\\t"
 | |
| 	default:
 | |
| 		if strings.ContainsRune(special, r) {
 | |
| 			return "\\" + string(r)
 | |
| 		}
 | |
| 		return string(r)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func invalidKeyError(key string) error {
 | |
| 	return fmt.Errorf("unknown property: %s", key)
 | |
| }
 |