package AsciiDatabase;
import java.io.*;

/* AsciiDatabase/RCFile.java
 *
 * Copyright (c) 2003 Chris Studholme <chris.studholme@utoronto.ca>
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See COPYING for more information.
 */

/**
 * <p>Class for reading configuration files in the following format:<pre>
 *    var1=value1
 *    var2=value2
 *    [header1]
 *    var3=value3
 *    var4=value4
 *    [header2]
 *    ... </pre>
 *
 * @author chris.studholme@utoronto.ca
 */
public class RCFile {

  private File file;
  private BufferedReader reader;
  private String line=null;
  private boolean repeatLine=false;

  private String lastproperty=null;

   
  /**
   * Opens the configuration file.
   *
   * @param afile configuration file
   * @exception IOException if there is an error reading from the file
   */
  public RCFile(File afile) throws IOException {
    file = afile;
    reader = new BufferedReader(new FileReader(file));
  }


  /**
   * Close the configuration file.
   */
  public void close() {
    try {
      reader.close();
    }
    catch (Exception e) {
    }
    reader=null;
  }

  /**
   * Close the configuration file.  Just calls close().
   */
  protected void finalize() {
    close();
  }

  /**
   * Reset to the beginning of the file.
   *
   * @exception IOException if there is an error reading from the file
   */
  public void ResetFile() throws IOException {
    reader = new BufferedReader(new FileReader(file));
    lastproperty=null;
    line=null;
    repeatLine=false;
  }

  
  /**
   * Read a single line of the file.  If the repeatLine flag is set,
   * the already loaded line is returned.
   *
   * @return String object containing the line
   * @exception IOException if there is an error reading from the file
   */
  private String readline() throws IOException {
    if ((line!=null)&&(repeatLine)) {
      repeatLine=false;
      return line;
    }
    repeatLine=false;
    line=reader.readLine();
    return line==null ? null : line.trim();
  }


  /**
   * Push back the last line read so it can be read again.
   */
  private void unreadline() {
    if (line!=null)
      repeatLine=true;
  }

   
  /**
   * Reads lines from the file (starting at the current position) until 
   * a header is found.  A header
   * is denoted by '[header]'.  There may be whitespace surrounding
   * the name of the header, but not before the [.
   *
   * @return name of header found, or null if no header was found
   * @exception IOException if there is an error reading from the file
   */
  public String FindNextHeader() throws IOException {
    lastproperty=null;

    do {

      // find next header
      readline();
      if (line==null) break;
      if (!line.startsWith("["))
	continue;

      // make sure we have closing ']'
      if (line.indexOf(']')<2)
	continue;
      
      return line.substring(1,line.indexOf(']')).trim();
      
    } while (true);
    
    return null;
  }

   
  /**
   * Scan through the file (from the beginning) for a particular header.
   *
   * @param header the particular header required
   * @return true if the required header was found, false otherwise
   * @exception IOException if there is an error reading from the file
   */
  // returns true if the header was found
  public boolean FindHeader(String header) throws IOException {
    ResetFile();
    do {
      // find next header
      String result = FindNextHeader();
      if (result==null)
	break;

      // check if header matches
      if (result.equalsIgnoreCase(header)) 
	return true;

    } while (true);
    
    return false;
  }


  /**
   * Returns the property name (name before equal sign) of the
   * last value read with ReadNextCharValue().
   *
   * @return String containing property name
   */
  public String LastProperty() {
    return lastproperty;
  }


  /**
   * Searches the file for the next value (property=value pair).  If
   * found, the value is returned and the property name can be retrieved
   * with LastProperty().  If a new header is found, null is returned
   * and the file is left positioned on the new header.
   *
   * @return String value found, null otherwise
   * @exception IOException if there is an error reading from the file
   */
  public String ReadNextCharValue() throws IOException {
    lastproperty=null;
    do {
      // find next value
      readline();
      if (line==null) break;
      if (line.startsWith("[")) {
	unreadline();
	break;
      }
      if (line.indexOf('=')<1)
	continue;

      lastproperty=line.substring(0,line.indexOf('=')).trim();
      return line.substring(line.indexOf('=')+1).trim();

    } while (true);

    return null;
  }


  /**
   * Searches the file (starting at the current position) for a
   * specific value (property=value pair).  The value is returned if
   * found.  null is returned if the property could not be found before
   * the next header (or end of file) was found.
   *
   * @param field property name required
   * @return String value found, null otherwise
   * @exception IOException if there is an error reading from the file
   */
  // returns dest if successful, null otherwise
  public String ReadNextCharValue(String field) throws IOException {
    do {
      lastproperty=null;

      // find next value
      String result=ReadNextCharValue();
      if (result==null)
	break;

      if (lastproperty.equalsIgnoreCase(field))
	return result;

    } while (true);
    return null;
  }

  
  /**
   * Searches for a specific value (property=value pair) under
   * a particular header.  The file is searched from the beginning.  
   *
   * @param field property required
   * @param header section containing property
   * @return String value if found, null otherwise
   * @exception IOException if there is an error reading from the file
   */
  public String ReadCharValue(String field, String header) 
    throws IOException {
    if (!FindHeader(header))
      return null;
    return ReadNextCharValue(field);
  }


  /**
   * Searches for a specific value (property=value pair) at the
   * beginning of the file (before the first header is found).  
   *
   * @param field required property
   * @return String value if found, null otherwise
   * @exception IOException if there is an error reading from the file
   */
  public String ReadCharValue(String field) throws IOException {
    ResetFile();
    return ReadNextCharValue(field);
  }
   
};

