| 1 16 package org.apache.commons.collections; 17 18 import java.io.File ; 19 import java.io.FileInputStream ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.io.InputStreamReader ; 23 import java.io.LineNumberReader ; 24 import java.io.OutputStream ; 25 import java.io.PrintWriter ; 26 import java.io.Reader ; 27 import java.io.UnsupportedEncodingException ; 28 import java.util.ArrayList ; 29 import java.util.Enumeration ; 30 import java.util.Hashtable ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.NoSuchElementException ; 34 import java.util.Properties ; 35 import java.util.StringTokenizer ; 36 import java.util.Vector ; 37 38 141 public class ExtendedProperties extends Hashtable { 142 143 146 private ExtendedProperties defaults; 147 148 154 protected String file; 155 156 160 protected String basePath; 161 162 165 protected String fileSeparator = System.getProperty("file.separator"); 166 167 170 protected boolean isInitialized = false; 171 172 176 protected static String include = "include"; 177 178 184 protected ArrayList keysAsListed = new ArrayList (); 185 186 protected final static String START_TOKEN="${"; 187 protected final static String END_TOKEN="}"; 188 189 190 196 protected String interpolate(String base) { 197 return (interpolateHelper(base, null)); 199 } 200 201 215 protected String interpolateHelper(String base, List priorVariables) { 216 if (base == null) { 218 return null; 219 } 220 221 if (priorVariables == null) { 224 priorVariables = new ArrayList (); 225 priorVariables.add(base); 226 } 227 228 int begin = -1; 229 int end = -1; 230 int prec = 0 - END_TOKEN.length(); 231 String variable = null; 232 StringBuffer result = new StringBuffer (); 233 234 while (((begin = base.indexOf(START_TOKEN, prec + END_TOKEN.length())) > -1) 236 && ((end = base.indexOf(END_TOKEN, begin)) > -1)) { 237 result.append(base.substring(prec + END_TOKEN.length(), begin)); 238 variable = base.substring(begin + START_TOKEN.length(), end); 239 240 if (priorVariables.contains(variable)) { 242 String initialBase = priorVariables.remove(0).toString(); 243 priorVariables.add(variable); 244 StringBuffer priorVariableSb = new StringBuffer (); 245 246 for (Iterator it = priorVariables.iterator(); it.hasNext();) { 249 priorVariableSb.append(it.next()); 250 if (it.hasNext()) { 251 priorVariableSb.append("->"); 252 } 253 } 254 255 throw new IllegalStateException ( 256 "infinite loop in property interpolation of " + initialBase + ": " + priorVariableSb.toString()); 257 } 258 else { 260 priorVariables.add(variable); 261 } 262 263 Object value = getProperty(variable); 265 if (value != null) { 266 result.append(interpolateHelper(value.toString(), priorVariables)); 267 268 priorVariables.remove(priorVariables.size() - 1); 273 } else if (defaults != null && defaults.getString(variable, null) != null) { 274 result.append(defaults.getString(variable)); 275 } else { 276 result.append(START_TOKEN).append(variable).append(END_TOKEN); 278 } 279 prec = end; 280 } 281 result.append(base.substring(prec + END_TOKEN.length(), base.length())); 282 283 return result.toString(); 284 } 285 286 289 private static String escape(String s) { 290 StringBuffer buf = new StringBuffer (s); 291 for (int i = 0; i < buf.length(); i++) { 292 char c = buf.charAt(i); 293 if (c == ',' || c == '\\') { 294 buf.insert(i, '\\'); 295 i++; 296 } 297 } 298 return buf.toString(); 299 } 300 301 304 private static String unescape(String s) { 305 StringBuffer buf = new StringBuffer (s); 306 for (int i = 0; i < buf.length() - 1; i++) { 307 char c1 = buf.charAt(i); 308 char c2 = buf.charAt(i + 1); 309 if (c1 == '\\' && c2 == '\\') { 310 buf.deleteCharAt(i); 311 } 312 } 313 return buf.toString(); 314 } 315 316 320 private static int countPreceding(String line, int index, char ch) { 321 int i; 322 for (i = index - 1; i >= 0; i--) { 323 if (line.charAt(i) != ch) { 324 break; 325 } 326 } 327 return index - 1 - i; 328 } 329 330 333 private static boolean endsWithSlash(String line) { 334 if (!line.endsWith("\\")) { 335 return false; 336 } 337 return (countPreceding(line, line.length() - 1, '\\') % 2 == 0); 338 } 339 340 346 static class PropertiesReader extends LineNumberReader { 347 352 public PropertiesReader(Reader reader) { 353 super(reader); 354 } 355 356 362 public String readProperty() throws IOException { 363 StringBuffer buffer = new StringBuffer (); 364 365 try { 366 while (true) { 367 String line = readLine().trim(); 368 if ((line.length() != 0) && (line.charAt(0) != '#')) { 369 if (endsWithSlash(line)) { 370 line = line.substring(0, line.length() - 1); 371 buffer.append(line); 372 } else { 373 buffer.append(line); 374 break; 375 } 376 } 377 } 378 } catch (NullPointerException ex) { 379 return null; 380 } 381 382 return buffer.toString(); 383 } 384 } 385 386 391 static class PropertiesTokenizer extends StringTokenizer { 392 395 static final String DELIMITER = ","; 396 397 402 public PropertiesTokenizer(String string) { 403 super(string, DELIMITER); 404 } 405 406 411 public boolean hasMoreTokens() { 412 return super.hasMoreTokens(); 413 } 414 415 420 public String nextToken() { 421 StringBuffer buffer = new StringBuffer (); 422 423 while (hasMoreTokens()) { 424 String token = super.nextToken(); 425 if (endsWithSlash(token)) { 426 buffer.append(token.substring(0, token.length() - 1)); 427 buffer.append(DELIMITER); 428 } else { 429 buffer.append(token); 430 break; 431 } 432 } 433 434 return buffer.toString().trim(); 435 } 436 } 437 438 441 public ExtendedProperties() { 442 super(); 443 } 444 445 451 public ExtendedProperties(String file) throws IOException { 452 this(file, null); 453 } 454 455 462 public ExtendedProperties(String file, String defaultFile) throws IOException { 463 this.file = file; 464 465 basePath = new File (file).getAbsolutePath(); 466 basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1); 467 468 FileInputStream in = null; 469 try { 470 in = new FileInputStream (file); 471 this.load(in); 472 } finally { 473 try { 474 if (in != null) { 475 in.close(); 476 } 477 } catch (IOException ex) {} 478 } 479 480 if (defaultFile != null) { 481 defaults = new ExtendedProperties(defaultFile); 482 } 483 } 484 485 489 public boolean isInitialized() { 490 return isInitialized; 491 } 492 493 499 public String getInclude() { 500 return include; 501 } 502 503 509 public void setInclude(String inc) { 510 include = inc; 511 } 512 513 519 public void load(InputStream input) throws IOException { 520 load(input, null); 521 } 522 523 531 public synchronized void load(InputStream input, String enc) throws IOException { 532 PropertiesReader reader = null; 533 if (enc != null) { 534 try { 535 reader = new PropertiesReader(new InputStreamReader (input, enc)); 536 537 } catch (UnsupportedEncodingException ex) { 538 } 540 } 541 542 if (reader == null) { 543 try { 544 reader = new PropertiesReader(new InputStreamReader (input, "8859_1")); 545 546 } catch (UnsupportedEncodingException ex) { 547 reader = new PropertiesReader(new InputStreamReader (input)); 550 } 551 } 552 553 try { 554 while (true) { 555 String line = reader.readProperty(); 556 int equalSign = line.indexOf('='); 557 558 if (equalSign > 0) { 559 String key = line.substring(0, equalSign).trim(); 560 String value = line.substring(equalSign + 1).trim(); 561 562 if ("".equals(value)) { 564 continue; 565 } 566 567 if (getInclude() != null && key.equalsIgnoreCase(getInclude())) { 568 File file = null; 570 571 if (value.startsWith(fileSeparator)) { 572 file = new File (value); 574 575 } else { 576 if (value.startsWith("." + fileSeparator)) { 580 value = value.substring(2); 581 } 582 583 file = new File (basePath + value); 584 } 585 586 if (file != null && file.exists() && file.canRead()) { 587 load(new FileInputStream (file)); 588 } 589 } else { 590 addProperty(key, value); 591 } 592 } 593 } 594 } catch (NullPointerException ex) { 595 return; 597 } finally { 598 isInitialized = true; 600 } 601 } 602 603 610 public Object getProperty(String key) { 611 Object obj = this.get(key); 613 614 if (obj == null) { 615 if (defaults != null) { 618 obj = defaults.get(key); 619 } 620 } 621 622 return obj; 623 } 624 625 644 public void addProperty(String key, Object value) { 645 if (value instanceof String ) { 646 String str = (String ) value; 647 if (str.indexOf(PropertiesTokenizer.DELIMITER) > 0) { 648 PropertiesTokenizer tokenizer = new PropertiesTokenizer(str); 650 while (tokenizer.hasMoreTokens()) { 651 String token = tokenizer.nextToken(); 652 addPropertyInternal(key, unescape(token)); 653 } 654 } else { 655 addPropertyInternal(key, unescape(str)); 657 } 658 } else { 659 addPropertyInternal(key, value); 660 } 661 662 isInitialized = true; 664 } 665 666 673 private void addPropertyDirect(String key, Object value) { 674 if (!containsKey(key)) { 676 keysAsListed.add(key); 677 } 678 put(key, value); 679 } 680 681 692 private void addPropertyInternal(String key, Object value) { 693 Object current = this.get(key); 694 695 if (current instanceof String ) { 696 Vector v = new Vector (2); 698 v.addElement(current); 699 v.addElement(value); 700 put(key, v); 701 702 } else if (current instanceof Vector ) { 703 ((Vector ) current).addElement(value); 705 706 } else { 707 if (!containsKey(key)) { 709 keysAsListed.add(key); 710 } 711 put(key, value); 712 } 713 } 714 715 723 public void setProperty(String key, Object value) { 724 clearProperty(key); 725 addProperty(key, value); 726 } 727 728 737 public synchronized void save(OutputStream output, String header) throws IOException { 738 if (output == null) { 739 return; 740 } 741 PrintWriter theWrtr = new PrintWriter (output); 742 if (header != null) { 743 theWrtr.println(header); 744 } 745 746 Enumeration theKeys = keys(); 747 while (theKeys.hasMoreElements()) { 748 String key = (String ) theKeys.nextElement(); 749 Object value = get(key); 750 if (value != null) { 751 if (value instanceof String ) { 752 StringBuffer currentOutput = new StringBuffer (); 753 currentOutput.append(key); 754 currentOutput.append("="); 755 currentOutput.append(escape((String ) value)); 756 theWrtr.println(currentOutput.toString()); 757 758 } else if (value instanceof Vector ) { 759 Vector values = (Vector ) value; 760 Enumeration valuesEnum = values.elements(); 761 while (valuesEnum.hasMoreElements()) { 762 String currentElement = (String ) valuesEnum.nextElement(); 763 StringBuffer currentOutput = new StringBuffer (); 764 currentOutput.append(key); 765 currentOutput.append("="); 766 currentOutput.append(escape(currentElement)); 767 theWrtr.println(currentOutput.toString()); 768 } 769 } 770 } 771 theWrtr.println(); 772 theWrtr.flush(); 773 } 774 } 775 776 783 public void combine(ExtendedProperties props) { 784 for (Iterator it = props.getKeys(); it.hasNext();) { 785 String key = (String ) it.next(); 786 setProperty(key, props.get(key)); 787 } 788 } 789 790 795 public void clearProperty(String key) { 796 if (containsKey(key)) { 797 for (int i = 0; i < keysAsListed.size(); i++) { 800 if (( keysAsListed.get(i)).equals(key)) { 801 keysAsListed.remove(i); 802 break; 803 } 804 } 805 remove(key); 806 } 807 } 808 809 815 public Iterator getKeys() { 816 return keysAsListed.iterator(); 817 } 818 819 826 public |