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.addon;
17  
18  import org.ini4j.Config;
19  import org.ini4j.Ini;
20  import org.ini4j.IniHandler;
21  import org.ini4j.InvalidIniFormatException;
22  
23  import org.ini4j.spi.Warnings;
24  
25  import java.io.File;
26  import java.io.FileReader;
27  import java.io.FileWriter;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.io.Reader;
32  import java.io.Writer;
33  
34  import java.net.URL;
35  
36  import java.util.ArrayList;
37  import java.util.Collections;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.regex.Matcher;
42  import java.util.regex.Pattern;
43  
44  public class ConfigParser
45  {
46      private PyIni _ini;
47  
48      @SuppressWarnings(Warnings.UNCHECKED)
49      public ConfigParser()
50      {
51          this(Collections.EMPTY_MAP);
52      }
53  
54      public ConfigParser(Map<String, String> defaults)
55      {
56          _ini = new PyIni(defaults);
57      }
58  
59      public boolean getBoolean(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
60      {
61          boolean ret;
62          String value = get(section, option);
63  
64          if ("1".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value))
65          {
66              ret = true;
67          }
68          else if ("0".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value) || "off".equalsIgnoreCase(value))
69          {
70              ret = false;
71          }
72          else
73          {
74              throw new IllegalArgumentException(value);
75          }
76  
77          return ret;
78      }
79  
80      public double getDouble(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
81      {
82          return Double.parseDouble(get(section, option));
83      }
84  
85      public float getFloat(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
86      {
87          return Float.parseFloat(get(section, option));
88      }
89  
90      public int getInt(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
91      {
92          return Integer.parseInt(get(section, option));
93      }
94  
95      public long getLong(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
96      {
97          return Long.parseLong(get(section, option));
98      }
99  
100     public void addSection(String section) throws DuplicateSectionException
101     {
102         if (_ini.containsKey(section))
103         {
104             throw new DuplicateSectionException(section);
105         }
106         else if (PyIni.DEFAULT_SECTION_NAME.equalsIgnoreCase(section))
107         {
108             throw new IllegalArgumentException(section);
109         }
110 
111         _ini.add(section);
112     }
113 
114     public Map<String, String> defaults()
115     {
116         return _ini.getDefaults();
117     }
118 
119     @SuppressWarnings(Warnings.UNCHECKED)
120     public String get(String section, String option) throws NoSectionException, NoOptionException, InterpolationException
121     {
122         return get(section, option, false, Collections.EMPTY_MAP);
123     }
124 
125     @SuppressWarnings(Warnings.UNCHECKED)
126     public String get(String section, String option, boolean raw) throws NoSectionException, NoOptionException, InterpolationException
127     {
128         return get(section, option, raw, Collections.EMPTY_MAP);
129     }
130 
131     public String get(String sectionName, String optionName, boolean raw, Map<String, String> variables) throws NoSectionException, NoOptionException,
132         InterpolationException
133     {
134         String value = requireOption(sectionName, optionName);
135 
136         if (!raw && (value != null) && (value.indexOf(PyIni.SUBST_CHAR) >= 0))
137         {
138             value = _ini.fetch(sectionName, optionName, variables);
139         }
140 
141         return value;
142     }
143 
144     public boolean hasOption(String sectionName, String optionName)
145     {
146         Ini.Section section = _ini.get(sectionName);
147 
148         return (section != null) && section.containsKey(optionName);
149     }
150 
151     public boolean hasSection(String sectionName)
152     {
153         return _ini.containsKey(sectionName);
154     }
155 
156     @SuppressWarnings(Warnings.UNCHECKED)
157     public List<Map.Entry<String, String>> items(String sectionName) throws NoSectionException, InterpolationMissingOptionException
158     {
159         return items(sectionName, false, Collections.EMPTY_MAP);
160     }
161 
162     @SuppressWarnings(Warnings.UNCHECKED)
163     public List<Map.Entry<String, String>> items(String sectionName, boolean raw) throws NoSectionException, 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 (InvalidIniFormatException 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 (InvalidIniFormatException 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 (InvalidIniFormatException 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 (InvalidIniFormatException 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 PyIni 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(), cause);
410         }
411     }
412 
413     protected static class PyIni extends Ini
414     {
415         private static final char SUBST_CHAR = '%';
416         private static final Pattern EXPRESSION = Pattern.compile("(?<!\\\\)\\%\\(([^\\)]+)\\)");
417         private static final int G_OPTION = 1;
418         protected static final String DEFAULT_SECTION_NAME = "DEFAULT";
419         private final Map<String, String> _defaults;
420         private Ini.Section _defaultSection;
421 
422         public PyIni(Map<String, String> defaults)
423         {
424             _defaults = defaults;
425             Config cfg = getConfig().clone();
426 
427             cfg.setEscape(false);
428             cfg.setMultiOption(false);
429             cfg.setMultiSection(false);
430             cfg.setLowerCaseOption(true);
431             cfg.setLowerCaseSection(true);
432             super.setConfig(cfg);
433         }
434 
435         @Override public void setConfig(Config value)
436         {
437             assert true;
438         }
439 
440         public Map<String, String> getDefaults()
441         {
442             return _defaults;
443         }
444 
445         @Override public Section add(String name)
446         {
447             Section section;
448 
449             if (DEFAULT_SECTION_NAME.equalsIgnoreCase(name))
450             {
451                 if (_defaultSection == null)
452                 {
453                     _defaultSection = new Ini.Section(name);
454                 }
455 
456                 section = _defaultSection;
457             }
458             else
459             {
460                 section = super.add(name);
461             }
462 
463             return section;
464         }
465 
466         public String fetch(String sectionName, String optionName, Map<String, String> variables) throws InterpolationMissingOptionException
467         {
468             return fetch(get(sectionName), optionName, variables);
469         }
470 
471         protected Ini.Section getDefaultSection()
472         {
473             return _defaultSection;
474         }
475 
476         protected String fetch(Ini.Section section, String optionName, Map<String, String> variables) throws InterpolationMissingOptionException
477         {
478             String value = section.get(optionName);
479 
480             if ((value != null) && (value.indexOf(SUBST_CHAR) >= 0))
481             {
482                 StringBuilder buffer = new StringBuilder(value);
483 
484                 resolve(buffer, section, variables);
485                 value = buffer.toString();
486             }
487 
488             return value;
489         }
490 
491         protected void resolve(StringBuilder buffer, Ini.Section owner, Map<String, String> vars) throws InterpolationMissingOptionException
492         {
493             Matcher m = EXPRESSION.matcher(buffer);
494 
495             while (m.find())
496             {
497                 String optionName = m.group(G_OPTION);
498                 String value = owner.get(optionName);
499 
500                 if (value == null)
501                 {
502                     value = vars.get(optionName);
503                 }
504 
505                 if (value == null)
506                 {
507                     value = _defaults.get(optionName);
508                 }
509 
510                 if ((value == null) && (_defaultSection != null))
511                 {
512                     value = _defaultSection.get(optionName);
513                 }
514 
515                 if (value == null)
516                 {
517                     throw new InterpolationMissingOptionException(optionName);
518                 }
519 
520                 buffer.replace(m.start(), m.end(), value);
521                 m.reset(buffer);
522             }
523         }
524 
525         @Override protected void store(IniHandler formatter) throws IOException
526         {
527             formatter.startIni();
528             if (_defaultSection != null)
529             {
530                 store(formatter, _defaultSection);
531             }
532 
533             for (Ini.Section s : values())
534             {
535                 store(formatter, s);
536             }
537 
538             formatter.endIni();
539         }
540 
541         protected void store(IniHandler formatter, Section section) throws IOException
542         {
543             formatter.startSection(section.getName());
544             for (String name : section.keySet())
545             {
546                 formatter.handleOption(name, section.get(name));
547             }
548 
549             formatter.endSection();
550         }
551     }
552 }