Logo Search packages:      
Sourcecode: cacao-oj6 version File versions

PropertyArray.java

/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.util;

import java.util.Enumeration;
import java.util.Vector;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

/**
 * A space-efficient string to string map.
 * This class is similar to java.util.Properties.
 * For this class, space is more important than speed.  Use this class when
 * you care much more about wasted space than wasting time doing reference
 * juggling in memory.  Arrays in this class must correspond to this format:
 * <br>
 * <Pre>
 * {"key1", "value1", "key2", "value2", ...}
 * </Pre>
 */

 // This code was derived from that in com.sun.javatest.util.Properties.

00049 public class PropertyArray {
    /**
     * A class used to report problems that may occur when using PropertyArray.
     */
00053     public static class PropertyArrayError extends Error {
      /**
       * Create a PropertyArrayError object.
       */
00057         public PropertyArrayError() {
            super();
        }

      /**
       * Create a PropertyArrayError object.
       * @param msg a detail message for the error
       */
00065         public PropertyArrayError(String msg) {
            super(msg);
        }
    }

    /** 
     * Create a mutable object.
     */
00073     public PropertyArray() {
        locked = false;
    }

    /** 
     * Create a mutable object.
     * @param initSize the initial capacity of the array
     */
00081     public PropertyArray(int initSize) {
        // This method probably should be some kind of optimization, but if
        // we pre-create the array, how will be know the size?  Another data
        // member I suppose...
        locked = false;
    }

    /** 
     * Create a immutable object, from data read from on a stream in
     * the format of a standard Java properties file.
     * @param in the stream from which to read the properties
     * @throws IOException if a problem occurred while reading the data
     */
00094     public PropertyArray(Reader in) throws IOException {
        dataA = load(in);
        locked = true;
    }

    /** 
     * Create a immutable PropertyArray object from a standard Properties object.
     * @param props the object from which to initialize the array
     */
00103     public PropertyArray(Properties props) {
        dataA = getArray(props);

        locked = true;
    }

    /** 
     * Create a immutable PropertyArray object from data in a compact array of
     * names and values.
     * @param data an array containing pairs of entries: even-numbered entries 
     * identify the names of properties, odd-numbered entries give the value
     * for the preceding property name.
     */
00116     public PropertyArray(String[] data) {
        locked = true;
        dataA = new String[data.length];

        // shallow copy the array
        for (int i = 0; i < data.length; i++) {
            dataA[i] = data[i];
        }
    }

    // --------------- STATIC METHODS ----------------

    /**
     * Get a compact array containing the names and values of entries
     * from a standard Properties object.
     * @param props the Properties object from which to get the data
     * @return an array containing the names of the properties in even-numbered
     * entries, and the corresponding values in the adjacent odd-numbered entries
     */ 
00135     public static String[] getArray(Properties props) {
        Enumeration values = props.elements();
        Enumeration keys = props.keys();
        Vector data = new Vector(props.size(),2);

        for (; keys.hasMoreElements() ;) {
            insert(data, (String)(keys.nextElement()),
                   (String)(values.nextElement()));
        }

        String[] arr = new String[data.size()];
        data.copyInto(arr);

        return arr;
    }

    /**
     * Add a mapping to an array, returning a new array.
     * @param data The array to which to the new array is to be added.  May be null.
     * @param key the name of the new value to be added
     * @param value the new value to be added
     * @return an array with the new element added
     * @exception PropertyArrayError May be thrown if a null key or value is
     *            supplied.
     */
00160     public static String[] put(String[] data, String key, String value) {
        Vector vec;
        String[] arr;
        String old = null;

        if (key == null || value == null) {
            // i'd like to make null values legal but...
            throw new PropertyArrayError(
                "A key or value was null.  Null keys and values are illegal");
        }

        if (data == null) {
            data = new String[0];
        }

        if (key == null)
            arr = data;
      else {
            vec = copyOutOf(data);
            old = insert(vec, key, value);
            arr = new String[vec.size()];
            vec.copyInto(arr);
        }

        return arr;
    }

    /**
     * Get a named value from the array of properties.
     * If the given data array is null or zero length, null is returned.
     * If the key paramter is null, null will be returned, no error will
     * occur.
     * @param data an array containing sequential name value pairs
     * @param key the name of the property to be returned
     * @return the value of the named entry, or null if not found
     */
00196     public static String get(String[] data, String key) {
        if (data == null || data.length == 0 || key == null) {
            return null;
        }
        
      int lower = 0;
      int upper = data.length - 2;
      int mid;

      if (upper < 0)
          return null;

      String last = data[upper];
      int cmp = key.compareTo(last);
      if (cmp > 0)
          return null;

      while (lower <= upper) {
          // in next line, take care to ensure that mid is always even
          mid = lower + ((upper - lower) / 4) * 2;
          String e = data[mid];
          cmp = key.compareTo(e);
          if (cmp < 0) 
            upper = mid - 2;
          else if (cmp > 0) 
            lower = mid + 2;
          else 
            return data[mid+1];
      }   

      // did not find an exact match 
      return null;
    }

    /**
     * Remove an entry from an array of properties.
     * @param data an array of sequential name value properties
     * @param key the name of the entry to be removed
     * @return an array that does not contain the named property
     */
00236     public static String[] remove(String[] data, String key) {
        Vector vec = copyOutOf(data);
      int lower = 0;
      int upper = vec.size() - 2;
      int mid = 0;
        String old = null;

      if (upper < 0) {
            // no data yet
            return data;
      }

        // goes at the end
      String last = (String)vec.elementAt(upper);
      int cmp = key.compareTo(last);
      if (cmp > 0) {
          return data;
      }

      while (lower <= upper) {
          // in next line, take care to ensure that mid is always even
          mid = lower + ((upper - lower) / 4) * 2;
          String e = (String)(vec.elementAt(mid));
          cmp = key.compareTo(e);
          if (cmp < 0) {
            upper = mid - 2;
          } 
          else if (cmp > 0) {
            lower = mid + 2;
          } 
          else {
                // strings equal, zap key and value
                vec.removeElementAt(mid);
                old = (String)(vec.elementAt(mid));
                vec.removeElementAt(mid);
                break;
            }
      }

        String[] outData = new String[vec.size()];
        vec.copyInto(outData);
        return outData;
    }

    /**
     * Get a standard Properties object from an array of properties.
     * @param data an array of sequential name value properties
     * @return a Properties object containing data from the array
     */
00285     public static Properties getProperties(String[] data) {
        Properties props = null;

        if (data == null || data.length == 0) {
            props = new Properties();
        } 
      else {
            props = new Properties();

            for (int i = 0; i < data.length; i+=2) {
                props.put(data[i], data[i+1]);
            }   // for
        }   // else
        return props;
    }

    /**
     * Write an array of properties to a stream.
     * The data is written using the format for standard Java property files.
     * @param data an array of sequential name value properties
     * @param out a stream to which to write the data
     * @throws IOException if a problem occurred while writing to the stream
     * @see #load(Reader)
     */
00309     public static void save(String[] data, Writer out) throws IOException {
        if (data == null || data.length == 0) {
            return;
        }
        
      for (int whichPair = 0; whichPair < data.length; whichPair+=2) {
          out.write(data[whichPair]);
          out.write('=');

          String val = data[whichPair+1];
          int len = val.length();
          boolean empty = false;

          for (int i = 0 ; i < len ; i++) {
            int ch = val.charAt(i);

            switch (ch) {
              case '\\': out.write('\\'); out.write('\\'); break;
              case '\t': out.write('\\'); out.write('t'); break;
              case '\n': out.write('\\'); out.write('n'); break;
              case '\r': out.write('\\'); out.write('r'); break;

              default:
                if ((ch < ' ') || (ch >= 127) || (empty && (ch == ' '))) {
                  out.write('\\');
                  out.write('u');
                  out.write(toHex((ch >> 12) & 0xF));
                  out.write(toHex((ch >>  8) & 0xF));
                  out.write(toHex((ch >>  4) & 0xF));
                  out.write(toHex((ch >>  0) & 0xF));
                } 
                else {
                  out.write(ch);
                }
            }   // switch
            empty = false;
          }   // for

          out.write(lineSeparator);
      }   // for
    }

    /**
     * Read an array of properties from an input stream.
     * The data will be read according to the standard format for Java
     * property files.
     * @param in the stream from which to read the data
     * @return an array of sequential name value properties
     * @throws IOException if an error occurred while reading the data
     * @see #save(String[], Writer)
     */
00360     public static String[] load(Reader in) throws IOException {
        Vector v = new Vector();
      int ch = 0;

        //// eat any preceding whitespace
        //do {
        //    ch = in.read();
        //} while ((ch == '\n') || (ch == '\r') || (ch == '\t') || (ch == ' '));

      ch = '\n';  
      // pretend to have read newline, so that if an initial
      // blank line is read, it is treated as the end of an empty props object

    parsing:
      while (true) { 
          // parse until EOF or a blank line
          switch (ch) {
          case -1:
            // EOF
            break parsing;
            
          case '#':
          case '!':
            // comment: skip to end of line
            do {
                ch = in.read();
            } while ((ch >= 0) && (ch != '\n') && (ch != '\r'));
            continue;
            
          case '\n':
          case '\t':
          case '\r':
          case ' ':
            // skip whitespace but count newlines along the way;
            // if there is more than one, we're done. This is intended
            // to work on both \n and \r\n systems
            int lines = 0;
            do {
                if (ch == '\n' && (++lines > 1))
                  break parsing;
                ch = in.read();
            } while ((ch == '\n') || (ch == '\r') || (ch == '\t') || (ch == ' '));
            continue;

          default:
            // key=value
            // start by reading the key; stop at newline (unless escaped in value)
            StringBuffer key = new StringBuffer();
            while ((ch >= 0) && (ch != '=') && (ch != ':') && 
                   (ch != ' ') && (ch != '\t') && (ch != '\n') && (ch != '\r')) {
                key.append((char)ch);
                ch = in.read();
            }
            while ((ch == ' ') || (ch == '\t')) {
                ch = in.read();
            }
            if ((ch == '=') || (ch == ':')) {
                ch = in.read();
            }
            while ((ch == ' ') || (ch == '\t')) {
                ch = in.read();
            }
            
            // Read the value
            StringBuffer val = new StringBuffer();
            while ((ch >= 0) && (ch != '\n') && (ch != '\r')) {
                int next = 0;
                if (ch == '\\') {
                  switch (ch = in.read()) {
                  case '\r':
                      if (((ch = in.read()) == '\n') || (ch == ' ') || (ch == '\t')) {
                        // fall thru to '\n' case
                      } 
                      else 
                        continue;
                  case '\n': 
                      while (((ch = in.read()) == ' ') || (ch == '\t')) 
                        /*do-nothing*/;
                      continue;
                  case 't': ch = '\t'; next = in.read(); break;
                  case 'n': ch = '\n'; next = in.read(); break;
                  case 'r': ch = '\r'; next = in.read(); break;
                  case 'u': {
                      while ((ch = in.read()) == 'u');
                      int d = 0;
                  loop:
                      for (int i = 0 ; i < 4 ; i++) {
                        next = in.read();
                        switch (ch) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                            d = (d << 4) + ch - '0';
                            break;
                        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                            d = (d << 4) + 10 + ch - 'a';
                            break;
                        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
                            d = (d << 4) + 10 + ch - 'A';
                            break;
                        default:
                            break loop;
                        }     
                        ch = next;
                      }
                      ch = d;
                      break;
                  }
                  default: next = in.read(); break;
                  }
                } 
                else {
                  next = in.read();
                }
                val.append((char)ch);
                ch = next;
            }
            
            //System.out.println(key + " = '" + val + "'");
            insert(v, key.toString(), val.toString());
          }
      }

      String[] data = new String[v.size()];
      v.copyInto(data);
        return data;
    }

    /**
     * Enumerate the properties in an array.
     * @param props an array of sequential name value properties
     * @return an enumeration of the properties in the array
     */
00492     public static Enumeration enumerate(final String[] props) {
        return new Enumeration() {
            int pos = 0;

            public boolean hasMoreElements() {
                return (props != null && pos < props.length); 
            }

            public Object nextElement() {
                if (props == null || pos >= props.length) {
                   return null;
                }
            else {
                   String current = props[pos];
                   pos += 2;
                   return current;
                } 
            }
        };
    }

    // --------------- INSTANCE METHODS ----------------

    /**
     * Get the data in this PropertyArray as a standard Properties object.
     * @return a Properties object containing the same data as this PropertyArray
     */
00519     public Properties getProperties() {
        return getProperties(dataA);
    }

    /**
     * Check if the property array is mutable.
     * @return true if data can be stored in this array, and false otherwise
     */
00527     public boolean isMutable() {
        return !locked;
    }

    /**
     * Get the number of properties stored in the property array.
     * @return the number of properties stored in the property array
     */
00535     public int size() {
        if (dataA == null) {
            return 0;
        } 
      else {
            return dataA.length / 2;
        }
    }

    /**
     * Get the value of a named property.
     * @param key the name of the desired property
     * @return the value of the property, or null if it was not found
     */
00549     public String get(String key) {
        return get(dataA, key);
    }

    /**
     * Get a copy of the data in this PropertyArray.
     * @return a copy of the data, or null if there is no data.
     */
00557     public String[] getArray() {
        if (dataA == null || dataA.length == 0) {
            return null;
        }

        return shallowCopy(dataA);
    }

    /**
     * Put a property into the PropertyArray.
     * @param key the name of the property to be added
     * @param value the value of the property to be added
     * @return the previous value (if any) of this property
     * @throws PropertyArrayError if a null key or value is supplied.
     */
00572     public String put(String key, String value) {
        String old = null;

        if (locked == true) {
            throw new IllegalStateException("PropertyArray is immutable.");
        }

        if (key == null || value == null) {
            // i'd like to make null values legal but...
            throw new PropertyArrayError(
                "A key or value was null.  Null keys and values are illegal");
        }

        Vector vec = copyOutOf(dataA);
        old = insert(vec, key, value);
        dataA = new String[vec.size()];
        vec.copyInto(dataA);

        return old;
    }

    /**
     * Remove a property.
     * @param key the name of the property to be removed
     */
00597     public void remove(String key) {
        dataA = remove(dataA, key);
    }

    /**
     * Save the properties to a stream. The data is written using the format
     * for a standard Java properties file.
     * @param out the stream to which to write the data
     * @throws IOException if an error occured while writing the data
     */
00607     public void save(Writer out) throws IOException {
        save(dataA, out);
    }

    // --------------- PRIVATE ----------------

    /**
     * Put a new value in, overwriting existing values.
     *
     * @param vec Vector to add data to.  Must not be null!
     */
00618     private static String insert(Vector vec, String key, String value) {
      int lower = 0;
      int upper = vec.size() - 2;
      int mid = 0;
        String old = null;

      if (upper < 0) {
            // no data yet
          vec.addElement(key);
          vec.addElement(value);
          return old;
      }

        // goes at the end
      String last = (String)vec.elementAt(upper);
      int cmp = key.compareTo(last);
      if (cmp > 0) {
          vec.addElement(key);
          vec.addElement(value);
          return null;
      }

      while (lower <= upper) {
          // in next line, take care to ensure that mid is always even
          mid = lower + ((upper - lower) / 4) * 2;
          String e = (String)(vec.elementAt(mid));
          cmp = key.compareTo(e);
          if (cmp < 0) {
            upper = mid - 2;
          } 
          else if (cmp > 0) {
            lower = mid + 2;
          } 
          else {
                // strings equal
                vec.removeElementAt(mid);
                old = (String)(vec.elementAt(mid));
                vec.removeElementAt(mid);
                break;
            }
      }

      // did not find an exact match (we did not expect to) 
      // adjust the insert point
      if (cmp > 0)
          mid += 2;

      vec.insertElementAt(key, mid);
      vec.insertElementAt(value, mid+1);

        return old;
    }

    private static Vector copyOutOf(String[] data) {
        Vector vec = null;
        
        if (data == null) {
            vec = new Vector(0,2);
        } 
      else {
            vec = new Vector(data.length,2);

            for (int i = 0; i < data.length; i++) {
                vec.addElement(data[i]);    
            }   // for
        }

        return vec;
    }

    private static String[] shallowCopy(String[] arrIn) {
        if (arrIn == null) {
            return null;
        }

        String[] arrOut = new String[arrIn.length];

        // shallow copy the array
        for (int i = 0; i < arrIn.length; i++) {
            arrOut[i] = arrIn[i];
        }

        return arrOut;
    }

    /**
     * Convert a nibble to a hex character
     * @param   nibble  the nibble to convert.
     */
00707     private static char toHex(int nibble) {
        return hexDigit[(nibble & 0xF)];
    }

    /** A table of hex digits */
00712     private static char[] hexDigit = {
        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
    };

    private static final String lineSeparator = System.getProperty("line.separator");

    private String[] dataA;
    private boolean locked;


}
 

Generated by  Doxygen 1.6.0   Back to index