home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / util / Properties.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  21.7 KB  |  557 lines  |  [TEXT/CWIE]

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