View Javadoc

1   /*
2    * Copyright 2005,2009 Ivan SZKIBA
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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         /** Use serialVersionUID for interoperability. */ 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 }