001 package EVolve.util.settings; 002 003 import java.io.BufferedReader; 004 import java.io.File; 005 import java.io.FileOutputStream; 006 import java.io.FileReader; 007 import java.io.IOException; 008 import java.io.PrintStream; 009 import java.util.HashSet; 010 import java.util.Iterator; 011 import java.util.LinkedHashMap; 012 import java.util.Map; 013 import java.util.Set; 014 015 public class IniFile { 016 private Map sections; 017 private File file; 018 private boolean modified; 019 020 public IniFile(String fileName) throws IOException { 021 this(new File(fileName)); 022 } 023 024 public IniFile(File file) throws IOException { 025 this.sections = new LinkedHashMap(); 026 this.file = file; 027 028 if (file.exists()) { 029 load(); 030 } 031 } 032 033 private static String parseValue(String value) { 034 if (value == null) { 035 return null; 036 } 037 038 value = value.trim(); 039 if (value.length() > 0) { 040 char s[] = value.toCharArray(); 041 if (s[0] == '"') { 042 // Find the next '"' 043 boolean escaped = false; 044 int i; 045 loop: 046 for (i = 1; i < s.length; i++) { 047 if (escaped) { 048 escaped = false; 049 } else { 050 switch (s[i]) { 051 case '\\': 052 escaped = true; 053 break; 054 case '"': 055 break loop; 056 default: 057 break; 058 } 059 } 060 } 061 062 if (s[i] == '"') { 063 if (i < s.length - 1) { 064 String commentStr = value.substring(i+1).trim(); 065 if (!commentStr.startsWith(";")) { 066 throw new RuntimeException("Expression expected to be a comment"); 067 } 068 } 069 value = value.substring(1, i); 070 } 071 } 072 } 073 074 return unescape(value); 075 } 076 077 public static String escape(String value) { 078 if (value == null) { 079 return null; 080 } 081 082 String result = ""; 083 char[] s = value.toCharArray(); 084 for (int i = 0; i < s.length; i++) { 085 char c = s[i]; 086 switch (c) { 087 case '?': 088 result += "\\?"; 089 break; 090 case '\b': 091 result += "\\b"; 092 break; 093 case '\t': 094 result += "\\t"; 095 break; 096 case '\n': 097 result += "\\n"; 098 break; 099 case '\f': 100 result += "\\f"; 101 break; 102 case '\r': 103 result += "\\r"; 104 break; 105 case '"': 106 result += "\\\""; 107 break; 108 case '\'': 109 result += "\\'"; 110 break; 111 case '\\': 112 result += "\\\\"; 113 break; 114 default: 115 result += c; 116 break; 117 } 118 } 119 120 return result; 121 } 122 123 public static String unescape(String value) { 124 if (value == null) { 125 return null; 126 } 127 128 String result = ""; 129 char[] s = value.toCharArray(); 130 for (int i = 0; i < s.length; i++) { 131 char c = s[i]; 132 if (c == '\\') { 133 if (i == (s.length - 1)) { 134 return result + '\\'; 135 } else { 136 c = s[++i]; 137 switch (c) { 138 case '?': 139 result += '?'; 140 break; 141 case 'b': 142 result += '\b'; 143 break; 144 case 't': 145 result += '\t'; 146 break; 147 case 'n': 148 result += '\n'; 149 break; 150 case 'f': 151 result += '\f'; 152 break; 153 case 'r': 154 result += '\r'; 155 case 'x': 156 { 157 i++; 158 String hexString = ""; 159 while (i < s.length) { 160 c = s[i]; 161 if (Character.digit(c, 16) >= 0) { 162 hexString += c; 163 i++; 164 } else { 165 break; 166 } 167 } 168 169 if (hexString.length() <= 0) { 170 i--; 171 result += "\\x"; 172 } else { 173 result += (char) Short.parseShort(hexString, 16); 174 } 175 } 176 break; 177 case '0': 178 case '1': 179 case '2': 180 case '3': 181 case '4': 182 case '5': 183 case '6': 184 case '7': 185 case '8': 186 case '9': 187 { 188 String octalString = ""; 189 while (i < s.length) { 190 c = s[i]; 191 if (Character.digit(c, 8) >= 0) { 192 octalString += c; 193 i++; 194 } else { 195 break; 196 } 197 } 198 199 if (octalString.length() <= 0) { 200 result += "\\"; 201 } else { 202 result += (char) Short.parseShort(octalString, 8); 203 } 204 } 205 break; 206 default: 207 result += '\\' + c; 208 break; 209 } 210 } 211 } else { 212 result += c; 213 } 214 } 215 216 return result; 217 } 218 219 public void load() throws IOException { 220 BufferedReader in = new BufferedReader(new FileReader(file)); 221 String line; 222 String currentSection = null; 223 224 while ((line = in.readLine()) != null) { 225 line = line.trim(); 226 if (line.length() <= 0) { 227 continue; 228 } 229 230 char c = line.charAt(0); 231 232 switch (c) { 233 case ';': 234 continue; 235 case '[': 236 currentSection = line.substring(1, line.length() - 1); 237 put(currentSection); 238 break; 239 default: 240 { 241 int eqPos = line.indexOf('='); 242 if (eqPos < 0) { 243 throw new RuntimeException("Invalid INI file format -- Expected key=value"); 244 } 245 246 String key = line.substring(0, eqPos).trim(); 247 String value = parseValue(line.substring(eqPos + 1)); 248 249 put(currentSection, key, value); 250 } 251 break; 252 } 253 } 254 255 modified = false; 256 257 in.close(); 258 } 259 260 public void write() throws IOException { 261 262 PrintStream out = new PrintStream(new FileOutputStream(file)); 263 264 Iterator sectionIt = sections.keySet().iterator(); 265 while (sectionIt.hasNext()) { 266 Object sectionName = (String) sectionIt.next(); 267 Map keysToValues = (Map) sections.get(sectionName); 268 269 out.println("[" + sectionName + "]"); 270 Iterator keysIt = keysToValues.keySet().iterator(); 271 while (keysIt.hasNext()) { 272 String key = (String) keysIt.next(); 273 String value = (String) keysToValues.get(key); 274 out.println(key + "=\"" + escape(value) + "\""); 275 } 276 out.println(); 277 } 278 } 279 280 public boolean contains(String section) { 281 if (section == null) { 282 return false; 283 } 284 285 return sections.containsKey(section); 286 } 287 288 public boolean contains(String section, String key) { 289 if (section == null || key == null) { 290 return false; 291 } 292 293 if (sections.containsKey(section)) { 294 Map keysToValues = (Map) sections.get(section); 295 return keysToValues.containsKey(key); 296 } 297 298 return false; 299 } 300 301 public boolean put(String section) { 302 if (section == null) { 303 return false; 304 } 305 306 if (!sections.containsKey(section)) { 307 sections.put(section, new LinkedHashMap()); 308 } 309 310 modified = true; 311 return true; 312 } 313 314 public boolean put(String section, String key, String value) { 315 if (section == null || key == null) { 316 return false; 317 } 318 319 if (value == null) { 320 value = ""; 321 } 322 323 Map keysToValues; 324 if (sections.containsKey(section)) { 325 keysToValues = (Map) sections.get(section); 326 } else { 327 keysToValues = new LinkedHashMap(); 328 sections.put(section, keysToValues); 329 } 330 331 keysToValues.put(key, value); 332 333 modified = true; 334 335 return true; 336 } 337 338 public String get(String section, String key) { 339 if (sections.containsKey(section)) { 340 Map keysToValues = (Map) sections.get(section); 341 if (keysToValues.containsKey(key)) { 342 return (String) keysToValues.get(key); 343 } 344 } 345 346 return null; 347 } 348 349 public Set getSections() { 350 return new HashSet(sections.keySet()); 351 } 352 353 public Set getKeys(String section) { 354 if (section == null) { 355 return null; 356 } 357 358 if (sections.containsKey(section)) { 359 Map keysToValues = (Map) sections.get(section); 360 return new HashSet(keysToValues.keySet()); 361 } 362 363 return null; 364 } 365 366 public Set getValues(String section) { 367 if (section == null) { 368 return null; 369 } 370 371 if (sections.containsKey(section)) { 372 Map keysToValues = (Map) sections.get(section); 373 return new HashSet(keysToValues.values()); 374 } 375 376 return null; 377 } 378 379 public void close() throws IOException { 380 if (sections != null) { 381 if (modified) { 382 this.write(); 383 } 384 sections = null; 385 } 386 } 387 388 public void finalize() { 389 try { 390 this.close(); 391 } catch (IOException e) { 392 // not much we can do now... 393 } 394 } 395 } 396