Ini.java

  1. /*
  2.  * Ini
  3.  *
  4.  * $Id$
  5.  * $URL$
  6.  */
  7. package gov.usgs.util;

  8. import java.io.BufferedReader;
  9. import java.io.InputStreamReader;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.OutputStream;
  13. import java.io.PrintStream;
  14. import java.io.PrintWriter;

  15. import java.util.Collections;
  16. import java.util.Iterator;
  17. import java.util.Properties;
  18. import java.util.HashMap;

  19. /**
  20.  * Ini is a Properties that supports sections.
  21.  *
  22.  * Format Rules:
  23.  * <ul>
  24.  * <li>Empty lines are ignored.
  25.  * <li>Leading and trailing white space are ignored.
  26.  * <li>Comments must be on separate lines, and begin with '#' or ';'.
  27.  * <li>Properties are key value pairs delimited by an equals: key = value
  28.  * <li>Section Names are on separate lines, begin with '[' and end with ']'. Any
  29.  * whitespace around the brackets is ignored.
  30.  * <li>Any properties before the first section are in the "null" section
  31.  * </ul>
  32.  *
  33.  * Format Example:
  34.  *
  35.  * <pre>
  36.  * #comment about the global
  37.  * global = value
  38.  *
  39.  * # comment about this section
  40.  * ; another comment about this section
  41.  * [ Section Name ]
  42.  * section = value
  43.  * </pre>
  44.  *
  45.  */
  46. public class Ini extends Properties {

  47.     /** Serialization version. */
  48.     private static final long serialVersionUID = 1L;

  49.     /** Section names map to Section properties. */
  50.     private HashMap<String, Properties> sections = new HashMap<String, Properties>();

  51.     /** String for representing a comment start */
  52.     public static final String COMMENT_START = ";";
  53.     /** String for representing an alternate comment start */
  54.     public static final String ALTERNATE_COMMENT_START = "#";

  55.     /** String to represent a section start */
  56.     public static final String SECTION_START = "[";
  57.     /** String to represent a section end */
  58.     public static final String SECTION_END = "]";
  59.     /** String to delimit properties */
  60.     public static final String PROPERTY_DELIMITER = "=";

  61.     /**
  62.      * Same as new Ini(null).
  63.      */
  64.     public Ini() {
  65.         this(null);
  66.     }

  67.     /**
  68.      * Construct a new Ini with defaults.
  69.      *
  70.      * @param properties
  71.      *            a Properties or Ini object with defaults. If an Ini object,
  72.      *            also makes a shallow copy of sections.
  73.      */
  74.     public Ini(final Properties properties) {
  75.         super(properties);

  76.         if (properties instanceof Ini) {
  77.             sections.putAll(((Ini) properties).getSections());
  78.         }
  79.     }

  80.     /**
  81.      * @return the section properties map.
  82.      */
  83.     public HashMap<String, Properties> getSections() {
  84.         return sections;
  85.     }

  86.     /**
  87.      * Get a section property.
  88.      *
  89.      * @param section
  90.      *            the section, if null calls getProperty(key).
  91.      * @param key
  92.      *            the property name.
  93.      * @return value or property, or null if no matching property found.
  94.      */
  95.     public String getSectionProperty(String section, String key) {
  96.         if (section == null) {
  97.             return getProperty(key);
  98.         } else {
  99.             Properties props = sections.get(section);
  100.             if (props != null) {
  101.                 return props.getProperty(key);
  102.             } else {
  103.                 return null;
  104.             }
  105.         }
  106.     }

  107.     /**
  108.      * Set a section property.
  109.      *
  110.      * @param section
  111.      *            the section, if null calls super.setProperty(key, value).
  112.      * @param key
  113.      *            the property name.
  114.      * @param value
  115.      *            the property value.
  116.      * @return any previous value for key.
  117.      */
  118.     public Object setSectionProperty(String section, String key, String value) {
  119.         if (section == null) {
  120.             return setProperty(key, value);
  121.         } else {
  122.             Properties props = sections.get(section);
  123.             if (props == null) {
  124.                 // new section
  125.                 props = new Properties();
  126.                 sections.put(section, props);
  127.             }
  128.             return props.setProperty(key, value);
  129.         }
  130.     }

  131.     /**
  132.      * Read an Ini input stream.
  133.      *
  134.      * @param inStream
  135.      *            the input stream to read.
  136.      * @throws IOException
  137.      *         if unable to parse input stream.
  138.      */
  139.     public void load(InputStream inStream) throws IOException {
  140.         BufferedReader br = new BufferedReader(new InputStreamReader(inStream));

  141.         // keep track of current line number
  142.         int lineNumber = 0;
  143.         // line being parsed
  144.         String line;
  145.         // section being parsed
  146.         Properties section = null;

  147.         while ((line = br.readLine()) != null) {
  148.             lineNumber = lineNumber + 1;
  149.             line = line.trim();

  150.             // empty line or comment
  151.             if (line.length() == 0 || line.startsWith(COMMENT_START)
  152.                     || line.startsWith(ALTERNATE_COMMENT_START)) {
  153.                 // ignore
  154.                 continue;
  155.             }

  156.             // section
  157.             else if (line.startsWith(SECTION_START)
  158.                     && line.endsWith(SECTION_END)) {
  159.                 // remove brackets
  160.                 line = line.replace(SECTION_START, "");
  161.                 line = line.replace(SECTION_END, "");
  162.                 line = line.trim();

  163.                 // store all properties in section
  164.                 section = new Properties();
  165.                 getSections().put(line, section);
  166.             }

  167.             // parse as property
  168.             else {
  169.                 int index = line.indexOf("=");
  170.                 if (index == -1) {
  171.                     throw new IOException("Expected " + PROPERTY_DELIMITER
  172.                             + " on line " + lineNumber + ": '" + line + "'");
  173.                 } else {
  174.                     String[] parts = line.split(PROPERTY_DELIMITER, 2);
  175.                     String key = parts[0].trim();
  176.                     String value = parts[1].trim();
  177.                     if (section != null) {
  178.                         section.setProperty(key, value);
  179.                     } else {
  180.                         setProperty(key, value);
  181.                     }
  182.                 }
  183.             }

  184.         }

  185.         br.close();
  186.     }

  187.     /**
  188.      * Write an Ini format to a PrintWriter.
  189.      *
  190.      * @param props
  191.      *            properties to write.
  192.      * @param writer
  193.      *            the writer that writes.
  194.      * @param header
  195.      *            an optioal header that will appear in comments at the start of
  196.      *            the ini format.
  197.      * @throws IOException
  198.      *         if unable to write output.
  199.      */
  200.     @SuppressWarnings("unchecked")
  201.     public static void write(final Properties props, final PrintWriter writer,
  202.             String header) throws IOException {

  203.         if (header != null) {
  204.             // write the header
  205.             writer.write(new StringBuffer(COMMENT_START).append(" ").append(
  206.                     header.trim().replace("\n", "\n" + COMMENT_START + " "))
  207.                     .append("\n").toString());
  208.         }

  209.         // write properties
  210.         Iterator<String> iter = (Iterator<String>) Collections.list(
  211.                 props.propertyNames()).iterator();
  212.         while (iter.hasNext()) {
  213.             String key = iter.next();
  214.             writer.write(new StringBuffer(key).append(PROPERTY_DELIMITER)
  215.                     .append(props.getProperty(key)).append("\n").toString());
  216.         }

  217.         // write sections
  218.         if (props instanceof Ini) {
  219.             Ini ini = (Ini) props;
  220.             iter = ini.getSections().keySet().iterator();
  221.             while (iter.hasNext()) {
  222.                 String sectionName = iter.next();
  223.                 writer.write(new StringBuffer(SECTION_START)
  224.                         .append(sectionName).append(SECTION_END).append("\n")
  225.                         .toString());
  226.                 write(ini.getSections().get(sectionName), writer, null);
  227.             }
  228.         }

  229.         // flush, but don't close
  230.         writer.flush();
  231.     }

  232.     /**
  233.      * Calls write(new PrintWriter(out), header).
  234.      */
  235.     public void store(OutputStream out, String header) throws IOException {
  236.         write(this, new PrintWriter(out), header);
  237.     }

  238.     /**
  239.      * Write properties to an OutputStream.
  240.      *
  241.      * @param out
  242.      *            the OutputStream used for writing.
  243.      */
  244.     public void save(final OutputStream out) {
  245.         try {
  246.             store(out, null);
  247.         } catch (IOException e) {
  248.             e.printStackTrace();
  249.         }
  250.     }

  251.     /**
  252.      * Write properties to a PrintStream.
  253.      *
  254.      * @param out
  255.      *            the PrintStream used for writing.
  256.      */
  257.     public void list(PrintStream out) {
  258.         try {
  259.             store(out, null);
  260.         } catch (IOException e) {
  261.             e.printStackTrace();
  262.         }
  263.     }

  264.     /**
  265.      * Write properties to a PrintWriter.
  266.      *
  267.      * @param out
  268.      *            the PrintWriter used for writing.
  269.      */
  270.     public void list(PrintWriter out) {
  271.         try {
  272.             write(this, out, null);
  273.         } catch (IOException e) {
  274.             e.printStackTrace();
  275.         }
  276.     }

  277. }