1 21 package com.Ostermiller.util; 22 23 import java.io.IOException ; 24 import java.io.InputStream ; 25 import java.io.FileInputStream ; 26 import java.io.InputStreamReader ; 27 import java.io.OutputStream ; 28 import java.io.FileOutputStream ; 29 import java.io.FileNotFoundException ; 30 import java.io.File ; 31 import java.util.ArrayList ; 32 import java.util.HashMap ; 33 import java.util.HashSet ; 34 import java.util.Set ; 35 import java.util.Arrays ; 36 import java.io.ByteArrayOutputStream ; 37 import java.io.UnsupportedEncodingException ; 38 39 124 public class UberProperties { 125 126 133 private HashMap <String ,Property> properties = new HashMap <String ,Property>(); 134 135 141 private String comment = null; 142 143 148 private class Property { 149 150 156 private ArrayList <String > list; 157 158 164 private String comment = null; 165 166 173 public void setComment(String comment){ 174 this.comment = comment; 175 } 176 177 184 public String getComment(){ 185 return this.comment; 186 } 187 188 195 public Property(String value){ 196 list = new ArrayList <String >(1); 197 add(value); 198 } 199 200 207 public Property(String [] values){ 208 list = new ArrayList <String >(values.length); 209 add(values); 210 } 211 212 219 public void set(String value){ 220 list.clear(); 221 add(value); 222 } 223 224 231 public void set(String [] values){ 232 list.clear(); 233 add(values); 234 } 235 236 243 public void add(String value){ 244 list.add(value); 245 } 246 247 254 public void add(String [] values){ 255 list.ensureCapacity(list.size() + values.length); 256 for (int i=0; i<values.length; i++){ 257 add(values[i]); 258 } 259 } 260 261 268 public String getValue(){ 269 return (String )list.get(list.size() - 1); 270 } 271 272 279 public String [] getValues(){ 280 return (String [])list.toArray(new String [list.size()]); 281 } 282 } 283 284 289 public UberProperties(){ 290 } 291 292 300 public UberProperties(UberProperties defaults){ 301 merge(defaults); 302 } 303 304 311 private void merge(UberProperties defaults){ 312 setComment(defaults.getComment()); 313 String [] names = defaults.propertyNames(); 314 for (int i=0; i<names.length; i++){ 315 setProperties(names[i], defaults.getProperties(names[i])); 316 setComment(names[i], defaults.getComment(names[i])); 317 } 318 } 319 320 329 public boolean contains(String name){ 330 if (name == null) throw new NullPointerException (); 331 return properties.containsKey(name); 332 } 333 334 343 public boolean remove(String name){ 344 if (!contains(name)) return false; 345 properties.remove(name); 346 return true; 347 } 348 349 359 public void setProperty(String name, String value){ 360 if (name == null) throw new NullPointerException (); 361 if (value == null){ 362 properties.remove(name); 363 } else { 364 Property property; 365 if (properties.containsKey(name)){ 366 property = (Property)properties.get(name); 367 property.set(value); 368 } else { 369 property = new Property(value); 370 properties.put(name, property); 371 } 372 } 373 } 374 375 387 public void setProperties(String name, String [] values){ 388 if (name == null) throw new NullPointerException (); 389 if (values.length == 0) throw new IllegalArgumentException (); 390 Property property; 391 if (properties.containsKey(name)){ 392 property = (Property)properties.get(name); 393 property.set(values); 394 } else { 395 property = new Property(values); 396 properties.put(name, property); 397 } 398 } 399 400 412 public void setProperty(String name, String value, String comment){ 413 if (name == null) throw new NullPointerException (); 414 if (value == null){ 415 properties.remove(name); 416 } else { 417 setProperty(name, value); 418 setComment(name, comment); 419 } 420 } 421 422 435 public void setProperties(String name, String [] values, String comment){ 436 if (name == null) throw new NullPointerException (); 437 if (values.length == 0) throw new IllegalArgumentException (); 438 setProperties(name, values); 439 setComment(name, comment); 440 } 441 442 454 private void setComment(String name, String comment){ 455 if (name == null) throw new NullPointerException (); 456 if (!properties.containsKey(name)) throw new IllegalArgumentException (); 457 ((Property)properties.get(name)).setComment(comment); 458 } 459 460 472 public void addProperty(String name, String value, String comment){ 473 if (name == null) throw new NullPointerException (); 474 if (value == null) throw new NullPointerException (); 475 addProperty(name, value); 476 setComment(name, comment); 477 } 478 479 491 public void addProperties(String name, String [] values, String comment){ 492 if (name == null) throw new NullPointerException (); 493 if (values == null) throw new NullPointerException (); 494 addProperties(name, values); 495 setComment(name, comment); 496 } 497 498 509 public void addProperty(String name, String value){ 510 if (name == null) throw new NullPointerException (); 511 if (value == null) throw new NullPointerException (); 512 Property property; 513 if (properties.containsKey(name)){ 514 property = (Property)properties.get(name); 515 property.add(value); 516 } else { 517 property = new Property(value); 518 properties.put(name, property); 519 } 520 } 521 522 533 public void addProperties(String name, String [] values){ 534 if (name == null) throw new NullPointerException (); 535 if (values == null) throw new NullPointerException (); 536 Property property; 537 if (properties.containsKey(name)){ 538 property = (Property)properties.get(name); 539 property.add(values); 540 } else { 541 property = new Property(values); 542 properties.put(name, property); 543 } 544 } 545 546 private static int hexDigitValue(char c){ 547 switch (c){ 548 case '0': return 0; 549 case '1': return 1; 550 case '2': return 2; 551 case '3': return 3; 552 case '4': return 4; 553 case '5': return 5; 554 case '6': return 6; 555 case '7': return 7; 556 case '8': return 8; 557 case '9': return 9; 558 case 'a': case 'A': return 10; 559 case 'b': case 'B': return 11; 560 case 'c': case 'C': return 12; 561 case 'd': case 'D': return 13; 562 case 'e': case 'E': return 14; 563 case 'f': case 'F': return 15; 564 default: return -1; 565 } 566 } 567 568 private static String unescape(String s){ 569 StringBuffer sb = new StringBuffer (s.length()); 570 for(int i=0; i<s.length(); i++){ 571 char c = s.charAt(i); 572 if (c == '\\'){ 573 i++; 574 if (i < s.length()){ 575 c = s.charAt(i); 576 switch (c){ 577 case 'n': { 578 sb.append('\n'); 579 } break; 580 case 'r': { 581 sb.append('\r'); 582 } break; 583 case 't': { 584 sb.append('\t'); 585 } break; 586 case 'f': { 587 sb.append('\f'); 588 } break; 589 case 'u': { 590 boolean foundUnicode = false; 591 if (i+4 < s.length()){ 592 int unicodeValue = 0; 593 for (int j = 3; unicodeValue >= 0 && j >= 0; j--){ 594 int val = hexDigitValue(s.charAt(i+(4-j))); 595 if (val == -1){ 596 unicodeValue = -1; 597 } else { 598 unicodeValue |= (val << (j << 2)); 599 } 600 } 601 if (unicodeValue >= 0) { 602 i+=4; 603 foundUnicode = true; 604 sb.append((char)unicodeValue); 605 } 606 } 607 if (!foundUnicode) sb.append(c); 608 } break; 609 default: { 610 sb.append(c); 611 } break; 612 } 613 } 614 } else { 615 sb.append(c); 616 } 617 } 618 return sb.toString(); 619 } 620 621 647 public void load(String [] userFile, String systemResource) throws IOException { 648 int length = userFile.length; 649 if (userFile.length == 0) throw new IllegalArgumentException (); 650 InputStream in = ClassLoader.getSystemResourceAsStream(systemResource); 651 if (in==null) throw new FileNotFoundException (systemResource); 652 if (systemResource != null) load(in); 653 File f = new File (System.getProperty("user.home")); 654 for (int i=0; f.exists() && i<length; i++){ 655 f = new File (f, userFile[i]); 656 } 657 if (f.exists()) load(new FileInputStream (f)); 658 } 659 660 670 public void load(InputStream in, boolean add) throws IOException { 671 PropertiesLexer lex = new PropertiesLexer(new InputStreamReader (in, "ISO-8859-1")); 672 PropertiesToken t; 673 HashSet <String > names = new HashSet <String >(); 674 StringBuffer comment = new StringBuffer (); 675 boolean foundComment = false; 676 StringBuffer name = new StringBuffer (); 677 StringBuffer value = new StringBuffer (); 678 int last = TYPE_COMMENT; 679 boolean atStart = true; 680 String lastSeparator = null; 681 while ((t = lex.getNextToken()) != null){ 682 if (t.getID() == PropertiesToken.COMMENT){ 683 int start = 1; 684 String commentText = t.getContents(); 685 if (commentText.startsWith("# ")) start = 2; 686 comment.append(commentText.substring(start, commentText.length())); 687 comment.append("\n"); 688 lex.getNextToken(); 689 foundComment = true; 690 } else if (t.getID() == PropertiesToken.NAME){ 691 if (atStart){ 692 setComment(comment.toString()); 693 comment.setLength(0); 694 atStart = false; 695 } 696 name.append(t.getContents()); 697 } else if (t.getID() == PropertiesToken.VALUE){ 698 if (atStart){ 699 setComment(comment.toString()); 700 comment.setLength(0); 701 atStart = false; 702 } 703 value.append(t.getContents()); 704 } else if (t.getID() == PropertiesToken.SEPARATOR){ 705 lastSeparator = t.getContents(); 706 } else if (t.getID() == PropertiesToken.END_LINE_WHITE_SPACE){ 707 if (atStart){ 708 setComment(comment.toString()); 709 comment.setLength(0); 710 atStart = false; 711 } 712 String stName = unescape(name.toString()); 713 String stValue = unescape(value.toString()); 714 if (lastSeparator != null || stName.length() > 0 || stValue.length() > 0 ){ 715 if (add || names.contains(stName)){ 716 addProperty(stName, stValue); 717 } else { 718 setProperty(stName, stValue); 719 names.add(stName); 720 } 721 if (foundComment) setComment(stName, unescape(comment.toString())); 722 } 723 comment.setLength(0); 724 name.setLength(0); 725 value.setLength(0); 726 foundComment = false; 727 lastSeparator = null; 728 } 729 } 730 } 731 732 744 public void load(InputStream in) throws IOException { 745 load(in, false); 746 } 747 748 766 public void save(String [] userFile) throws IOException { 767 int length = userFile.length; 768 if (length == 0) throw new IllegalArgumentException (); 769 File f = new File (System.getProperty("user.home")); 770 for (int i=0; i<length; i++){ 771 f = new File (f, userFile[i]); 772 if (i == length - 2 && !f.exists()){ 773 f.mkdirs(); 774 } 775 } 776 OutputStream out = new FileOutputStream (f); 777 save(out); 778 out.close(); 779 } 780 781 789 public void save(OutputStream out) throws IOException { 790 writeComment(out, comment); 791 out.write('\n'); 792 String [] names = propertyNames(); 793 Arrays.sort(names); 794 for (int i=0; i<names.length; i++){ 795 writeComment(out, getComment(names[i])); 796 String [] values = getProperties(names[i]); 797 for (int j=0; j<values.length; j++){ 798 writeProperty(out, names[i], values[j]); 799 } 800 } 801 out.flush(); 802 } 803 804 private static void writeProperty(OutputStream out, String name, String value) throws IOException { 805 writeEscapedISO88591(out, name, TYPE_NAME); 806 out.write('='); 807 writeEscapedISO88591(out, value, TYPE_VALUE); 808 out.write('\n'); 809 810 } 811 812 private static void writeComment(OutputStream out, String comment) throws IOException { 813 if (comment != null){ 814 java.util.StringTokenizer tok = new java.util.StringTokenizer (comment, "\r\n"); 815 while (tok.hasMoreTokens()){ 816 out.write('#'); 817 out.write(' '); 818 writeEscapedISO88591(out, tok.nextToken(), TYPE_COMMENT); 819 out.write('\n'); 820 } 821 } 822 } 823 824 private static final int TYPE_COMMENT = 0; 825 private static final int TYPE_NAME = 1; 826 private static final int TYPE_VALUE = 2; 827 828 private static void writeEscapedISO88591(OutputStream out, String s, int type) throws IOException { 829 for (int i=0; i<s.length(); i++){ 830 int c = (int)s.charAt(i); 831 if (c < 0x100){ 832 boolean escape = false; 833 if (c == '\r' || c == '\n' || c == '\\'){ 834 escape = true; 835 } else if (c == ' ' || c == '\t' || c == '\f'){ 836 if(type == TYPE_NAME){ 837 escape = true; 838 } else if (type == TYPE_VALUE && (i==0 || i == s.length() - 1)){ 839 escape = true; 840 } 841 } else if (type == TYPE_NAME && (c == '=' || c == ':')){ 842 escape = true; 843 } 844 if (escape){ 845 switch (c){ 846 case '\n': { 847 switch (type){ 848 case TYPE_COMMENT: { 849 out.write('\n'); 850 out.write('#'); 851 out.write(' '); 852 } break; 853 case TYPE_NAME: { 854 out.write('\\'); 855 out.write('n'); 856 out.write('\\'); 857 out.write('\n'); 858 out.write('\t'); 859 } break; 860 case TYPE_VALUE: { 861 out.write('\\'); 862 out.write('n'); 863 out.write('\\'); 864 out.write('\n'); 865 out.write('\t'); 866 out.write('\t'); 867 } break; 868 } 869 } break; 870 case '\\': { 871 out.write('\\'); 872 out.write('\\'); 873 } break; 874 case '\r': { 875 out.write('\\'); 876 out.write('r'); 877 } break; 878 case '\t': { 879 out.write('\\'); 880 out.write('t'); 881 } break; 882 case '\f': { 883 out.write('\\'); 884 out.write('f'); 885 } break; 886 default : { 887 out.write('\\'); 888 out.write((byte)c); 889 } break; 890 } 891 } else { 892 out.write((byte)c); 893 } 894 } else { 895 out.write('\\'); 896 out.write('u'); 897 out.write(StringHelper.prepad(Integer.toHexString(c), 4, '0').getBytes("ISO-8859-1")); 898 } 899 } 900 } 901 902 912 public String getProperty(String name){ 913 String value = null; 914 if (properties.containsKey(name)){ 915 value = ((Property)properties.get(name)).getValue(); 916 } 917 return value; 918 } 919 920 931 public String getProperty(String name, String defaultValue){ 932 String value = getProperty(name); 933 if (value == null) value = defaultValue; 934 return value; 935 } 936 937 950 public String [] getProperties(String name){ 951 String [] values = null; 952 if (properties.containsKey(name)){ 953 values = ((Property)properties.get(name)).getValues(); 954 } 955 return values; 956 } 957 958 972 public String [] getProperties(String name, String [] defaultValues){ 973 String [] values = getProperties(name); 974 if (values == null) values = defaultValues; 975 return values; 976 } 977 978 989 public String getComment(String name){ 990 String comment = null; 991 if (properties.containsKey(name)){ 992 comment = ((Property)properties.get(name)).getComment(); 993 } 994 return comment; 995 } 996 997 1006 public String [] propertyNames(){ 1007 Set <String > names = properties.keySet(); 1008 return (String [])names.toArray(new String [names.size()]); 1009 } 1010 1011 1018 public void setComment(String comment){ 1019 this.comment = comment; 1020 } 1021 1022 1029 public String getComment(){ 1030 return this.comment; 1031 } 1032 1033 1041 public int getPropertyNameCount(){ 1042 return properties.keySet().size(); 1043 } 1044 1045 1052 public String toString(){ 1053 ByteArrayOutputStream out = new ByteArrayOutputStream (); 1054 try { 1055 this.save(out); 1056 } catch (IOException iox){ 1057 throw new Error ("IO constructed on memory, this shouldn't happen.", iox); 1058 } 1059 String s = null; 1060 try { 1061 s = new String (out.toByteArray(), "ISO-8859-1"); 1062 } catch (UnsupportedEncodingException uee){ 1063 throw new Error ("ISO-8859-1 should be recognized.", uee); 1064 } 1065 return s; 1066 } 1067} 1068 | Popular Tags |