1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.ini4j;
17
18 import org.ini4j.spi.IniHandler;
19 import org.ini4j.spi.Warnings;
20
21 import java.io.File;
22 import java.io.FileReader;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.io.Reader;
28 import java.io.Serializable;
29 import java.io.Writer;
30
31 import java.net.URL;
32
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41 public class ConfigParser implements Serializable
42 {
43 private static final long serialVersionUID = 9118857036229164353L;
44 private PyIni _ini;
45
46 @SuppressWarnings(Warnings.UNCHECKED)
47 public ConfigParser()
48 {
49 this(Collections.EMPTY_MAP);
50 }
51
52 public ConfigParser(Map<String, String> defaults)
53 {
54 _ini = new PyIni(defaults);
55 }
56
57 public boolean getBoolean(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
58 {
59 boolean ret;
60 String value = get(section, option);
61
62 if ("1".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value))
63 {
64 ret = true;
65 }
66 else if ("0".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)
67 || "off".equalsIgnoreCase(value))
68 {
69 ret = false;
70 }
71 else
72 {
73 throw new IllegalArgumentException(value);
74 }
75
76 return ret;
77 }
78
79 public double getDouble(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
80 {
81 return Double.parseDouble(get(section, option));
82 }
83
84 public float getFloat(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
85 {
86 return Float.parseFloat(get(section, option));
87 }
88
89 public int getInt(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
90 {
91 return Integer.parseInt(get(section, option));
92 }
93
94 public long getLong(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
95 {
96 return Long.parseLong(get(section, option));
97 }
98
99 public void addSection(String section) throws DuplicateSectionException
100 {
101 if (_ini.containsKey(section))
102 {
103 throw new DuplicateSectionException(section);
104 }
105 else if (PyIni.DEFAULT_SECTION_NAME.equalsIgnoreCase(section))
106 {
107 throw new IllegalArgumentException(section);
108 }
109
110 _ini.add(section);
111 }
112
113 public Map<String, String> defaults()
114 {
115 return _ini.getDefaults();
116 }
117
118 @SuppressWarnings(Warnings.UNCHECKED)
119 public String get(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
120 {
121 return get(section, option, false, Collections.EMPTY_MAP);
122 }
123
124 @SuppressWarnings(Warnings.UNCHECKED)
125 public String get(String section, String option, boolean raw) throws NoSectionException, NoOptionException, InterpolationException
126 {
127 return get(section, option, raw, Collections.EMPTY_MAP);
128 }
129
130 public String get(String sectionName, String optionName, boolean raw, Map<String, String> variables) throws NoSectionException,
131 NoOptionException, InterpolationException
132 {
133 String value = requireOption(sectionName, optionName);
134
135 if (!raw && (value != null) && (value.indexOf(PyIni.SUBST_CHAR) >= 0))
136 {
137 value = _ini.fetch(sectionName, optionName, variables);
138 }
139
140 return value;
141 }
142
143 public boolean hasOption(String sectionName, String optionName)
144 {
145 Ini.Section section = _ini.get(sectionName);
146
147 return (section != null) && section.containsKey(optionName);
148 }
149
150 public boolean hasSection(String sectionName)
151 {
152 return _ini.containsKey(sectionName);
153 }
154
155 @SuppressWarnings(Warnings.UNCHECKED)
156 public List<Map.Entry<String, String>> items(String sectionName) throws NoSectionException, InterpolationMissingOptionException
157 {
158 return items(sectionName, false, Collections.EMPTY_MAP);
159 }
160
161 @SuppressWarnings(Warnings.UNCHECKED)
162 public List<Map.Entry<String, String>> items(String sectionName, boolean raw) throws NoSectionException,
163 InterpolationMissingOptionException
164 {
165 return items(sectionName, raw, Collections.EMPTY_MAP);
166 }
167
168 public List<Map.Entry<String, String>> items(String sectionName, boolean raw, Map<String, String> variables) throws NoSectionException,
169 InterpolationMissingOptionException
170 {
171 Ini.Section section = requireSection(sectionName);
172 Map<String, String> ret;
173
174 if (raw)
175 {
176 ret = new HashMap<String, String>(section);
177 }
178 else
179 {
180 ret = new HashMap<String, String>();
181 for (String key : section.keySet())
182 {
183 ret.put(key, _ini.fetch(section, key, variables));
184 }
185 }
186
187 return new ArrayList<Map.Entry<String, String>>(ret.entrySet());
188 }
189
190 public List<String> options(String sectionName) throws NoSectionException
191 {
192 requireSection(sectionName);
193
194 return new ArrayList<String>(_ini.get(sectionName).keySet());
195 }
196
197 public void read(String... filenames) throws IOException, ParsingException
198 {
199 for (String filename : filenames)
200 {
201 read(new File(filename));
202 }
203 }
204
205 public void read(Reader reader) throws IOException, ParsingException
206 {
207 try
208 {
209 _ini.load(reader);
210 }
211 catch (InvalidFileFormatException x)
212 {
213 throw new ParsingException(x);
214 }
215 }
216
217 public void read(URL url) throws IOException, ParsingException
218 {
219 try
220 {
221 _ini.load(url);
222 }
223 catch (InvalidFileFormatException x)
224 {
225 throw new ParsingException(x);
226 }
227 }
228
229 public void read(File file) throws IOException, ParsingException
230 {
231 try
232 {
233 _ini.load(new FileReader(file));
234 }
235 catch (InvalidFileFormatException x)
236 {
237 throw new ParsingException(x);
238 }
239 }
240
241 public void read(InputStream stream) throws IOException, ParsingException
242 {
243 try
244 {
245 _ini.load(stream);
246 }
247 catch (InvalidFileFormatException x)
248 {
249 throw new ParsingException(x);
250 }
251 }
252
253 public boolean removeOption(String sectionName, String optionName) throws NoSectionException
254 {
255 Ini.Section section = requireSection(sectionName);
256 boolean ret = section.containsKey(optionName);
257
258 section.remove(optionName);
259
260 return ret;
261 }
262
263 public boolean removeSection(String sectionName)
264 {
265 boolean ret = _ini.containsKey(sectionName);
266
267 _ini.remove(sectionName);
268
269 return ret;
270 }
271
272 public List<String> sections()
273 {
274 return new ArrayList<String>(_ini.keySet());
275 }
276
277 public void set(String sectionName, String optionName, Object value) throws NoSectionException
278 {
279 Ini.Section section = requireSection(sectionName);
280
281 if (value == null)
282 {
283 section.remove(optionName);
284 }
285 else
286 {
287 section.put(optionName, value.toString());
288 }
289 }
290
291 public void write(Writer writer) throws IOException
292 {
293 _ini.store(writer);
294 }
295
296 public void write(OutputStream stream) throws IOException
297 {
298 _ini.store(stream);
299 }
300
301 public void write(File file) throws IOException
302 {
303 _ini.store(new FileWriter(file));
304 }
305
306 protected Ini getIni()
307 {
308 return _ini;
309 }
310
311 private String requireOption(String sectionName, String optionName) throws NoSectionException, NoOptionException
312 {
313 Ini.Section section = requireSection(sectionName);
314 String option = section.get(optionName);
315
316 if (option == null)
317 {
318 throw new NoOptionException(optionName);
319 }
320
321 return option;
322 }
323
324 private Ini.Section requireSection(String sectionName) throws NoSectionException
325 {
326 Ini.Section section = _ini.get(sectionName);
327
328 if (section == null)
329 {
330 throw new NoSectionException(sectionName);
331 }
332
333 return section;
334 }
335
336 public static class ConfigParserException extends Exception
337 {
338
339 private static final long serialVersionUID = -6845546313519392093L;
340
341 public ConfigParserException(String message)
342 {
343 super(message);
344 }
345 }
346
347 public static final class DuplicateSectionException extends ConfigParserException
348 {
349
350 private static final long serialVersionUID = -5244008445735700699L;
351
352 private DuplicateSectionException(String message)
353 {
354 super(message);
355 }
356 }
357
358 public static class InterpolationException extends ConfigParserException
359 {
360
361 private static final long serialVersionUID = 8924443303158546939L;
362
363 protected InterpolationException(String message)
364 {
365 super(message);
366 }
367 }
368
369 public static final class InterpolationMissingOptionException extends InterpolationException
370 {
371
372 private static final long serialVersionUID = 2903136975820447879L;
373
374 private InterpolationMissingOptionException(String message)
375 {
376 super(message);
377 }
378 }
379
380 public static final class NoOptionException extends ConfigParserException
381 {
382
383 private static final long serialVersionUID = 8460082078809425858L;
384
385 private NoOptionException(String message)
386 {
387 super(message);
388 }
389 }
390
391 public static final class NoSectionException extends ConfigParserException
392 {
393
394 private static final long serialVersionUID = 8553627727493146118L;
395
396 private NoSectionException(String message)
397 {
398 super(message);
399 }
400 }
401
402 public static final class ParsingException extends IOException
403 {
404
405 private static final long serialVersionUID = -5395990242007205038L;
406
407 private ParsingException(Throwable cause)
408 {
409 super(cause.getMessage());
410 initCause(cause);
411 }
412 }
413
414 static class PyIni extends Ini
415 {
416 private static final char SUBST_CHAR = '%';
417 private static final Pattern EXPRESSION = Pattern.compile("(?<!\\\\)\\%\\(([^\\)]+)\\)");
418 private static final int G_OPTION = 1;
419 protected static final String DEFAULT_SECTION_NAME = "DEFAULT";
420 private static final long serialVersionUID = -7152857626328996122L;
421 private final Map<String, String> _defaults;
422 private Ini.Section _defaultSection;
423
424 public PyIni(Map<String, String> defaults)
425 {
426 _defaults = defaults;
427 Config cfg = getConfig().clone();
428
429 cfg.setEscape(false);
430 cfg.setMultiOption(false);
431 cfg.setMultiSection(false);
432 cfg.setLowerCaseOption(true);
433 cfg.setLowerCaseSection(true);
434 super.setConfig(cfg);
435 }
436
437 @Override public void setConfig(Config value)
438 {
439 assert true;
440 }
441
442 public Map<String, String> getDefaults()
443 {
444 return _defaults;
445 }
446
447 @Override public Section add(String name)
448 {
449 Section section;
450
451 if (DEFAULT_SECTION_NAME.equalsIgnoreCase(name))
452 {
453 if (_defaultSection == null)
454 {
455 _defaultSection = newSection(name);
456 }
457
458 section = _defaultSection;
459 }
460 else
461 {
462 section = super.add(name);
463 }
464
465 return section;
466 }
467
468 public String fetch(String sectionName, String optionName, Map<String, String> variables) throws InterpolationMissingOptionException
469 {
470 return fetch(get(sectionName), optionName, variables);
471 }
472
473 protected Ini.Section getDefaultSection()
474 {
475 return _defaultSection;
476 }
477
478 protected String fetch(Ini.Section section, String optionName, Map<String, String> variables)
479 throws InterpolationMissingOptionException
480 {
481 String value = section.get(optionName);
482
483 if ((value != null) && (value.indexOf(SUBST_CHAR) >= 0))
484 {
485 StringBuilder buffer = new StringBuilder(value);
486
487 resolve(buffer, section, variables);
488 value = buffer.toString();
489 }
490
491 return value;
492 }
493
494 protected void resolve(StringBuilder buffer, Ini.Section owner, Map<String, String> vars) throws InterpolationMissingOptionException
495 {
496 Matcher m = EXPRESSION.matcher(buffer);
497
498 while (m.find())
499 {
500 String optionName = m.group(G_OPTION);
501 String value = owner.get(optionName);
502
503 if (value == null)
504 {
505 value = vars.get(optionName);
506 }
507
508 if (value == null)
509 {
510 value = _defaults.get(optionName);
511 }
512
513 if ((value == null) && (_defaultSection != null))
514 {
515 value = _defaultSection.get(optionName);
516 }
517
518 if (value == null)
519 {
520 throw new InterpolationMissingOptionException(optionName);
521 }
522
523 buffer.replace(m.start(), m.end(), value);
524 m.reset(buffer);
525 }
526 }
527
528 @Override protected void store(IniHandler formatter)
529 {
530 formatter.startIni();
531 if (_defaultSection != null)
532 {
533 store(formatter, _defaultSection);
534 }
535
536 for (Ini.Section s : values())
537 {
538 store(formatter, s);
539 }
540
541 formatter.endIni();
542 }
543
544 @Override protected void store(IniHandler formatter, Section section)
545 {
546 formatter.startSection(section.getName());
547 for (String name : section.keySet())
548 {
549 formatter.handleOption(name, section.get(name));
550 }
551
552 formatter.endSection();
553 }
554 }
555 }