1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| |
16 |
| package org.ini4j; |
17 |
| |
18 |
| import java.io.*; |
19 |
| import java.net.URL; |
20 |
| import java.util.*; |
21 |
| import java.lang.reflect.*; |
22 |
| |
23 |
| import org.xml.sax.*; |
24 |
| import org.xml.sax.helpers.*; |
25 |
| import javax.xml.parsers.*; |
26 |
| |
27 |
| public class Ini extends LinkedHashMap<String,Ini.Section> |
28 |
| { |
29 |
| private static final String OPERATOR = " " + IniParser.OPERATOR + " "; |
30 |
| private static final char SUBST_CHAR = '$'; |
31 |
| private static final String SUBST_BEGIN = SUBST_CHAR + "{"; |
32 |
| private static final int SUBST_BEGIN_LEN = SUBST_BEGIN.length(); |
33 |
| private static final String SUBST_END = "}"; |
34 |
| private static final int SUBST_END_LEN = SUBST_END.length(); |
35 |
| private static final char SUBST_ESCAPE = '\\'; |
36 |
| private static final char SUBST_SEPARATOR = '/'; |
37 |
| private static final String SUBST_PROPERTY = "@prop"; |
38 |
| private static final String SUBST_ENVIRONMENT = "@env"; |
39 |
| |
40 |
| private Map<Class,Object> _beans; |
41 |
| |
42 |
| public class Section extends LinkedHashMap<String,String> |
43 |
| { |
44 |
| private String _name; |
45 |
| private Map<Class,Object> _beans; |
46 |
| |
47 |
| class BeanInvocationHandler extends AbstractBeanInvocationHandler |
48 |
| { |
49 |
401
| protected Object getPropertySpi(String property, Class<?> clazz)
|
50 |
| { |
51 |
401
| return fetch(property);
|
52 |
| } |
53 |
| |
54 |
1
| protected void setPropertySpi(String property, Object value, Class<?> clazz)
|
55 |
| { |
56 |
1
| put(property, value.toString());
|
57 |
| } |
58 |
| |
59 |
336
| protected boolean hasPropertySpi(String property)
|
60 |
| { |
61 |
336
| return containsKey(property);
|
62 |
| } |
63 |
| } |
64 |
| |
65 |
165
| public Section(String name)
|
66 |
| { |
67 |
165
| super();
|
68 |
165
| _name = name;
|
69 |
| } |
70 |
| |
71 |
47
| public String getName()
|
72 |
| { |
73 |
47
| return _name;
|
74 |
| } |
75 |
| |
76 |
85
| public synchronized <T> T to(Class<T> clazz)
|
77 |
| { |
78 |
85
| Object bean;
|
79 |
| |
80 |
85
| if ( _beans == null )
|
81 |
| { |
82 |
80
| _beans = new HashMap<Class,Object>();
|
83 |
80
| bean = null;
|
84 |
| } |
85 |
| else |
86 |
| { |
87 |
5
| bean = _beans.get(clazz);
|
88 |
| } |
89 |
| |
90 |
85
| if ( bean == null )
|
91 |
| { |
92 |
81
| bean = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {clazz}, new BeanInvocationHandler());
|
93 |
81
| _beans.put(clazz, bean);
|
94 |
| } |
95 |
| |
96 |
85
| return clazz.cast(bean);
|
97 |
| } |
98 |
| |
99 |
538
| public String fetch(Object key)
|
100 |
| { |
101 |
538
| String value = get(key);
|
102 |
| |
103 |
538
| if ( (value != null) && (value.indexOf(SUBST_CHAR) >= 0) )
|
104 |
| { |
105 |
84
| StringBuilder buffer = new StringBuilder(value);
|
106 |
84
| resolve(buffer, this);
|
107 |
84
| value = buffer.toString();
|
108 |
| } |
109 |
538
| return value;
|
110 |
| } |
111 |
| } |
112 |
| |
113 |
| class BeanInvocationHandler extends AbstractBeanInvocationHandler |
114 |
| { |
115 |
| private Map<String,Object> _sectionBeans = new HashMap<String, Object>(); |
116 |
| |
117 |
136
| protected Object getPropertySpi(String property, Class<?> clazz)
|
118 |
| { |
119 |
136
| Object o = _sectionBeans.get(property);
|
120 |
| |
121 |
136
| if ( o == null )
|
122 |
| { |
123 |
80
| Section section = get(property);
|
124 |
| |
125 |
80
| if ( section != null )
|
126 |
| { |
127 |
79
| o = section.to(clazz);
|
128 |
79
| _sectionBeans.put(property, o);
|
129 |
| } |
130 |
| } |
131 |
| |
132 |
136
| return o;
|
133 |
| } |
134 |
| |
135 |
1
| protected void setPropertySpi(String property, Object value, Class<?> clazz)
|
136 |
| { |
137 |
1
| throw new UnsupportedOperationException("read only bean");
|
138 |
| } |
139 |
| |
140 |
1
| protected boolean hasPropertySpi(String property)
|
141 |
| { |
142 |
1
| return false;
|
143 |
| } |
144 |
| } |
145 |
| |
146 |
| class Builder implements IniHandler |
147 |
| { |
148 |
| private Section currentSection; |
149 |
| |
150 |
25
| public void startIni()
|
151 |
| { |
152 |
| ; |
153 |
| } |
154 |
| |
155 |
24
| public void endIni()
|
156 |
| { |
157 |
| ; |
158 |
| } |
159 |
| |
160 |
182
| public void startSection(String sectionName)
|
161 |
| { |
162 |
182
| Section s = get(sectionName);
|
163 |
182
| currentSection = (s != null) ? s : add(sectionName);
|
164 |
| } |
165 |
| |
166 |
182
| public void endSection()
|
167 |
| { |
168 |
182
| currentSection = null;
|
169 |
| } |
170 |
| |
171 |
671
| public void handleOption(String name, String value)
|
172 |
| { |
173 |
671
| currentSection.put(name, value);
|
174 |
| } |
175 |
| } |
176 |
| |
177 |
34
| public Ini()
|
178 |
| { |
179 |
| ; |
180 |
| } |
181 |
| |
182 |
2
| public Ini(Reader input) throws IOException, InvalidIniFormatException
|
183 |
| { |
184 |
2
| this();
|
185 |
2
| load(input);
|
186 |
| } |
187 |
| |
188 |
12
| public Ini(InputStream input) throws IOException, InvalidIniFormatException
|
189 |
| { |
190 |
12
| this();
|
191 |
12
| load(input);
|
192 |
| } |
193 |
| |
194 |
4
| public Ini(URL input) throws IOException, InvalidIniFormatException
|
195 |
| { |
196 |
4
| this();
|
197 |
4
| load(input);
|
198 |
| } |
199 |
| |
200 |
165
| public Section add(String name)
|
201 |
| { |
202 |
165
| Section s = new Section(name);
|
203 |
165
| put(name, s);
|
204 |
165
| return s;
|
205 |
| } |
206 |
| |
207 |
3
| public Section remove(Section section)
|
208 |
| { |
209 |
3
| return remove((Object)section.getName() );
|
210 |
| } |
211 |
| |
212 |
1
| public void store(OutputStream output) throws IOException
|
213 |
| { |
214 |
1
| store(new OutputStreamWriter(output));
|
215 |
| } |
216 |
| |
217 |
3
| public void store(Writer output) throws IOException
|
218 |
| { |
219 |
3
| PrintWriter pr = new PrintWriter(output);
|
220 |
| |
221 |
3
| for(Ini.Section s : values())
|
222 |
| { |
223 |
15
| pr.print(IniParser.SECTION_BEGIN);
|
224 |
15
| pr.print(Convert.escape(s.getName()));
|
225 |
15
| pr.println(IniParser.SECTION_END);
|
226 |
| |
227 |
15
| for(Map.Entry<String,String> e : s.entrySet())
|
228 |
| { |
229 |
57
| pr.print(Convert.escape(e.getKey()));
|
230 |
57
| pr.print(OPERATOR);
|
231 |
57
| pr.println(Convert.escape(e.getValue()));
|
232 |
| } |
233 |
| |
234 |
15
| pr.println();
|
235 |
| } |
236 |
3
| pr.flush();
|
237 |
| } |
238 |
| |
239 |
14
| public void load(InputStream input) throws IOException, InvalidIniFormatException
|
240 |
| { |
241 |
14
| load(new InputStreamReader(input));
|
242 |
| } |
243 |
| |
244 |
17
| public void load(Reader input) throws IOException, InvalidIniFormatException
|
245 |
| { |
246 |
17
| Builder builder = new Builder();
|
247 |
17
| IniParser.newInstance().parse(input, builder);
|
248 |
| } |
249 |
| |
250 |
9
| public void load(URL input) throws IOException, InvalidIniFormatException
|
251 |
| { |
252 |
9
| Builder builder = new Builder();
|
253 |
9
| IniParser.newInstance().parse(input, builder);
|
254 |
| } |
255 |
| |
256 |
1
| public void storeToXML(OutputStream output) throws IOException
|
257 |
| { |
258 |
1
| storeToXML(new OutputStreamWriter(output));
|
259 |
| } |
260 |
| |
261 |
2
| public void storeToXML(Writer output) throws IOException
|
262 |
| { |
263 |
2
| PrintWriter pr = new PrintWriter(output);
|
264 |
| |
265 |
2
| pr.println("<ini version='1.0'>");
|
266 |
| |
267 |
2
| for(Ini.Section s : values())
|
268 |
| { |
269 |
14
| pr.print(" <section key='");
|
270 |
14
| pr.print(s.getName());
|
271 |
14
| pr.println("'>");
|
272 |
| |
273 |
14
| for(Map.Entry<String,String> e : s.entrySet())
|
274 |
| { |
275 |
56
| pr.print(" <option key='");
|
276 |
56
| pr.print(e.getKey());
|
277 |
56
| pr.print("' value='");
|
278 |
56
| pr.print(e.getValue());
|
279 |
56
| pr.println("'/>");
|
280 |
| } |
281 |
| |
282 |
14
| pr.println(" </section>");
|
283 |
| } |
284 |
| |
285 |
2
| pr.println("</ini>");
|
286 |
2
| pr.flush();
|
287 |
| } |
288 |
| |
289 |
2
| public void loadFromXML(InputStream input) throws IOException, InvalidIniFormatException
|
290 |
| { |
291 |
2
| loadFromXML(new InputStreamReader(input));
|
292 |
| } |
293 |
| |
294 |
3
| public void loadFromXML(Reader input) throws IOException, InvalidIniFormatException
|
295 |
| { |
296 |
3
| Builder builder = new Builder();
|
297 |
3
| IniParser.newInstance().parseXML(input, builder);
|
298 |
| } |
299 |
| |
300 |
1
| public void loadFromXML(URL input) throws IOException, InvalidIniFormatException
|
301 |
| { |
302 |
1
| Builder builder = new Builder();
|
303 |
1
| IniParser.newInstance().parseXML(input, builder);
|
304 |
| } |
305 |
| |
306 |
16
| public <T> T to(Class<T> clazz)
|
307 |
| { |
308 |
16
| Object bean;
|
309 |
| |
310 |
16
| if ( _beans == null )
|
311 |
| { |
312 |
13
| _beans = new HashMap<Class,Object>();
|
313 |
13
| bean = null;
|
314 |
| } |
315 |
| else |
316 |
| { |
317 |
3
| bean = _beans.get(clazz);
|
318 |
| } |
319 |
| |
320 |
16
| if ( bean == null )
|
321 |
| { |
322 |
14
| bean = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {clazz}, new BeanInvocationHandler());
|
323 |
14
| _beans.put(clazz, bean);
|
324 |
| } |
325 |
| |
326 |
16
| return clazz.cast(bean);
|
327 |
| } |
328 |
| |
329 |
95
| protected void resolve(StringBuilder buffer, Section owner)
|
330 |
| { |
331 |
95
| int begin = -1;
|
332 |
95
| int end = -1;
|
333 |
| |
334 |
95
| for(int i = buffer.indexOf(SUBST_BEGIN); (i>=0); i = buffer.indexOf(SUBST_BEGIN, i+1) )
|
335 |
| { |
336 |
95
| if ( (i+2) > buffer.length() )
|
337 |
| { |
338 |
0
| break;
|
339 |
| } |
340 |
| |
341 |
95
| if ( (i != 0) && (buffer.charAt(i-1) == SUBST_ESCAPE) )
|
342 |
| { |
343 |
0
| continue;
|
344 |
| } |
345 |
| |
346 |
95
| begin = i;
|
347 |
| |
348 |
95
| end = buffer.indexOf(SUBST_END, i);
|
349 |
| |
350 |
95
| if ( end < 0 )
|
351 |
| { |
352 |
2
| break;
|
353 |
| } |
354 |
| |
355 |
93
| if ( (begin >= 0) && (end > 0) )
|
356 |
| { |
357 |
93
| String var = buffer.substring(begin+SUBST_BEGIN_LEN,end);
|
358 |
93
| String group = null;
|
359 |
93
| int sep = var.indexOf(SUBST_SEPARATOR);
|
360 |
93
| String value = null;
|
361 |
| |
362 |
93
| if ( sep > 0 )
|
363 |
| { |
364 |
90
| group = var.substring(0,sep);
|
365 |
90
| var = var.substring(sep+1);
|
366 |
| } |
367 |
| |
368 |
93
| if ( var != null )
|
369 |
| { |
370 |
93
| if ( group == null )
|
371 |
| { |
372 |
3
| value = owner.fetch(var);
|
373 |
| } |
374 |
90
| else if ( SUBST_ENVIRONMENT.equals(group))
|
375 |
| { |
376 |
1
| value = System.getenv(var);
|
377 |
| } |
378 |
89
| else if ( SUBST_PROPERTY.equals(group) )
|
379 |
| { |
380 |
1
| value = System.getProperty(var);
|
381 |
| } |
382 |
| else |
383 |
| { |
384 |
88
| owner = get(group);
|
385 |
| |
386 |
88
| if ( owner != null )
|
387 |
| { |
388 |
87
| value = owner.fetch(var);
|
389 |
| } |
390 |
| } |
391 |
| } |
392 |
| |
393 |
93
| if ( value != null )
|
394 |
| { |
395 |
89
| buffer.replace(begin,end+SUBST_END_LEN, value);
|
396 |
| } |
397 |
| } |
398 |
| } |
399 |
| } |
400 |
| |
401 |
| } |