1 19 20 package org.netbeans.modules.tomcat5.util; 21 22 import java.io.BufferedReader ; 23 import java.io.BufferedWriter ; 24 import java.io.IOException ; 25 import java.io.InputStream ; 26 import java.io.InputStreamReader ; 27 import java.io.OutputStream ; 28 import java.io.OutputStreamWriter ; 29 import java.util.AbstractMap ; 30 import java.util.AbstractSet ; 31 import java.util.ArrayList ; 32 import java.util.Arrays ; 33 import java.util.HashMap ; 34 import java.util.Iterator ; 35 import java.util.LinkedList ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.NoSuchElementException ; 39 import java.util.Set ; 40 41 43 59 public final class EditableProperties extends AbstractMap implements Cloneable { 60 61 63 private List items; 64 65 66 private Map itemIndex; 67 68 private boolean alphabetize = true; 69 70 private static final String keyValueSeparators = "=: \t\r\n\f"; 71 72 private static final String strictKeyValueSeparators = "=:"; 73 74 private static final String whiteSpaceChars = " \t\r\n\f"; 75 76 private static final String commentChars = "#!"; 77 78 private static final String INDENT = " "; 79 80 private static final int WAITING_FOR_KEY_VALUE = 1; 82 private static final int READING_KEY_VALUE = 2; 83 84 87 public EditableProperties() { 88 items = new ArrayList (); 89 itemIndex = new HashMap (); 90 } 91 92 96 public EditableProperties(boolean alphabetize) { 97 this(); 98 this.alphabetize = alphabetize; 99 } 100 101 106 public EditableProperties(Map map) { 107 this(); 108 putAll(map); 109 } 110 111 115 EditableProperties(EditableProperties ep) { 116 this(); 117 Iterator it = ep.items.iterator(); 118 while (it.hasNext()) { 119 Item item = (Item)it.next(); 120 addItem((Item)item.clone(), false); 121 } 122 } 123 124 130 public Set entrySet() { 131 return new SetImpl(this); 132 } 133 134 139 public void load(InputStream stream) throws IOException { 140 int state = WAITING_FOR_KEY_VALUE; 141 BufferedReader input = new BufferedReader (new InputStreamReader (stream, "ISO-8859-1")); 142 LinkedList tempList = new LinkedList (); 143 String line; 144 int commentLinesCount = 0; 145 while (null != (line = input.readLine())) { 148 tempList.add(line); 149 boolean empty = isEmpty(line); 150 boolean comment = isComment(line); 151 if (state == WAITING_FOR_KEY_VALUE) { 152 if (empty) { 153 createNonKeyItem(tempList); 155 commentLinesCount = 0; 156 } else { 157 if (comment) { 158 commentLinesCount++; 159 } else { 160 state = READING_KEY_VALUE; 161 } 162 } 163 } 164 if (state == READING_KEY_VALUE && !isContinue(line)) { 165 createKeyItem(tempList, commentLinesCount); 167 state = WAITING_FOR_KEY_VALUE; 168 commentLinesCount = 0; 169 } 170 } 171 if (tempList.size() > 0) { 172 if (state == READING_KEY_VALUE) { 173 createKeyItem(tempList, commentLinesCount); 175 } else { 176 createNonKeyItem(tempList); 177 } 178 } 179 } 180 181 186 public void store(OutputStream stream) throws IOException { 187 boolean previousLineWasEmpty = true; 188 BufferedWriter output = new BufferedWriter (new OutputStreamWriter (stream, "ISO-8859-1")); 189 Iterator it = items.iterator(); 190 while (it.hasNext()) { 191 Item item = (Item)it.next(); 192 if (item.isSeparate() && !previousLineWasEmpty) { 193 output.newLine(); 194 } 195 Iterator it2 = item.getRawData().iterator(); 196 String line = null; 197 while (it2.hasNext()) { 198 line = (String )it2.next(); 199 output.write(line); 200 output.newLine(); 201 } 202 if (line != null) { 203 previousLineWasEmpty = isEmpty(line); 204 } 205 } 206 output.flush(); 207 } 208 209 public Object put(Object key, Object value) { 210 if (key == null || value == null) { 211 throw new NullPointerException (); 212 } 213 Item item = (Item)itemIndex.get((String ) key); 214 String result = null; 215 if (item != null) { 216 result = item.getValue(); 217 item.setValue((String ) value); 218 } else { 219 item = new Item((String ) key, (String ) value); 220 addItem(item, alphabetize); 221 } 222 return result; 223 } 224 225 231 public String getProperty(String key) { 232 return (String )get(key); 233 } 234 235 243 public String setProperty(String key, String value) { 244 String result = getProperty(key); 245 put(key, value); 246 return result; 247 } 248 249 260 public String setProperty(String key, String [] value) { 261 String result = getProperty(key); 262 if (key == null || value == null) { 263 throw new NullPointerException (); 264 } 265 List valueList = Arrays.asList(value); 266 Item item = (Item) itemIndex.get(key); 267 if (item != null) { 268 item.setValue(valueList); 269 } else { 270 addItem(new Item(key, valueList), alphabetize); 271 } 272 return result; 273 } 274 275 285 public String [] getComment(String key) { 286 Item item = (Item)itemIndex.get(key); 287 if (item == null) { 288 return new String [0]; 289 } 290 return item.getComment(); 291 } 292 293 305 public void setComment(String key, String [] comment, boolean separate) { 306 Item item = (Item)itemIndex.get(key); 308 if (item == null) { 309 throw new IllegalArgumentException ("Cannot set comment for non-existing property "+key); 310 } 311 item.setComment(comment, separate); 312 } 313 314 public Object clone() { 315 return cloneProperties(); 316 } 317 318 322 public EditableProperties cloneProperties() { 323 return new EditableProperties(this); 324 } 325 326 private void createNonKeyItem(List lines) { 328 if (items.size() > 0) { 330 Item item = (Item)items.get(items.size()-1); 331 if (item.getKey() == null) { 332 item.addCommentLines(lines); 334 lines.clear(); 335 return; 336 } 337 } 338 Item item = new Item(lines); 340 addItem(item, false); 341 lines.clear(); 342 } 343 344 private void createKeyItem(List lines, int commentLinesCount) { 347 Item item = new Item(lines.subList(0, commentLinesCount), lines.subList(commentLinesCount, lines.size())); 348 addItem(item, false); 349 lines.clear(); 350 } 351 352 private void addItem(Item item, boolean sort) { 353 String key = item.getKey(); 354 if (sort) { 355 assert key != null; 356 for (int i=0; i<items.size(); i++) { 357 String k = ((Item)items.get(i)).getKey(); 358 if (k != null && k.compareToIgnoreCase(key) > 0) { 359 items.add(i, item); 360 itemIndex.put(key, item); 361 return; 362 } 363 } 364 } 365 items.add(item); 366 if (key != null) { 367 itemIndex.put(key, item); 368 } 369 } 370 371 private void removeItem(Item item) { 372 items.remove(item); 373 if (item.getKey() != null) { 374 itemIndex.remove(item.getKey()); 375 } 376 } 377 378 private boolean isContinue(String line) { 380 int index = line.length() - 1; 381 int slashCount = 0; 382 while (index >= 0 && line.charAt(index) == '\\') { 383 slashCount++; 384 index--; 385 } 386 return (slashCount % 2 != 0); 389 } 390 391 private static boolean isComment(String line) { 393 line = trimLeft(line); 394 if (line.length() != 0 && commentChars.indexOf(line.charAt(0)) != -1) { 395 return true; 396 } else { 397 return false; 398 } 399 } 400 401 private static boolean isEmpty(String line) { 403 return trimLeft(line).length() == 0; 404 } 405 406 private static String trimLeft(String line) { 408 int start = 0; 409 while (start < line.length()) { 410 if (whiteSpaceChars.indexOf(line.charAt(start)) == -1) { 411 break; 412 } 413 start++; 414 } 415 return line.substring(start); 416 } 417 418 423 private static class Item implements Cloneable { 424 425 427 private List commentLines; 428 429 431 private List keyValueLines; 432 433 434 private String key; 435 436 437 private String value; 438 439 441 private boolean separate; 442 443 private Item() { 445 } 446 447 451 public Item(List commentLines) { 452 this.commentLines = new ArrayList (commentLines); 453 } 454 455 459 public Item(List commentLines, List keyValueLines) { 460 this.commentLines = new ArrayList (commentLines); 461 this.keyValueLines = new ArrayList (keyValueLines); 462 parse(keyValueLines); 463 } 464 465 468 public Item(String key, String value) { 469 this.key = key; 470 this.value = value; 471 } 472 473 476 public Item(String key, List value) { 477 this.key = key; 478 setValue(value); 479 } 480 481 void addCommentLines(List lines) { 483 assert key == null; 484 commentLines.addAll(lines); 485 } 486 487 public String [] getComment() { 488 String [] res = new String [commentLines.size()]; 489 for (int i = 0; i < res.length; i++) { 490 res[i] = decodeUnicode((String ) commentLines.get(i)); 492 } 493 return res; 494 } 495 496 public void setComment(String [] commentLines, boolean separate) { 497 this.separate = separate; 498 this.commentLines = new ArrayList (commentLines.length); 499 for (int i = 0; i < commentLines.length; i++) { 500 this.commentLines.add(encodeUnicode(commentLines[i])); 502 } 503 } 504 505 public String getKey() { 506 return key; 507 } 508 509 public String getValue() { 510 return value; 511 } 512 513 public void setValue(String value) { 514 this.value = value; 515 keyValueLines = null; 516 } 517 518 public void setValue(List value) { 519 StringBuffer val = new StringBuffer (); 520 List l = new ArrayList (); 521 if (!value.isEmpty()) { 522 l.add(encode(key, true) + "=\\"); Iterator it = value.iterator(); 524 while (it.hasNext()) { 525 String s = (String )it.next(); 526 val.append(s); 527 s = encode(s, false); 528 l.add(it.hasNext() ? INDENT + s + '\\' : INDENT + s); } 530 } else { 531 l.add(encode(key, true) + '='); } 534 this.value = val.toString(); 535 keyValueLines = l; 536 } 537 538 public boolean isSeparate() { 539 return separate; 540 } 541 542 545 public List getRawData() { 546 ArrayList l = new ArrayList (); 547 if (commentLines != null) { 548 l.addAll(commentLines); 549 } 550 if (keyValueLines != null) { 551 l.addAll(keyValueLines); 552 } else { 553 keyValueLines = new ArrayList (); 554 if (key != null && value != null) { 555 keyValueLines.add(encode(key, true)+"="+encode(value, false)); 556 } 557 l.addAll(keyValueLines); 558 } 559 return l; 560 } 561 562 private void parse(List keyValueLines) { 563 String line = mergeLines(keyValueLines); 565 splitKeyValue(line); 567 } 568 569 private String mergeLines(List lines) { 570 String line = ""; 571 Iterator it = lines.iterator(); 572 while (it.hasNext()) { 573 String l = trimLeft((String )it.next()); 574 if (it.hasNext()) { 576 assert l.endsWith("\\") : lines; 577 l = l.substring(0, l.length()-1); 578 } 579 line += l; 580 } 581 return line; 582 } 583 584 private void splitKeyValue(String line) { 585 int separatorIndex = 0; 586 while (separatorIndex < line.length()) { 587 char ch = line.charAt(separatorIndex); 588 if (ch == '\\') { 589 separatorIndex++; 591 } else { 592 if (keyValueSeparators.indexOf(ch) != -1) { 593 break; 594 } 595 } 596 separatorIndex++; 597 } 598 key = decode(line.substring(0, separatorIndex)); 599 line = trimLeft(line.substring(separatorIndex)); 600 if (line.length() == 0) { 601 value = ""; 602 return; 603 } 604 if (strictKeyValueSeparators.indexOf(line.charAt(0)) != -1) { 605 line = trimLeft(line.substring(1)); 606 } 607 value = decode(line); 608 } 609 610 private static String decode(String input) { 611 char ch; 612 int len = input.length(); 613 StringBuffer output = new StringBuffer (len); 614 for (int x=0; x<len; x++) { 615 ch = input.charAt(x); 616 if (ch != '\\') { 617 output.append(ch); 618 continue; 619 } 620 x++; 621 if (x==len) { 622 continue; 624 } 625 ch = input.charAt(x); 626 if (ch == 'u') { 627 if (x+5>len) { 628 output.append(input.substring(x-1)); 630 x += 4; 631 continue; 632 } 633 String val = input.substring(x+1, x+5); 634 try { 635 output.append((char)Integer.parseInt(val, 16)); 636 } catch (NumberFormatException e) { 637 output.append(input.substring(x - 1, x + 5)); 639 } 640 x += 4; 641 } else { 642 if (ch == 't') { 643 ch = '\t'; 644 } else if (ch == 'r') { 645 ch = '\r'; 646 } else if (ch == 'n') { 647 ch = '\n'; 648 } else if (ch == 'f') { 649 ch = '\f'; 650 } 651 output.append(ch); 652 } 653 } 654 return output.toString(); 655 } 656 657 private static String encode(String input, boolean escapeSpace) { 658 int len = input.length(); 659 StringBuffer output = new StringBuffer (len*2); 660 661 for(int x=0; x<len; x++) { 662 char ch = input.charAt(x); 663 switch(ch) { 664 case ' ': 665 if (x == 0 || escapeSpace) { 666 output.append('\\'); 667 } 668 output.append(' '); 669 break; 670 case '\\': 671 output.append("\\\\"); 672 break; 673 case '\t': 674 output.append("\\t"); 675 break; 676 case '\n': 677 output.append("\\n"); 678 break; 679 case '\r': 680 output.append("\\r"); 681 break; 682 case '\f': 683 output.append("\\f"); 684 break; 685 default: 686 if ((ch < 0x0020) || (ch > 0x007e)) { 687 output.append("\\u"); 688 String hex = Integer.toHexString(ch); 689 for (int i = 0; i < 4 - hex.length(); i++) { 690 output.append('0'); 691 } 692 output.append(hex); 693 } else { 694 output.append(ch); 695 } 696 } 697 } 698 return output.toString(); 699 } 700 701 private static String decodeUnicode(String input) { 702 char ch; 703 int len = input.length(); 704 StringBuffer output = new StringBuffer (len); 705 for (int x = 0; x < len; x++) { 706 ch = input.charAt(x); 707 if (ch != '\\') { 708 output.append(ch); 709 continue; 710 } 711 x++; 712 if (x==len) { 713 continue; 715 } 716 ch = input.charAt(x); 717 if (ch == 'u') { 718 if (x+5>len) { 719 output.append(input.substring(x-1)); 721 x += 4; 722 continue; 723 } 724 String val = input.substring(x+1, x+5); 725 try { 726 output.append((char)Integer.parseInt(val, 16)); 727 } catch (NumberFormatException e) { 728 output.append(input.substring(x - 1, x + 5)); 730 } 731 x += 4; 732 } else { 733 output.append(ch); 734 } 735 } 736 return output.toString(); 737 } 738 739 private static String encodeUnicode(String input) { 740 int len = input.length(); 741 StringBuffer output = new StringBuffer (len * 2); 742 for (int x = 0; x < len; x++) { 743 char ch = input.charAt(x); 744 if ((ch < 0x0020) || (ch > 0x007e)) { 745 output.append("\\u"); String hex = Integer.toHexString(ch); 747 for (int i = 0; i < 4 - hex.length(); i++) { 748 output.append('0'); 749 } 750 output.append(hex); 751 } else { 752 output.append(ch); 753 } 754 } 755 return output.toString(); 756 } 757 758 public Object clone() { 759 Item item = new Item(); 760 if (keyValueLines != null) { 761 item.keyValueLines = new ArrayList (keyValueLines); 762 } 763 if (commentLines != null) { 764 item.commentLines = new ArrayList (commentLines); 765 } 766 item.key = key; 767 item.value = value; 768 return item; 769 } 770 771 } 772 773 private static class SetImpl extends AbstractSet { 774 775 private EditableProperties props; 776 777 public SetImpl(EditableProperties props) { 778 this.props = props; 779 } 780 781 public Iterator iterator() { 782 return new IteratorImpl(props); 783 } 784 785 public int size() { 786 return props.items.size(); 787 } 788 789 } 790 791 private static class IteratorImpl implements Iterator { 792 793 private EditableProperties props; 794 private int index = -1; 795 private Item item; 796 797 public IteratorImpl(EditableProperties props) { 798 this.props = props; 799 } 800 801 public boolean hasNext() { 802 return findNext() != -1; 803 } 804 805 public Object next() { 806 index = findNext(); 807 if (index == -1) { 808 throw new NoSuchElementException ("There is no more items"); 809 } 810 item = (Item)props.items.get(index); 811 return new MapEntryImpl(item); 812 } 813 814 public void remove() { 815 if (item == null) { 816 throw new IllegalStateException (); 817 } 818 props.removeItem(item); 819 index--; 820 item = null; 821 } 822 823 private int findNext() { 824 int res = index+1; 825 while (res < props.size()) { 826 Item i = (Item)props.items.get(res); 827 if (i.getKey() != null && i.getValue() != null) { 828 return res; 829 } 830 res++; 831 } 832 return -1; 833 } 834 835 } 836 837 private static class MapEntryImpl implements Map.Entry { 838 839 private Item item; 840 841 public MapEntryImpl(Item item) { 842 this.item = item; 843 } 844 845 public Object getKey() { 846 return item.getKey(); 847 } 848 849 public Object getValue() { 850 return item.getValue(); 851 } 852 853 public Object setValue(Object value) { 854 String result = item.getValue(); 855 item.setValue((String )value); 856 return result; 857 } 858 859 } 860 861 } 862 | Popular Tags |