1
2
3
4
5 package properties
6
7 import (
8 "fmt"
9 "io/ioutil"
10 "net/http"
11 "os"
12 "strings"
13 )
14
15
16 type Encoding uint
17
18 const (
19
20
21
22
23 utf8Default Encoding = iota
24
25
26 UTF8
27
28
29 ISO_8859_1
30 )
31
32 type Loader struct {
33
34
35
36 Encoding Encoding
37
38
39
40
41
42 DisableExpansion bool
43
44
45
46
47 IgnoreMissing bool
48 }
49
50
51 func (l *Loader) LoadBytes(buf []byte) (*Properties, error) {
52 return l.loadBytes(buf, l.Encoding)
53 }
54
55
56
57
58
59
60 func (l *Loader) LoadAll(names []string) (*Properties, error) {
61 all := NewProperties()
62 for _, name := range names {
63 n, err := expandName(name)
64 if err != nil {
65 return nil, err
66 }
67
68 var p *Properties
69 switch {
70 case strings.HasPrefix(n, "http://"):
71 p, err = l.LoadURL(n)
72 case strings.HasPrefix(n, "https://"):
73 p, err = l.LoadURL(n)
74 default:
75 p, err = l.LoadFile(n)
76 }
77 if err != nil {
78 return nil, err
79 }
80 all.Merge(p)
81 }
82
83 all.DisableExpansion = l.DisableExpansion
84 if all.DisableExpansion {
85 return all, nil
86 }
87 return all, all.check()
88 }
89
90
91
92
93 func (l *Loader) LoadFile(filename string) (*Properties, error) {
94 data, err := ioutil.ReadFile(filename)
95 if err != nil {
96 if l.IgnoreMissing && os.IsNotExist(err) {
97 LogPrintf("properties: %s not found. skipping", filename)
98 return NewProperties(), nil
99 }
100 return nil, err
101 }
102 return l.loadBytes(data, l.Encoding)
103 }
104
105
106
107
108
109
110
111
112
113 func (l *Loader) LoadURL(url string) (*Properties, error) {
114 resp, err := http.Get(url)
115 if err != nil {
116 return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
117 }
118 defer resp.Body.Close()
119
120 if resp.StatusCode == 404 && l.IgnoreMissing {
121 LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
122 return NewProperties(), nil
123 }
124
125 if resp.StatusCode != 200 {
126 return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
127 }
128
129 body, err := ioutil.ReadAll(resp.Body)
130 if err != nil {
131 return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
132 }
133
134 ct := resp.Header.Get("Content-Type")
135 ct = strings.Join(strings.Fields(ct), "")
136 var enc Encoding
137 switch strings.ToLower(ct) {
138 case "text/plain", "text/plain;charset=iso-8859-1", "text/plain;charset=latin1":
139 enc = ISO_8859_1
140 case "", "text/plain;charset=utf-8":
141 enc = UTF8
142 default:
143 return nil, fmt.Errorf("properties: invalid content type %s", ct)
144 }
145
146 return l.loadBytes(body, enc)
147 }
148
149 func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) {
150 p, err := parse(convert(buf, enc))
151 if err != nil {
152 return nil, err
153 }
154 p.DisableExpansion = l.DisableExpansion
155 if p.DisableExpansion {
156 return p, nil
157 }
158 return p, p.check()
159 }
160
161
162 func Load(buf []byte, enc Encoding) (*Properties, error) {
163 l := &Loader{Encoding: enc}
164 return l.LoadBytes(buf)
165 }
166
167
168 func LoadString(s string) (*Properties, error) {
169 l := &Loader{Encoding: UTF8}
170 return l.LoadBytes([]byte(s))
171 }
172
173
174 func LoadMap(m map[string]string) *Properties {
175 p := NewProperties()
176 for k, v := range m {
177 p.Set(k, v)
178 }
179 return p
180 }
181
182
183 func LoadFile(filename string, enc Encoding) (*Properties, error) {
184 l := &Loader{Encoding: enc}
185 return l.LoadAll([]string{filename})
186 }
187
188
189
190
191 func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
192 l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
193 return l.LoadAll(filenames)
194 }
195
196
197
198 func LoadURL(url string) (*Properties, error) {
199 l := &Loader{Encoding: UTF8}
200 return l.LoadAll([]string{url})
201 }
202
203
204
205
206
207 func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
208 l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing}
209 return l.LoadAll(urls)
210 }
211
212
213
214
215
216 func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
217 l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
218 return l.LoadAll(names)
219 }
220
221
222
223 func MustLoadString(s string) *Properties {
224 return must(LoadString(s))
225 }
226
227
228
229 func MustLoadFile(filename string, enc Encoding) *Properties {
230 return must(LoadFile(filename, enc))
231 }
232
233
234
235
236 func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties {
237 return must(LoadFiles(filenames, enc, ignoreMissing))
238 }
239
240
241
242 func MustLoadURL(url string) *Properties {
243 return must(LoadURL(url))
244 }
245
246
247
248
249 func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
250 return must(LoadURLs(urls, ignoreMissing))
251 }
252
253
254
255
256
257 func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
258 return must(LoadAll(names, enc, ignoreMissing))
259 }
260
261 func must(p *Properties, err error) *Properties {
262 if err != nil {
263 ErrorHandler(err)
264 }
265 return p
266 }
267
268
269
270
271
272 func expandName(name string) (string, error) {
273 return expand(name, []string{}, "${", "}", make(map[string]string))
274 }
275
276
277
278
279 func convert(buf []byte, enc Encoding) string {
280 switch enc {
281 case utf8Default, UTF8:
282 return string(buf)
283 case ISO_8859_1:
284 runes := make([]rune, len(buf))
285 for i, b := range buf {
286 runes[i] = rune(b)
287 }
288 return string(runes)
289 default:
290 ErrorHandler(fmt.Errorf("unsupported encoding %v", enc))
291 }
292 panic("ErrorHandler should exit")
293 }
294
View as plain text