GMCConfiguration.java
package edu.udel.cis.vsl.gmc;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* <p>
* A GMCConfiguration is composed of a number of GMCSection's, each of which
* encapsulates a set of key-value pairs, where the keys correspond to
* commandline parameters and the value is the value assigned to that parameter.
* In addition, there can be any number of "free arguments" that are not
* assigned to any parameter. The free arguments are always strings.
* </p>
*
* <p>
* A configuration is typically generated by parsing a command line, or at least
* the suffix of a command line after the initial command(s). The command line
* suffix <code>-verbose -errBound=10 filename.c</code>, for example, would
* yield a configuration with an anonymous GMCSection in which "verbose" is
* mapped to true, "errBound" is mapped to the integer 10, and with one free
* argument "fileName.c".
* </p>
*
* <p>
* A configuration has associated to it a set of options, which are specified
* when the configuration is instantiated. And each GMCSection has associated to
* it a configuration.
* </p>
*
* <p>
* Each option is an instance of class {@link Option}. An option has a name and
* a type. The type is one of STRING, INTEGER, DOUBLE, BOOLEAN, or MAP. The type
* determines the kind of value that can be assigned to an option. The first
* four are "scalar" types and take values of type String, Integer, Double, and
* Boolean, respectively.
* </p>
*
* <p>
* An option of MAP type may be assigned a value of type Map<String,Object>.
* This kind of option is provided for command line arguments such as
* <code>-inputX=10 -inputB=true -inputZ="hello"</code>. This constructs a map
* from String to Object which maps "X" to the Integer 10, "B" to the Boolean
* true, and "Z" to the String "hello". This map is the value that is assigned
* to the option named "input". In general, the values of the map can be any
* scalar values, and their types will be inferred from their format. For
* example 1.0 will be interpreted as a Double, 1 as an Integer, "1" (with
* quotes) a String.
* </p>
*
*
* @author Stephen F. Siegel
*
*/
public class GMCConfiguration implements Serializable {
// Instance fields...
/**
*
*/
private static final long serialVersionUID = 6956568041621532151L;
/**
* The reserved name of the anonymous GMCSection of a configuration.
*/
public static final String ANONYMOUS_SECTION = "anonymous";
/**
* The anonymous GMCSection of this configuration. Could be null.
*/
private GMCSection anonymousSection;
/**
* Map from option name to option, for all options associated to this
* configuration.
*/
private Map<String, Option> optionMap = new LinkedHashMap<>();
/**
* Map from section name to section, for all non-anonymous sections
* associated to this configuratsion.
*/
private Map<String, GMCSection> sectionMap = new LinkedHashMap<>();
public PrintStream out = System.out;
/**
* Should output be suppressed? Usually used in CIVL test Default: false
*/
private boolean isQuiet;
private boolean printTransition = false;
private boolean saveStates = true;
// Constructors...
/**
* Constructs new configuration with the set of options obtained from the
* given collection. The set of options associated to this configuration is
* determined from the given collection. Duplicates in the collection will
* be ignored. The new configuration will be empty: i.e., it will have no
* entries, i.e., nothing will be assigned to any option.
*
* @param options
* a collection of non-null options; duplicates will be ignored.
* The options must have distinct names
* @throws IllegalArgumentException
* if two options in the collection have the same name
*/
public GMCConfiguration(Collection<Option> options) {
for (Option option : options) {
if (optionMap.put(option.name(), option) != null)
throw new IllegalArgumentException(
"Saw two options named " + option.name());
}
anonymousSection = new GMCSection(this, ANONYMOUS_SECTION);
isQuiet = false;
}
// Helper methods...
/**
* Checks that the given option is associated to this configuration.
*
* @param option
* an Option
* @throws IllegalArgumentException
* if the given option is not associated to this configuration
*/
void checkContainsOption(Option option) {
Option actual = optionMap.get(option.name());
if (actual == null || !actual.equals(option))
throw new IllegalArgumentException("Option " + option.name()
+ " is not associated to this configuration");
}
/**
* Processes escape characters of the given string.
*
* @param string
* The string whose escape characters are to be processed.
* @return A string which is the result of processing the escape characters
* of the given string.
*/
private static String escapeString(String string) {
String result = string;
result = result.replace("\\", "\\" + "\\");
result = result.replace("\n", "\\" + "n");
result = result.replace("\t", "\\" + "t");
result = result.replace("\"", "\\" + "\"");
result = "\"" + result + "\"";
return result;
}
/**
* Prints the value of an option. Needs to handle escape characters for
* strings.
*
* @param out
* The print stream to be used.
* @param value
* The value to be printed.
*/
static void printValue(PrintStream out, Object value) {
if (value instanceof String) {
out.print(escapeString((String) value));
} else
out.print(value);
}
// Public methods...
/**
* Returns the set of options associated to this configuration. This returns
* all options associated to this configuration, not just those that have a
* value assigned to them.
*
* @return the set of options
*/
public Collection<Option> getOptions() {
return optionMap.values();
}
/**
* Returns the option with the given name associated to this configuration,
* or null if there is none.
*
* @param name
* the name of an option
* @return the option with that name
*/
public Option getOption(String name) {
return optionMap.get(name);
}
/**
* Returns the section with the given name, or null if there is none.
*
* @param name
* the name of the section
* @return the section with that name
*/
public GMCSection getSection(String name) {
if (this.anonymousSection != null
&& this.anonymousSection.getName().equals(name))
return this.anonymousSection;
return this.sectionMap.get(name);
}
/**
* Returns the anonymous section
*
* @return the anonymous sections
*/
public GMCSection getAnonymousSection() {
return this.anonymousSection;
}
/**
* Returns the number of NON-anonymous sections.
*
* @return the number of NON-anonymous sections.
*/
public int getNumSections() {
return this.sectionMap.size();
}
/**
* Updates the anonymous section with the given section. Also updates the
* configuration associates with the given section to be this configuration.
*
* @param section
* The section to be used as the anonymous section of this
* configuration.
*/
public void setAnonymousSection(GMCSection section) {
this.anonymousSection = section;
this.anonymousSection.setConfiguration(this);
}
public boolean isQuiet() {
return isQuiet;
}
public void setQuiet(boolean isQuiet) {
this.isQuiet = isQuiet;
}
/**
* Adds a section to the NON-anonymous section map. Also updates the
* configuration associates with the given section to be this configuration.
* <br>
* Precondition: <code>!section.getName().equals(DEFAULT_SECTION)</code>.
*
* @param section
* The section to be added
*/
public void addSection(GMCSection section) {
assert !section.getName().equals(ANONYMOUS_SECTION);
section.setConfiguration(this);
this.sectionMap.put(section.getName(), section);
}
/**
* Returns a deep copy of this configuration. The two configurations will
* share references to the same options, and to the same Strings, but not to
* anything else. As options and strings are immutable, this should not be a
* problem.
*
* @return deep copy of this configuration
*/
@Override
public GMCConfiguration clone() {
GMCConfiguration result = new GMCConfiguration(getOptions());
if (this.anonymousSection != null)
result.setAnonymousSection(anonymousSection.clone());
for (GMCSection section : sectionMap.values()) {
result.addSection(section.clone());
}
return result;
}
/**
* Returns an iterable object of the NON-anonymous sections.
*
* @return the iterable object of the NON-anonymous sections.
*/
public Iterable<GMCSection> getSections() {
return this.sectionMap.values();
}
/**
* Modifies this configuration by reading in the values of the given
* configuration and using those to set values of this one. Existing entries
* in this one may be overwritten in the process.
*
* @param that
* another configuration; the set of options associated to that
* should be a subset of the set of options associated to this
*/
public void read(GMCConfiguration that) {
if (that.anonymousSection != null)
this.anonymousSection = that.anonymousSection.clone();
else
this.anonymousSection = null;
for (GMCSection thatSection : that.getSections()) {
String name = thatSection.getName();
if (this.sectionMap.containsKey(name))
this.sectionMap.get(name).read(thatSection);
else
this.sectionMap.put(name, thatSection.clone());
}
}
/**
* Prints the current state of this configuration in a manner similar to
* what would appear on a commandline. Each scalar assignment appears on one
* line. At the end the free arguments are printed, one on each line.
*
* @param out
* print stream to which to print
*/
public void print(PrintStream out) {
if (this.anonymousSection != null)
anonymousSection.print(out);
for (GMCSection section : this.getSections())
section.print(out);
out.flush();
}
public boolean printTransitions() {
return printTransition;
}
public void setPrintTransition(boolean printTransitions) {
this.printTransition = printTransitions;
}
public void setPrintStream(PrintStream out) {
this.out = out;
}
public void setSaveStates(boolean saveStates) {
this.saveStates = saveStates;
}
public boolean getSaveStates() {
return saveStates;
}
}