001 /*
002 * Copyright 2005 [ini4j] Development Team
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.ini4j;
017
018 import java.io.*;
019 import java.net.URL;
020 import java.util.*;
021 import java.lang.reflect.*;
022
023 import org.xml.sax.*;
024 import org.xml.sax.helpers.*;
025 import javax.xml.parsers.*;
026
027 public class Ini extends LinkedHashMap<String,Ini.Section>
028 {
029 private static final String OPERATOR = " " + IniParser.OPERATOR + " ";
030 private static final char SUBST_CHAR = '$';
031 private static final String SUBST_BEGIN = SUBST_CHAR + "{";
032 private static final int SUBST_BEGIN_LEN = SUBST_BEGIN.length();
033 private static final String SUBST_END = "}";
034 private static final int SUBST_END_LEN = SUBST_END.length();
035 private static final char SUBST_ESCAPE = '\\';
036 private static final char SUBST_SEPARATOR = '/';
037 private static final String SUBST_PROPERTY = "@prop";
038 private static final String SUBST_ENVIRONMENT = "@env";
039
040 private Map<Class,Object> _beans;
041
042 public class Section extends LinkedHashMap<String,String>
043 {
044 private String _name;
045 private Map<Class,Object> _beans;
046
047 class BeanInvocationHandler extends AbstractBeanInvocationHandler
048 {
049 protected Object getPropertySpi(String property, Class<?> clazz)
050 {
051 return fetch(property);
052 }
053
054 protected void setPropertySpi(String property, Object value, Class<?> clazz)
055 {
056 put(property, value.toString());
057 }
058
059 protected boolean hasPropertySpi(String property)
060 {
061 return containsKey(property);
062 }
063 }
064
065 public Section(String name)
066 {
067 super();
068 _name = name;
069 }
070
071 public String getName()
072 {
073 return _name;
074 }
075
076 public synchronized <T> T to(Class<T> clazz)
077 {
078 Object bean;
079
080 if ( _beans == null )
081 {
082 _beans = new HashMap<Class,Object>();
083 bean = null;
084 }
085 else
086 {
087 bean = _beans.get(clazz);
088 }
089
090 if ( bean == null )
091 {
092 bean = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {clazz}, new BeanInvocationHandler());
093 _beans.put(clazz, bean);
094 }
095
096 return clazz.cast(bean);
097 }
098
099 public String fetch(Object key)
100 {
101 String value = get(key);
102
103 if ( (value != null) && (value.indexOf(SUBST_CHAR) >= 0) )
104 {
105 StringBuilder buffer = new StringBuilder(value);
106 resolve(buffer, this);
107 value = buffer.toString();
108 }
109 return value;
110 }
111 }
112
113 class BeanInvocationHandler extends AbstractBeanInvocationHandler
114 {
115 private Map<String,Object> _sectionBeans = new HashMap<String, Object>();
116
117 protected Object getPropertySpi(String property, Class<?> clazz)
118 {
119 Object o = _sectionBeans.get(property);
120
121 if ( o == null )
122 {
123 Section section = get(property);
124
125 if ( section != null )
126 {
127 o = section.to(clazz);
128 _sectionBeans.put(property, o);
129 }
130 }
131
132 return o;
133 }
134
135 protected void setPropertySpi(String property, Object value, Class<?> clazz)
136 {
137 throw new UnsupportedOperationException("read only bean");
138 }
139
140 protected boolean hasPropertySpi(String property)
141 {
142 return false;
143 }
144 }
145
146 class Builder implements IniHandler
147 {
148 private Section currentSection;
149
150 public void startIni()
151 {
152 ;
153 }
154
155 public void endIni()
156 {
157 ;
158 }
159
160 public void startSection(String sectionName)
161 {
162 Section s = get(sectionName);
163 currentSection = (s != null) ? s : add(sectionName);
164 }
165
166 public void endSection()
167 {
168 currentSection = null;
169 }
170
171 public void handleOption(String name, String value)
172 {
173 currentSection.put(name, value);
174 }
175 }
176
177 public Ini()
178 {
179 ;
180 }
181
182 public Ini(Reader input) throws IOException, InvalidIniFormatException
183 {
184 this();
185 load(input);
186 }
187
188 public Ini(InputStream input) throws IOException, InvalidIniFormatException
189 {
190 this();
191 load(input);
192 }
193
194 public Ini(URL input) throws IOException, InvalidIniFormatException
195 {
196 this();
197 load(input);
198 }
199
200 public Section add(String name)
201 {
202 Section s = new Section(name);
203 put(name, s);
204 return s;
205 }
206
207 public Section remove(Section section)
208 {
209 return remove((Object)section.getName() );
210 }
211
212 public void store(OutputStream output) throws IOException
213 {
214 store(new OutputStreamWriter(output));
215 }
216
217 public void store(Writer output) throws IOException
218 {
219 PrintWriter pr = new PrintWriter(output);
220
221 for(Ini.Section s : values())
222 {
223 pr.print(IniParser.SECTION_BEGIN);
224 pr.print(Convert.escape(s.getName()));
225 pr.println(IniParser.SECTION_END);
226
227 for(Map.Entry<String,String> e : s.entrySet())
228 {
229 pr.print(Convert.escape(e.getKey()));
230 pr.print(OPERATOR);
231 pr.println(Convert.escape(e.getValue()));
232 }
233
234 pr.println();
235 }
236 pr.flush();
237 }
238
239 public void load(InputStream input) throws IOException, InvalidIniFormatException
240 {
241 load(new InputStreamReader(input));
242 }
243
244 public void load(Reader input) throws IOException, InvalidIniFormatException
245 {
246 Builder builder = new Builder();
247 IniParser.newInstance().parse(input, builder);
248 }
249
250 public void load(URL input) throws IOException, InvalidIniFormatException
251 {
252 Builder builder = new Builder();
253 IniParser.newInstance().parse(input, builder);
254 }
255
256 public void storeToXML(OutputStream output) throws IOException
257 {
258 storeToXML(new OutputStreamWriter(output));
259 }
260
261 public void storeToXML(Writer output) throws IOException
262 {
263 PrintWriter pr = new PrintWriter(output);
264
265 pr.println("<ini version='1.0'>");
266
267 for(Ini.Section s : values())
268 {
269 pr.print(" <section key='");
270 pr.print(s.getName());
271 pr.println("'>");
272
273 for(Map.Entry<String,String> e : s.entrySet())
274 {
275 pr.print(" <option key='");
276 pr.print(e.getKey());
277 pr.print("' value='");
278 pr.print(e.getValue());
279 pr.println("'/>");
280 }
281
282 pr.println(" </section>");
283 }
284
285 pr.println("</ini>");
286 pr.flush();
287 }
288
289 public void loadFromXML(InputStream input) throws IOException, InvalidIniFormatException
290 {
291 loadFromXML(new InputStreamReader(input));
292 }
293
294 public void loadFromXML(Reader input) throws IOException, InvalidIniFormatException
295 {
296 Builder builder = new Builder();
297 IniParser.newInstance().parseXML(input, builder);
298 }
299
300 public void loadFromXML(URL input) throws IOException, InvalidIniFormatException
301 {
302 Builder builder = new Builder();
303 IniParser.newInstance().parseXML(input, builder);
304 }
305
306 public <T> T to(Class<T> clazz)
307 {
308 Object bean;
309
310 if ( _beans == null )
311 {
312 _beans = new HashMap<Class,Object>();
313 bean = null;
314 }
315 else
316 {
317 bean = _beans.get(clazz);
318 }
319
320 if ( bean == null )
321 {
322 bean = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {clazz}, new BeanInvocationHandler());
323 _beans.put(clazz, bean);
324 }
325
326 return clazz.cast(bean);
327 }
328
329 protected void resolve(StringBuilder buffer, Section owner)
330 {
331 int begin = -1;
332 int end = -1;
333
334 for(int i = buffer.indexOf(SUBST_BEGIN); (i>=0); i = buffer.indexOf(SUBST_BEGIN, i+1) )
335 {
336 if ( (i+2) > buffer.length() )
337 {
338 break;
339 }
340
341 if ( (i != 0) && (buffer.charAt(i-1) == SUBST_ESCAPE) )
342 {
343 continue;
344 }
345
346 begin = i;
347
348 end = buffer.indexOf(SUBST_END, i);
349
350 if ( end < 0 )
351 {
352 break;
353 }
354
355 if ( (begin >= 0) && (end > 0) )
356 {
357 String var = buffer.substring(begin+SUBST_BEGIN_LEN,end);
358 String group = null;
359 int sep = var.indexOf(SUBST_SEPARATOR);
360 String value = null;
361
362 if ( sep > 0 )
363 {
364 group = var.substring(0,sep);
365 var = var.substring(sep+1);
366 }
367
368 if ( var != null )
369 {
370 if ( group == null )
371 {
372 value = owner.fetch(var);
373 }
374 else if ( SUBST_ENVIRONMENT.equals(group))
375 {
376 value = System.getenv(var);
377 }
378 else if ( SUBST_PROPERTY.equals(group) )
379 {
380 value = System.getProperty(var);
381 }
382 else
383 {
384 owner = get(group);
385
386 if ( owner != null )
387 {
388 value = owner.fetch(var);
389 }
390 }
391 }
392
393 if ( value != null )
394 {
395 buffer.replace(begin,end+SUBST_END_LEN, value);
396 }
397 }
398 }
399 }
400
401 }