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 }