section.go raw

   1  // Copyright 2014 Unknwon
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
   4  // not use this file except in compliance with the License. You may obtain
   5  // a copy of the License at
   6  //
   7  //     http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12  // License for the specific language governing permissions and limitations
  13  // under the License.
  14  
  15  package ini
  16  
  17  import (
  18  	"errors"
  19  	"fmt"
  20  	"strings"
  21  )
  22  
  23  // Section represents a config section.
  24  type Section struct {
  25  	f        *File
  26  	Comment  string
  27  	name     string
  28  	keys     map[string]*Key
  29  	keyList  []string
  30  	keysHash map[string]string
  31  
  32  	isRawSection bool
  33  	rawBody      string
  34  }
  35  
  36  func newSection(f *File, name string) *Section {
  37  	return &Section{
  38  		f:        f,
  39  		name:     name,
  40  		keys:     make(map[string]*Key),
  41  		keyList:  make([]string, 0, 10),
  42  		keysHash: make(map[string]string),
  43  	}
  44  }
  45  
  46  // Name returns name of Section.
  47  func (s *Section) Name() string {
  48  	return s.name
  49  }
  50  
  51  // Body returns rawBody of Section if the section was marked as unparseable.
  52  // It still follows the other rules of the INI format surrounding leading/trailing whitespace.
  53  func (s *Section) Body() string {
  54  	return strings.TrimSpace(s.rawBody)
  55  }
  56  
  57  // SetBody updates body content only if section is raw.
  58  func (s *Section) SetBody(body string) {
  59  	if !s.isRawSection {
  60  		return
  61  	}
  62  	s.rawBody = body
  63  }
  64  
  65  // NewKey creates a new key to given section.
  66  func (s *Section) NewKey(name, val string) (*Key, error) {
  67  	if len(name) == 0 {
  68  		return nil, errors.New("error creating new key: empty key name")
  69  	} else if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
  70  		name = strings.ToLower(name)
  71  	}
  72  
  73  	if s.f.BlockMode {
  74  		s.f.lock.Lock()
  75  		defer s.f.lock.Unlock()
  76  	}
  77  
  78  	if inSlice(name, s.keyList) {
  79  		if s.f.options.AllowShadows {
  80  			if err := s.keys[name].addShadow(val); err != nil {
  81  				return nil, err
  82  			}
  83  		} else {
  84  			s.keys[name].value = val
  85  			s.keysHash[name] = val
  86  		}
  87  		return s.keys[name], nil
  88  	}
  89  
  90  	s.keyList = append(s.keyList, name)
  91  	s.keys[name] = newKey(s, name, val)
  92  	s.keysHash[name] = val
  93  	return s.keys[name], nil
  94  }
  95  
  96  // NewBooleanKey creates a new boolean type key to given section.
  97  func (s *Section) NewBooleanKey(name string) (*Key, error) {
  98  	key, err := s.NewKey(name, "true")
  99  	if err != nil {
 100  		return nil, err
 101  	}
 102  
 103  	key.isBooleanType = true
 104  	return key, nil
 105  }
 106  
 107  // GetKey returns key in section by given name.
 108  func (s *Section) GetKey(name string) (*Key, error) {
 109  	if s.f.BlockMode {
 110  		s.f.lock.RLock()
 111  	}
 112  	if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
 113  		name = strings.ToLower(name)
 114  	}
 115  	key := s.keys[name]
 116  	if s.f.BlockMode {
 117  		s.f.lock.RUnlock()
 118  	}
 119  
 120  	if key == nil {
 121  		// Check if it is a child-section.
 122  		sname := s.name
 123  		for {
 124  			if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
 125  				sname = sname[:i]
 126  				sec, err := s.f.GetSection(sname)
 127  				if err != nil {
 128  					continue
 129  				}
 130  				return sec.GetKey(name)
 131  			}
 132  			break
 133  		}
 134  		return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name)
 135  	}
 136  	return key, nil
 137  }
 138  
 139  // HasKey returns true if section contains a key with given name.
 140  func (s *Section) HasKey(name string) bool {
 141  	key, _ := s.GetKey(name)
 142  	return key != nil
 143  }
 144  
 145  // Deprecated: Use "HasKey" instead.
 146  func (s *Section) Haskey(name string) bool {
 147  	return s.HasKey(name)
 148  }
 149  
 150  // HasValue returns true if section contains given raw value.
 151  func (s *Section) HasValue(value string) bool {
 152  	if s.f.BlockMode {
 153  		s.f.lock.RLock()
 154  		defer s.f.lock.RUnlock()
 155  	}
 156  
 157  	for _, k := range s.keys {
 158  		if value == k.value {
 159  			return true
 160  		}
 161  	}
 162  	return false
 163  }
 164  
 165  // Key assumes named Key exists in section and returns a zero-value when not.
 166  func (s *Section) Key(name string) *Key {
 167  	key, err := s.GetKey(name)
 168  	if err != nil {
 169  		// It's OK here because the only possible error is empty key name,
 170  		// but if it's empty, this piece of code won't be executed.
 171  		key, _ = s.NewKey(name, "")
 172  		return key
 173  	}
 174  	return key
 175  }
 176  
 177  // Keys returns list of keys of section.
 178  func (s *Section) Keys() []*Key {
 179  	keys := make([]*Key, len(s.keyList))
 180  	for i := range s.keyList {
 181  		keys[i] = s.Key(s.keyList[i])
 182  	}
 183  	return keys
 184  }
 185  
 186  // ParentKeys returns list of keys of parent section.
 187  func (s *Section) ParentKeys() []*Key {
 188  	var parentKeys []*Key
 189  	sname := s.name
 190  	for {
 191  		if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
 192  			sname = sname[:i]
 193  			sec, err := s.f.GetSection(sname)
 194  			if err != nil {
 195  				continue
 196  			}
 197  			parentKeys = append(parentKeys, sec.Keys()...)
 198  		} else {
 199  			break
 200  		}
 201  
 202  	}
 203  	return parentKeys
 204  }
 205  
 206  // KeyStrings returns list of key names of section.
 207  func (s *Section) KeyStrings() []string {
 208  	list := make([]string, len(s.keyList))
 209  	copy(list, s.keyList)
 210  	return list
 211  }
 212  
 213  // KeysHash returns keys hash consisting of names and values.
 214  func (s *Section) KeysHash() map[string]string {
 215  	if s.f.BlockMode {
 216  		s.f.lock.RLock()
 217  		defer s.f.lock.RUnlock()
 218  	}
 219  
 220  	hash := make(map[string]string, len(s.keysHash))
 221  	for key, value := range s.keysHash {
 222  		hash[key] = value
 223  	}
 224  	return hash
 225  }
 226  
 227  // DeleteKey deletes a key from section.
 228  func (s *Section) DeleteKey(name string) {
 229  	if s.f.BlockMode {
 230  		s.f.lock.Lock()
 231  		defer s.f.lock.Unlock()
 232  	}
 233  
 234  	for i, k := range s.keyList {
 235  		if k == name {
 236  			s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
 237  			delete(s.keys, name)
 238  			delete(s.keysHash, name)
 239  			return
 240  		}
 241  	}
 242  }
 243  
 244  // ChildSections returns a list of child sections of current section.
 245  // For example, "[parent.child1]" and "[parent.child12]" are child sections
 246  // of section "[parent]".
 247  func (s *Section) ChildSections() []*Section {
 248  	prefix := s.name + s.f.options.ChildSectionDelimiter
 249  	children := make([]*Section, 0, 3)
 250  	for _, name := range s.f.sectionList {
 251  		if strings.HasPrefix(name, prefix) {
 252  			children = append(children, s.f.sections[name]...)
 253  		}
 254  	}
 255  	return children
 256  }
 257