home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / Properties.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  19.8 KB  |  514 lines

  1. /*
  2.  * @(#)Properties.java    1.41 98/03/18
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util;
  16.  
  17. import java.io.IOException;
  18. import java.io.PrintStream;
  19. import java.io.PrintWriter;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.BufferedReader;
  23. import java.io.OutputStream;
  24. import java.io.OutputStreamWriter;
  25. import java.io.BufferedWriter;
  26. import java.util.Hashtable;
  27.  
  28. /**
  29.  * The <code>Properties</code> class represents a persistent set of 
  30.  * properties. The <code>Properties</code> can be saved to a stream 
  31.  * or loaded from a stream. Each key and its corresponding value in 
  32.  * the property list is a string. 
  33.  * <p>
  34.  * A property list can contain another property list as its 
  35.  * "defaults"; this second property list is searched if 
  36.  * the property key is not found in the original property list. 
  37.  *
  38.  * @author  Arthur van Hoff
  39.  * @author  Michael McCloskey
  40.  * @version 1.41, 98/03/18
  41.  * @since   JDK1.0
  42.  */
  43. public
  44. class Properties extends Hashtable {
  45.     /**
  46.      * A property list that contains default values for any keys not 
  47.      * found in this property list. 
  48.      */
  49.     protected Properties defaults;
  50.  
  51.     /**
  52.      * Creates an empty property list with no default values. 
  53.      */
  54.     public Properties() {
  55.     this(null);
  56.     }
  57.  
  58.     /**
  59.      * Creates an empty property list with the specified defaults. 
  60.      *
  61.      * @param   defaults   the defaults.
  62.      */
  63.     public Properties(Properties defaults) {
  64.     this.defaults = defaults;
  65.     }
  66.  
  67.     /**
  68.      * Calls the hashtable method <code>put</code>. Provided for
  69.      * parallelism with the getProperties method. Enforces use of
  70.      * strings for property keys and values.
  71.      * @since    JDK1.2
  72.      */
  73.  
  74.     public synchronized Object setProperty(String key, String value) {
  75.         return put(key, value);
  76.     }
  77.  
  78.     /**
  79.      * Maps the specified <code>key</code> to the specified 
  80.      * <code>value</code> in this Properties set. Neither the key nor the 
  81.      * value can be <code>null</code>.
  82.      * <p>
  83.      *
  84.      * @param      key     the Properties key.
  85.      * @param      value   the value.
  86.      * @return     the previous value of the specified key in this hashtable,
  87.      *             or <code>null</code> if it did not have one.
  88.      * @exception  NullPointerException  if the key or value is
  89.      *               <code>null</code>
  90.      * @exception  IllegalArgumentException if the key is not a String
  91.      *                                    or if the value is not a String
  92.      * @see     Object#equals(Object)
  93.      * @see     #get(Object)
  94.      * @since   JDK1.2
  95.      */
  96.     public synchronized Object put(Object key, Object value) {
  97.         if (!(key instanceof String))
  98.             throw new IllegalArgumentException("Property keys must be Strings");
  99.         if (!(value instanceof String))
  100.             throw new IllegalArgumentException("Property values must be Strings");
  101.  
  102.         return super.put(key, value);
  103.     }
  104.  
  105.     private static String keyValueSeparators = "=: \t\r\n\f";
  106.  
  107.     private static String specialSaveChars = "=: \t\r\n\f#!";
  108.  
  109.     private static String whiteSpaceChars = " \t\r\n\f";
  110.  
  111.     /**
  112.      * Reads a property list (key and element pairs) from the input stream.
  113.      * <p>
  114.      * Every property occupies one line of the input stream. Each line
  115.      * is terminated by a line terminator (<code>\n</code> or <code>\r</code>
  116.      * or <code>\r\n</code>). Lines from the input stream are processed until
  117.      * end of file is reached on the input stream.
  118.      * <p>
  119.      * A line that contains only whitespace or whose first non-whitespace
  120.      * character is an ASCII <code>#</code> or <code>!</code> is ignored
  121.      * (thus, <code>#</code> or <code>!</code> indicate comment lines).
  122.      * <p>
  123.      * Every line other than a blank line or a comment line describes one
  124.      * property to be added to the table (except that if a line ends with \,
  125.      * then the following line, if it exists, is treated as a continuation
  126.      * line, as described
  127.      * below). The key consists of all the characters in the line starting
  128.      * with the first non-whitespace character and up to, but not including,
  129.      * the first ASCII <code>=</code>, <code>:</code>, or whitespace
  130.      * character. All of the key termination characters may be included in
  131.      * the key by preceding them with a \. 
  132.      * Any whitespace after the key is skipped; if the first non-whitespace
  133.      * character after the key is <code>=</code> or <code>:</code>, then it
  134.      * is ignored and any whitespace characters after it are also skipped.
  135.      * All remaining characters on the line become part of the associated
  136.      * element string. Within the element string, the ASCII
  137.      * escape sequences <code>\t</code>, <code>\n</code>,
  138.      * <code>\r</code>, <code>\\</code>, <code>\"</code>, <code>\'</code>,
  139.      * <code>\  </code>  (a backslash and a space), and
  140.      * <code>\\u</code><i>xxxx</i> are recognized and converted to single
  141.      * characters. Moreover, if the last character on the line is
  142.      * <code>\</code>, then the next line is treated as a continuation of the
  143.      * current line; the <code>\</code> and line terminator are simply
  144.      * discarded, and any leading whitespace characters on the continuation
  145.      * line are also discarded and are not part of the element string.
  146.      * <p>
  147.      * As an example, each of the following four lines specifies the key
  148.      * <code>"Truth"</code> and the associated element value
  149.      * <code>"Beauty"</code>:
  150.      * <p>
  151.      * <pre>
  152.      * Truth = Beauty
  153.      *    Truth:Beauty
  154.      * Truth            :Beauty
  155.      * </pre>
  156.      * As another example, the following three lines specify a single
  157.      * property:
  158.      * <p>
  159.      * <pre>
  160.      * fruits                apple, banana, pear, \
  161.      *                                  cantaloupe, watermelon, \
  162.      *                                  kiwi, mango
  163.      * </pre>
  164.      * The key is <code>"fruits"</code> and the associated element is:
  165.      * <p>
  166.      * <pre>"apple, banana, pear, cantaloupe, watermelon,kiwi, mango"</pre>
  167.      * Note that a space appears before each <code>\</code> so that a space
  168.      * will appear after each comma in the final result; the <code>\</code>,
  169.      * line terminator, and leading whitespace on the continuation line are
  170.      * merely discarded and are <i>not</i> replaced by one or more other
  171.      * characters.
  172.      * <p>
  173.      * As a third example, the line:
  174.      * <p>
  175.      * <pre>cheeses
  176.      * </pre>
  177.      * specifies that the key is <code>"cheeses"</code> and the associated
  178.      * element is the empty string.<p>
  179.      *
  180.      * @param      in   the input stream.
  181.      * @exception  IOException  if an error occurred when reading from the
  182.      *               input stream.
  183.      */
  184.     public synchronized void load(InputStream inStream) throws IOException {
  185.     
  186.         BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "8859_1"));
  187.     while (true) {
  188.             // Get next line
  189.             String theLine = in.readLine();
  190.             if(theLine == null)
  191.                 return;
  192.          
  193.             if (theLine.length() > 0) {
  194.                 // Continue lines that end in slashes
  195.                 while (theLine.endsWith("\\")) {
  196.                     String nextLine = in.readLine();
  197.                     if(nextLine == null)
  198.                         nextLine = new String("");
  199.                     String loppedLine = theLine.substring(0, theLine.length()-1);
  200.                     // Advance beyond whitespace on new line
  201.                     int startIndex=0;
  202.                     for(startIndex=0; startIndex<nextLine.length(); startIndex++)
  203.                         if (whiteSpaceChars.indexOf(nextLine.charAt(startIndex)) == -1)
  204.                             break;
  205.                     nextLine = nextLine.substring(startIndex,nextLine.length());
  206.                     theLine = new String(loppedLine+nextLine);
  207.                 }
  208.                 // Skip comments
  209.                 char firstChar = theLine.charAt(0);
  210.                 if((firstChar != '#') && (firstChar != '!')) {
  211.                     // Find separation between key and value
  212.                     int len = theLine.length();
  213.                     int separatorIndex;
  214.                     for(separatorIndex=0; separatorIndex<len; separatorIndex++) {
  215.                         char currentChar = theLine.charAt(separatorIndex);
  216.                         if (currentChar == '\\')
  217.                             separatorIndex++;
  218.                         else if(keyValueSeparators.indexOf(currentChar) != -1)
  219.                             break;
  220.                     }
  221.  
  222.                     // Value starts after whitespace and separators
  223.                     int valueIndex;
  224.                     for (valueIndex=separatorIndex+1; valueIndex<len; valueIndex++)
  225.                        if (keyValueSeparators.indexOf(theLine.charAt(valueIndex)) == -1)
  226.                            break;
  227.                     String key = theLine.substring(0, separatorIndex);
  228.                     String value = (separatorIndex < len) ? theLine.substring(valueIndex, len) : "";
  229.  
  230.                     // Convert then store key and value
  231.                     key = loadConvert(key); 
  232.                     value = loadConvert(value);
  233.                     put(key, value);
  234.                 }
  235.             }
  236.     }
  237.     }
  238.  
  239.     /*
  240.      * convert encoded \\uxxxx to unicode chars
  241.      * and change special saved chars to their original forms
  242.      */
  243.     private String loadConvert (String theString) {
  244.         char aChar;
  245.         int len = theString.length();
  246.         StringBuffer outBuffer = new StringBuffer(len);
  247.   
  248.         for(int x=0; x<len; ) {
  249.             aChar = theString.charAt(x++);
  250.             if (aChar == '\\') {
  251.                 aChar = theString.charAt(x++);
  252.                 if(aChar == 'u') {
  253.                     // Read the xxxx
  254.                     int value=0;
  255.             for (int i=0; i<4; i++) {
  256.                 aChar = theString.charAt(x++);
  257.                 switch (aChar) {
  258.                   case '0': case '1': case '2': case '3': case '4':
  259.                   case '5': case '6': case '7': case '8': case '9':
  260.                      value = (value << 4) + aChar - '0';
  261.                  break;
  262.               case 'a': case 'b': case 'c':
  263.                           case 'd': case 'e': case 'f':
  264.                  value = (value << 4) + 10 + aChar - 'a';
  265.                  break;
  266.               case 'A': case 'B': case 'C':
  267.                           case 'D': case 'E': case 'F':
  268.                  value = (value << 4) + 10 + aChar - 'A';
  269.                  break;
  270.               default:
  271.                               throw new IllegalArgumentException(
  272.                                            "Malformed \\uxxxx encoding.");
  273.                         }
  274.                     }
  275.                     outBuffer.append((char)value);
  276.                 } else {
  277.                     if (aChar == 't') aChar = '\t';
  278.                     else if (aChar == 'r') aChar = '\r';
  279.                     else if (aChar == 'n') aChar = '\n';
  280.                     else if (aChar == 'f') aChar = '\f';
  281.                     outBuffer.append(aChar);
  282.                 }
  283.             } else 
  284.                 outBuffer.append(aChar);
  285.         }
  286.         return outBuffer.toString();
  287.     }
  288.  
  289.     /*
  290.      * Converts unicodes to encoded \\uxxxx
  291.      * and writes out any of the characters in specialSaveChars
  292.      * with a preceding slash
  293.      */
  294.     private String saveConvert(String theString) {
  295.         char aChar;
  296.         int len = theString.length();
  297.         StringBuffer outBuffer = new StringBuffer(len*2);
  298.  
  299.         for(int x=0; x<len; ) {
  300.             aChar = theString.charAt(x++);
  301.             switch(aChar) {
  302.                 case '\\':outBuffer.append('\\'); outBuffer.append('\\');
  303.                           continue;
  304.                 case '\t':outBuffer.append('\\'); outBuffer.append('t');
  305.                           continue;
  306.                 case '\n':outBuffer.append('\\'); outBuffer.append('n');
  307.                           continue;
  308.                 case '\r':outBuffer.append('\\'); outBuffer.append('r');
  309.                           continue;
  310.                 case '\f':outBuffer.append('\\'); outBuffer.append('f');
  311.                           continue;
  312.                 default:
  313.                     if ((aChar < 20) || (aChar > 127)) {
  314.                         outBuffer.append('\\');
  315.                         outBuffer.append('u');
  316.                         outBuffer.append(toHex((aChar >> 12) & 0xF));
  317.                         outBuffer.append(toHex((aChar >> 8) & 0xF));
  318.                         outBuffer.append(toHex((aChar >> 4) & 0xF));
  319.                         outBuffer.append(toHex((aChar >> 0) & 0xF));
  320.                     }
  321.                     else {
  322.                         if (specialSaveChars.indexOf(aChar) != -1)
  323.                             outBuffer.append('\\');      
  324.                         outBuffer.append(aChar);
  325.                     }
  326.             }
  327.         }
  328.         return outBuffer.toString();
  329.     }
  330.  
  331.     /**
  332.      * Writes this property list (key and element pairs) in this 
  333.      * <code>Properties</code> table to the output stream in a format suitable
  334.      * for loading into a <code>Properties</code> table using the
  335.      * <code>load</code> method.
  336.      * <p>
  337.      * Properties from the defaults table of this <code>Properties</code>
  338.      * table (if any) are <i>not</i> written out by this method.
  339.      * <p>
  340.      * If the header argument is not null, then an ASCII <code>#</code>
  341.      * character, the header string, and a newline are first written to the
  342.      * output stream. Thus, the <code>header</code> can serve as an
  343.      * identifying comment.
  344.      * <p>
  345.      * Next, a comment line is always written, consisting of an ASCII
  346.      * <code>#</code> character, the current date and time (as if produced
  347.      * by the <code>toString</code> method of <code>Date</code> for the
  348.      * current time), and a newline.
  349.      * <p>
  350.      * Then every entry in this <code>Properties</code> table is written out,
  351.      * one per line. For each entry the key string is written, then an ASCII
  352.      * <code>=</code>, then the associated element string. Each character of
  353.      * the element string is examined to see whether it should be rendered as
  354.      * an escape sequence. The ASCII characters <code>\</code>, tab, newline,
  355.      * and carriage return are written as <code>\\</code>, <code>\t</code>,
  356.      * <code>\n</code>, and <code>\r</code>, respectively. Characters less
  357.      * than <code>\u0020</code> and characters greater than
  358.      * <code>\u007E</code> are written as <code>\\u</code><i>xxxx</i> for
  359.      * the appropriate hexadecimal value <i>xxxx</i>. Space characters, but
  360.      * not embedded or trailing space characters, are written with a preceding
  361.      * <code>\</code>. The key and value characters <code>#</code>,
  362.      * <code>!</code>, <code>=</code>, and <code>:</code> are written with a
  363.      * preceding slash to ensure that they are properly loaded.
  364.      * <p>
  365.      * After the entries have been written, the output stream is flushed.  The
  366.      * output stream remains open after this method returns.
  367.      *
  368.      * @param   out      an output stream.
  369.      * @param   header   a description of the property list.
  370.      */
  371.     public synchronized void save(OutputStream out, String header)  {
  372.         BufferedWriter awriter;
  373.         try {
  374.             awriter = new BufferedWriter(
  375.                                  new OutputStreamWriter(out, "8859_1"));
  376.             if (header != null) 
  377.                 awriter.write('#' + header + '\n');
  378.            
  379.             awriter.write('#' + new Date().toString() + '\n');
  380.  
  381.             for (Enumeration e = keys(); e.hasMoreElements();) {
  382.                 String key = (String)e.nextElement();
  383.                 String val = (String)get(key);
  384.                 key = saveConvert(key);
  385.                 val = saveConvert(val);
  386.                 awriter.write(key + '=' + val + '\n');
  387.             }
  388.             awriter.flush();
  389.         } catch (IOException e) {
  390.         }
  391.     }
  392.  
  393.     /**
  394.      * Searches for the property with the specified key in this property list.
  395.      * If the key is not found in this property list, the default property list,
  396.      * and its defaults, recursively, are then checked. The method returns
  397.      * <code>null</code> if the property is not found.
  398.      *
  399.      * @param   key   the property key.
  400.      * @return  the value in this property list with the specified key value.
  401.      * @see     java.util.Properties#defaults
  402.      */
  403.     public String getProperty(String key) {
  404.     Object oval = super.get(key);
  405.     String sval = (oval instanceof String) ? (String)oval : null;
  406.     return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
  407.     }
  408.  
  409.     /**
  410.      * Searches for the property with the specified key in this property list.
  411.      * If the key is not found in this property list, the default property list,
  412.      * and its defaults, recursively, are then checked. The method returns the
  413.      * default value argument if the property is not found.
  414.      *
  415.      * @param   key            the hashtable key.
  416.      * @param   defaultValue   a default value.
  417.      *
  418.      * @return  the value in this property list with the specified key value.
  419.      * @see     java.util.Properties#defaults
  420.      */
  421.     public String getProperty(String key, String defaultValue) {
  422.     String val = getProperty(key);
  423.     return (val == null) ? defaultValue : val;
  424.     }
  425.  
  426.     /**
  427.      * Returns an enumeration of all the keys in this property list, including
  428.      * the keys in the default property list.
  429.      *
  430.      * @return  an enumeration of all the keys in this property list, including
  431.      *          the keys in the default property list.
  432.      * @see     java.util.Enumeration
  433.      * @see     java.util.Properties#defaults
  434.      */
  435.     public Enumeration propertyNames() {
  436.     Hashtable h = new Hashtable();
  437.     enumerate(h);
  438.     return h.keys();
  439.     }
  440.  
  441.     /**
  442.      * Prints this property list out to the specified output stream. 
  443.      * This method is useful for debugging. 
  444.      *
  445.      * @param   out   an output stream.
  446.      */
  447.     public void list(PrintStream out) {
  448.     out.println("-- listing properties --");
  449.     Hashtable h = new Hashtable();
  450.     enumerate(h);
  451.     for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  452.         String key = (String)e.nextElement();
  453.         String val = (String)h.get(key);
  454.         if (val.length() > 40) {
  455.                 val = val.substring(0, 37) + "...";
  456.         }
  457.         out.println(key + "=" + val);
  458.     }
  459.     }
  460.  
  461.     /**
  462.      * Prints this property list out to the specified output stream. 
  463.      * This method is useful for debugging. 
  464.      *
  465.      * @param   out   an output stream.
  466.      * @since   JDK1.1
  467.      */
  468.     /*
  469.      * Rather than use an anonymous inner class to share common code, this
  470.      * method is duplicated in order to ensure that a non-1.1 compiler can
  471.      * compile this file.
  472.      */
  473.     public void list(PrintWriter out) {
  474.     out.println("-- listing properties --");
  475.     Hashtable h = new Hashtable();
  476.     enumerate(h);
  477.     for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  478.         String key = (String)e.nextElement();
  479.         String val = (String)h.get(key);
  480.         if (val.length() > 40) {
  481.         val = val.substring(0, 37) + "...";
  482.         }
  483.         out.println(key + "=" + val);
  484.     }
  485.     }
  486.  
  487.     /**
  488.      * Enumerates all key/value pairs in the specified hastable.
  489.      * @param h the hashtable
  490.      */
  491.     private synchronized void enumerate(Hashtable h) {
  492.     if (defaults != null) {
  493.         defaults.enumerate(h);
  494.     }
  495.     for (Enumeration e = keys() ; e.hasMoreElements() ;) {
  496.         String key = (String)e.nextElement();
  497.         h.put(key, get(key));
  498.     }
  499.     }
  500.  
  501.     /**
  502.      * Convert a nibble to a hex character
  503.      * @param    nibble    the nibble to convert.
  504.      */
  505.     private static char toHex(int nibble) {
  506.     return hexDigit[(nibble & 0xF)];
  507.     }
  508.  
  509.     /** A table of hex digits */
  510.     private static char[] hexDigit = {
  511.     '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
  512.     };
  513. }
  514.