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 a Properties or Ini object with defaults. If an Ini object,
  71.    *                   also makes a shallow copy of sections.
  72.    */
  73.   public Ini(final Properties properties) {
  74.     super(properties);

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

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

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

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

  125.   /**
  126.    * Read an Ini input stream.
  127.    *
  128.    * @param inStream the input stream to read.
  129.    * @throws IOException if unable to parse input stream.
  130.    */
  131.   public void load(InputStream inStream) throws IOException {
  132.     BufferedReader br = new BufferedReader(new InputStreamReader(inStream));

  133.     // keep track of current line number
  134.     int lineNumber = 0;
  135.     // line being parsed
  136.     String line;
  137.     // section being parsed
  138.     Properties section = null;

  139.     while ((line = br.readLine()) != null) {
  140.       lineNumber = lineNumber + 1;
  141.       line = line.trim();

  142.       // empty line or comment
  143.       if (line.length() == 0 || line.startsWith(COMMENT_START) || line.startsWith(ALTERNATE_COMMENT_START)) {
  144.         // ignore
  145.         continue;
  146.       }

  147.       // section
  148.       else if (line.startsWith(SECTION_START) && line.endsWith(SECTION_END)) {
  149.         // remove brackets
  150.         line = line.replace(SECTION_START, "");
  151.         line = line.replace(SECTION_END, "");
  152.         line = line.trim();

  153.         // store all properties in section
  154.         section = new Properties();
  155.         getSections().put(line, section);
  156.       }

  157.       // parse as property
  158.       else {
  159.         int index = line.indexOf("=");
  160.         if (index == -1) {
  161.           throw new IOException("Expected " + PROPERTY_DELIMITER + " on line " + lineNumber + ": '" + line + "'");
  162.         } else {
  163.           String[] parts = line.split(PROPERTY_DELIMITER, 2);
  164.           String key = parts[0].trim();
  165.           String value = parts[1].trim();
  166.           if (section != null) {
  167.             section.setProperty(key, value);
  168.           } else {
  169.             setProperty(key, value);
  170.           }
  171.         }
  172.       }

  173.     }

  174.     br.close();
  175.   }

  176.   /**
  177.    * Write an Ini format to a PrintWriter.
  178.    *
  179.    * @param props  properties to write.
  180.    * @param writer the writer that writes.
  181.    * @param header an optioal header that will appear in comments at the start of
  182.    *               the ini format.
  183.    * @throws IOException if unable to write output.
  184.    */
  185.   @SuppressWarnings("unchecked")
  186.   public static void write(final Properties props, final PrintWriter writer, String header) throws IOException {

  187.     if (header != null) {
  188.       // write the header
  189.       writer.write(new StringBuffer(COMMENT_START).append(" ")
  190.           .append(header.trim().replace("\n", "\n" + COMMENT_START + " ")).append("\n").toString());
  191.     }

  192.     // write properties
  193.     Iterator<String> iter = (Iterator<String>) Collections.list(props.propertyNames()).iterator();
  194.     while (iter.hasNext()) {
  195.       String key = iter.next();
  196.       writer.write(
  197.           new StringBuffer(key).append(PROPERTY_DELIMITER).append(props.getProperty(key)).append("\n").toString());
  198.     }

  199.     // write sections
  200.     if (props instanceof Ini) {
  201.       Ini ini = (Ini) props;
  202.       iter = ini.getSections().keySet().iterator();
  203.       while (iter.hasNext()) {
  204.         String sectionName = iter.next();
  205.         writer.write(new StringBuffer(SECTION_START).append(sectionName).append(SECTION_END).append("\n").toString());
  206.         write(ini.getSections().get(sectionName), writer, null);
  207.       }
  208.     }

  209.     // flush, but don't close
  210.     writer.flush();
  211.   }

  212.   /**
  213.    * Calls write(new PrintWriter(out), header).
  214.    */
  215.   public void store(OutputStream out, String header) throws IOException {
  216.     write(this, new PrintWriter(out), header);
  217.   }

  218.   /**
  219.    * Write properties to an OutputStream.
  220.    *
  221.    * @param out the OutputStream used for writing.
  222.    */
  223.   public void save(final OutputStream out) {
  224.     try {
  225.       store(out, null);
  226.     } catch (IOException e) {
  227.       e.printStackTrace();
  228.     }
  229.   }

  230.   /**
  231.    * Write properties to a PrintStream.
  232.    *
  233.    * @param out the PrintStream used for writing.
  234.    */
  235.   public void list(PrintStream out) {
  236.     try {
  237.       store(out, null);
  238.     } catch (IOException e) {
  239.       e.printStackTrace();
  240.     }
  241.   }

  242.   /**
  243.    * Write properties to a PrintWriter.
  244.    *
  245.    * @param out the PrintWriter used for writing.
  246.    */
  247.   public void list(PrintWriter out) {
  248.     try {
  249.       write(this, out, null);
  250.     } catch (IOException e) {
  251.       e.printStackTrace();
  252.     }
  253.   }

  254. }