/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package org.jgap.data.config;
import java.util.*;
import java.io.*;
/**
* This class provides an interface to the configuration information to create
* a JGAP Configuration GUI.
*
* @author Siddhartha Azad
* @since 2.4
*
*/
public class MetaConfig {
// file to read the GUI Configuration information from
private static final String METACON_FILENAME = "jgap-meta.con";
private static final String CN = "MetaConfig";
// singleton instance
private static MetaConfig instance;
// ClassName-ConfigProperty mapping
private Hashtable m_metaMap = new Hashtable();
// state for the parser
private int m_state;
private static final int INIT = 0;
private static final int CLASS = 1;
private static final int PROPERTY = 2;
private static final int VALUES = 3;
// class name currently being handled
private String m_currName;
private ConfigProperty m_currProperty;
public static MetaConfig getInstance()
throws MetaConfigException, IOException {
if (null == instance) {
instance = new MetaConfig();
}
return instance;
}
private MetaConfig()
throws MetaConfigException, IOException {
m_state = MetaConfig.INIT;
init();
}
// public interface
/**
* Read the meta-config file and load it in memory.
* @param className the name of the class of which the properties are
* required
* @return the list of properties for this class, if class is registered,
* otherwise null
*
* @author Siddhartha Azad
* @since 2.4
* */
public List getConfigProperty(String className) {
return (List)m_metaMap.get(className);
}
/**
* Read the meta-config file and load it in memory.
* Having to read my own property file without using the Java Property
* class since I need to preserve the order of these properties, plus
* I have duplicate labels.
* @throws MetaConfigException
* @throws IOException
*
* @author Siddhartha Azad
* @since 2.4
*/
protected void init()
throws MetaConfigException, IOException {
Reader fr = getReader(METACON_FILENAME);
LineNumberReader lr = new LineNumberReader(fr);
String line = lr.readLine();
while (line != null) {
if (!this.isComment(line)) {
parseLine(line);
}
line = lr.readLine();
}
endState();
lr.close();
}
/**
* Returns a reader to a file
* @param a_filename the file to retrieve a reader for
* @throws IOException
* @return the Reader
*
* @author Klaus Meffert
* @since 3.0
*/
public Reader getReader(String a_filename) throws IOException {
File metaFile = new File(a_filename);
FileReader fr = new FileReader(metaFile);
return fr;
}
/**
* Check whether a line is a comment. Any line starting with a '#' is a
* comment.
* @return true if a line is a comment, false if it is not
*
* @author Siddhartha Azad
* @since 2.4
* */
private boolean isComment(String line) {
String tmpLine = line.trim();
StringBuffer sb = new StringBuffer(tmpLine);
if (sb.charAt(0) == '#') {
return true;
}
return false;
}
/**
* Parse a line. This method is dispatches lines to other methods, hence
* acting like a state machine.
* @throws MetaConfigException
*
* @author Siddhartha Azad
* @since 2.4
* */
private void parseLine(String a_line)
throws MetaConfigException {
String[] tokens = a_line.split("=");
if (tokens == null || tokens.length != 2)
throw new MetaConfigException(CN + ".parseLine():Exception while " +
"parsing " + METACON_FILENAME + " line " +
a_line + " is invalid");
if (m_state == MetaConfig.INIT && tokens[0].equals("class")) {
handleClass(tokens[1]);
}
else if (m_state == MetaConfig.CLASS && tokens[0].equals("property")) {
handleProperty(tokens[1]);
}
else if (m_state == MetaConfig.PROPERTY && tokens[0].equals("values")) {
handleValues(tokens[1]);
}
else if (m_state == MetaConfig.PROPERTY && tokens[0].equals("class")) {
handleClass(tokens[1]);
}
else if (m_state == MetaConfig.VALUES && tokens[0].equals("class")) {
handleClass(tokens[1]);
}
else if (m_state == MetaConfig.VALUES && tokens[0].equals("property")) {
handleProperty(tokens[1]);
}
else {
throw new MetaConfigException(CN + ".parseLine():Exception while "
+ "parsing " + METACON_FILENAME + " state "
+ m_state
+ " incompatible with line " + a_line);
}
}
/**
* Handle the state when a 'class' tag is found.
* @author Siddhartha Azad
* @since 2.4
* */
private void handleClass(final String a_token) {
m_state = MetaConfig.CLASS;
if (m_currProperty != null) {
add(m_currName, m_currProperty);
}
m_currProperty = new ConfigProperty();
m_currName = a_token;
}
/**
* Handle the state when a 'property' tag is found.
* @throws MetaConfigException
*
* @author Siddhartha Azad
* @since 2.4
* */
private void handleProperty(final String a_token)
throws MetaConfigException {
int prevState = m_state;
if (prevState == MetaConfig.VALUES) {
if (m_currProperty != null) {
add(m_currName, m_currProperty);
}
}
m_currProperty = new ConfigProperty();
m_state = MetaConfig.PROPERTY;
String[] tokens = a_token.split(",");
if (tokens.length < 2 || tokens.length > 3) {
throw new MetaConfigException("Invalid format of property line: " +
a_token);
}
m_currProperty.setName(tokens[0].trim());
m_currProperty.setWidget(tokens[1].trim());
if (tokens.length == 3) {
m_currProperty.setLabel(tokens[2]);
}
}
/**
* Handle the state when a 'values' tag is found.
* @param a_token the rhs of the values property
* @throws MetaConfigException
*
* @author Siddhartha Azad
* @since 2.4
*
* */
private void handleValues(final String a_token)
throws MetaConfigException {
m_state = MetaConfig.VALUES;
String[] tokens = a_token.split(",");
if (tokens.length == 0) {
throw new MetaConfigException("Invalid format of property line: " +
a_token);
}
for (int i = 0; i < tokens.length; i++) {
m_currProperty.addValue(tokens[i].trim());
}
}
/**
* Called once the EOF is encountered while parsing the file.
*
* @throws MetaConfigException if parsing ends in an invalid state
*
* @author Siddhartha Azad
* @since 2.4
* */
private void endState()
throws MetaConfigException {
if (m_state != MetaConfig.PROPERTY && m_state != MetaConfig.VALUES) {
throw new MetaConfigException("Invalid format of JGAP MetaConfig "
+ "file: " + METACON_FILENAME
+ "Ending in Invalid state : "
+ m_state);
}
if (m_currProperty != null) {
add(m_currName, m_currProperty);
}
}
/**
* Add a new ConfigProperty for a certain class to the hashtable of
* properties.
* @param currName name of the class to which the property belongs
* @param a_cp the ConfigProperty to be added to the class
*
* @author Siddhartha Azad
* @since 2.4
* */
private void add(final String currName, ConfigProperty a_cp) {
List props = (List) m_metaMap.get(currName);
if (null == props) {
props = Collections.synchronizedList(new ArrayList());
m_metaMap.put(currName, props);
}
props.add(a_cp);
}
}