package org.skymaps;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* StarFormat
is a program that takes star subtraction data
* compiled by StarSearch
and reformats it according to a given
* specification. The newly formatted files are outputted as text (.txt)
* files that have the same file names as the input files.
*
* It is important that the program StarSearch
be run first on
* a set of data (see the StarSearch
documentation for more
* information) so that data text files are available to
* StarFormat
for processing. Only files that have been generated
* by StarSearch
can be used as input files for
* StarFormat
, and the initialization file
* starsearchinit.xml
must remain invariant between calling these
* two programs.
*
*
* This program is meant to be a convenience program for formatting star data
* fetched by StarSearch
for analysis in Grapher or similar
* programs.
*
*
* StarFormat
is meant to be called in the following manner (from
* a console):
*
*
* % java -cp /zshare/smei/java/common org.skymaps.StarFormat [Options]
*
*
*
* Note that there may be a BASH alias for this program, such as
* starformat
.
*
*
* Options
* The following command-line options are allowed:
*
*
*
* -dest=directory
* - Instructs
StarFormat
to write text files to
* directory
. This must be a valid directory, either
* absolute or relative, and it must have write permissions enabled.
* If this option is not specified, then the default write directory
* is the user's current directory (i.e. the directory from which
* StarFormat
is called).
* -dir=directory
* - Instructs
StarFormat
to perform the search for input
* files in directory
. If this option is not specified,
* then the default starting directory is the current user directory
* (i.e. the directory from which the user calls
* StarFormat
). This must be a valid directory, either
* absolute or relative, and it must have read permissions enabled.
* -help
* - Displays a usage message to the user on standard output.
*
*
*
* If illegal or malformed command-line optiosn are entered by the user, then
* the program indicates the error and terminates normally.
*
*
* File Formats
* The input files must be generated by StarSearch
(see the
* StarSearch
documentation for information about this format).
* The output files are generated in a similar format. However, StarFormat
* gives the user the ability to choose which data entries (data fields) in
* the input files will be written to and in which order they will appear in
* the output files. This formatting information is controlled by the XML
* initialization file starsearchinit.xml
and its associated
* DTD file, both of which must be located in the same directory as
* StarFormat
.
*
*
* There is one special transformation that always occurs when
* StarFormat
is run. The YYYY_DOY_hhmmss
field
* of the input files is transformed from standard SMEI format to a special
* eight digit double-precision floating point number representing the day
* offset of the date from 2003_148_010505 (negative values may be generated
* if the date is before this "zero date"). This fractional day is stored
* under the field title "time". This transformation can be modified in
* one of two ways: the FormattedField entry in the initialization file
* corresponding to the SMEI date can be removed (in which case the date will
* not appear in the output files), or the StarFormat
code
* can be modified and recompiled.
*
*
* The XML Initialization File
*
*
* When StarFormat
is run, its initialization file
* starsearchinit.xml
is parsed. This file should be located
* in the same directory as StarFormat
and has a corresponding
* DTD file named starsearchinit.dtd
. The initialization file
* contains various kinds of information, but StarFormat
only
* parses information within the
* <Fields></Fields>
and
* <FormattedFields></FormattedFields>
tag pairs.
*
*
* The Fields
element contains Field
elements, which
* correspond to the data fields in the text files read by
* StarFormat
. These include the title of the data field (which
* is found at the top of its respective column in the inputted text files),
* the size of the field (in characters), and what type of data the field
* contains (string, int, float, floate, double, or double).
* StarFormat
reads in data fields from the inputted text files
* in the order they appear in the initialization file.
*
*
* The FormattedFields
element contains
* FormattedField
elements, which correspond to the data fields
* that will be written to the output text files that StarFormat
* generates. Required attributes of FormattedField
elements
* include the name of the formatted field (which will appear at the top of
* the field's respective column in the output files), the size of the
* field (in characters), and the name of the Field
that the
* FormattedField
element corresponds to (this name must match
* one of the Field
elements' names, case-sensitive).
* StarFormat
writes these formatted fields to the output
* text files in the order they appear in the initialization file.
*
*
* When StarFormat
is run, it parses each located input file
* in the given directory according to the number, type, size, and order of
* Field
elements in the initialization file. This information
* is transferred to the output files according to the number, size, order,
* and corresponding field identities of the FormattedField
* elements in the initialization file.
*
*
* Note that in order for the SMEI date conversion described above to work,
* the following entry must appear in the FormattedFields
section
* in the initialization file:
*
*
*
* <FormattedField name="time" size="10"
* correspondingTo="YYYY_DOY_hhmmss" />
*
*
*
* YYYY_DOY_hhmmss
must be the name of a Field
* element that denotes a standard SMEI date.
*
*
* @author Jordan T. Vaughan
* @version 1.0
* @see StarSearch
*/
public class StarFormat {
/**
* The default directory where the search for input files is performed.
*/
public static final String DEFAULT_DIRECTORY = ".";
/**
* The default directory where the output files are written to.
*/
public static final String DEFAULT_DESTINATION = ".";
/**
* The path and name of the XML initialization file (which should be in the
* same directory as the StarFormat .class file). Modify this value
* (that is, recompile the code with a new file) if the directory of the
* initialization file should change.
*/
public static final String INI_FILE = "/zshare/smei/java/common/org/" +
"skymaps/starsearchinit.xml";
private static final String DIR_OPTION = "-dir=";
private static final String DEST_OPTION = "-dest=";
private static final String HELP_OPTION = "-help";
/**
* The number of whitespace characters separating fields in the input
* and output files.
*/
public static final int FIELD_SEPARATION_SIZE = 1;
/* Quantities to subtract from converted dates.
*/
/**
* The year of the zero date.
*/
public static final int ZERO_TIME_YEAR = 2003;
/**
* The numerical day of the year of the zero date.
*/
public static final int ZERO_TIME_DOY = 148;
/**
* The hour of the day of the zero date.
*/
public static final int ZERO_TIME_HOUR = 1;
/**
* The minute of the hour of the zero date.
*/
public static final int ZERO_TIME_MINUTE = 5;
/**
* The second of the minute of the zero date.
*/
public static final int ZERO_TIME_SECOND = 5;
/**
* The decimal precision, not including the sign (if it is displayed),
* of the outputted date.
*/
public static final int TIME_DECIMAL_PRECISION = 8;
/**
* The program begins here.
*
* @param args the command line options to parse
* @since 1.0
*/
public static void main(String[] args) {
// Relevant local variables.
File searchDirectory = null;
File destinationDirectory = null;
Vector fields = null;
HashMap fieldIndicies = null;
Vector formattedFields = null;
HashMap> starEntries = null;
// Parse command-line options.
for(int counter = 0; counter < args.length; counter++) {
if(args[counter].length() >= DIR_OPTION.length() &&
args[counter].substring(0, DIR_OPTION.length()).
equalsIgnoreCase(DIR_OPTION)) {
// Setting the search directory to the user's choice.
if(searchDirectory != null) {
System.out.println("ERROR: Search directory specified " +
"more than once. Aborting.\n");
return;
}
args[counter] = args[counter].substring(DIR_OPTION.length());
if(args[counter].length() == 0) {
System.out.println("ERROR: No search directory given. " +
"Aborting.\n");
return;
}
searchDirectory = new File(args[counter]);
if(!searchDirectory.exists()) {
System.out.println("ERROR: Given directory does not " +
"exist -- " + args[counter] + "\n");
return;
}
if(!searchDirectory.isDirectory()) {
System.out.println("ERROR: Given directory is not a " +
"directory -- " + args[counter] + "\n");
return;
}
}
else if(args[counter].length() >= DEST_OPTION.length() &&
args[counter].substring(0, DEST_OPTION.length()).
equalsIgnoreCase(DEST_OPTION)) {
// Setting the destination directory to the user's choice.
if(destinationDirectory != null) {
System.out.println("ERROR: Destiantion directory " +
"specified more than once. Aborting.\n");
return;
}
args[counter] = args[counter].substring(DEST_OPTION.length());
if(args[counter].length() == 0) {
System.out.println("ERROR: No destination directory " +
"given. Aborting.\n");
return;
}
destinationDirectory = new File(args[counter]);
if(!destinationDirectory.exists()) {
System.out.println("ERROR: Given directory does not " +
"exist -- " + args[counter] + "\n");
return;
}
if(!destinationDirectory.isDirectory()) {
System.out.println("ERROR: Given directory is not a " +
"directory -- " + args[counter] + "\n");
return;
}
}
else if(args[counter].equalsIgnoreCase(HELP_OPTION)) {
// Display the usage message.
printUsageMessage();
return;
}
else {
// Unrecognized command-line option. Display an error message.
System.out.println("ERROR: Unrecognized option -- " +
args[counter] + "\nType \"starformat -help\" for " +
"options.\n");
return;
}
}
/* Create default directories if they were not already specified by the
* user via the command line.
*/
if(searchDirectory == null) {
searchDirectory = new File(DEFAULT_DIRECTORY);
if(!searchDirectory.exists()) {
System.out.println("FATAL ERROR: Default search directory " +
DEFAULT_DIRECTORY + " does not exist.\n" + "Aborting.\n");
return;
}
if(!searchDirectory.isDirectory()) {
System.out.println("FATAL ERROR: Default search directory " +
DEFAULT_DIRECTORY + " is not a directory.\n" +
"Aborting.\n");
return;
}
}
if(destinationDirectory == null) {
destinationDirectory = new File(DEFAULT_DESTINATION);
if(!destinationDirectory.exists()) {
System.out.println("FATAL ERROR: Default destination " +
"directory " + DEFAULT_DESTINATION + " does not exist.\n" +
"Aborting.\n");
return;
}
if(!destinationDirectory.isDirectory()) {
System.out.println("FATAL ERROR: Default destination " +
"directory " + DEFAULT_DESTINATION + " is not a " +
"directory.\nAborting.\n");
return;
}
}
/* Read in XML initialization file and retrieve entries. But first,
* build the XML DOM tree.
*/
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setIgnoringComments(true);
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
}
catch(ParserConfigurationException e) {
System.out.println("FATAL ERROR: Unable to instantiate an XML " +
"parser to parse the\ninitialization file. Aborting.\n");
return;
}
Document document = null;
try {
document = builder.parse(new File(INI_FILE));
}
catch(IOException e) {
System.out.println("ERROR: An IO error occured while attempting " +
"to parse the initialization file.\n Aborting.\n");
return;
}
catch(SAXException e) {
System.out.println("ERROR: The initialization file is not a " +
"well-formed XML file, or it\ndoes not conform to the " +
"specifications provided in the associated DTD. Aborting.\n");
return;
}
// Read the Field entries.
fields = new Vector();
fieldIndicies = new HashMap();
NodeList nodeList = ((Element)document.getDocumentElement().
getElementsByTagName("Fields").item(0)).
getElementsByTagName("Field");
int offset = 0;
for(int counter = 0; counter < nodeList.getLength(); counter++) {
Element field = (Element)nodeList.item(counter);
try {
Field newField = new Field(
field.getAttribute("name"),
Integer.parseInt(field.getAttribute("size")),
offset,
Field.getTypeFlag(field.getAttribute("type")));
fields.add(newField);
fieldIndicies.put(field.getAttribute("name"), newField);
offset += newField.getSize() + FIELD_SEPARATION_SIZE;
}
catch(NumberFormatException e) {
System.out.println("ERROR: Initialization file contains a " +
"Field entry with an illegal size.\nAborting.\n");
return;
}
}
// Read the FormattedField entries.
formattedFields = new Vector();
nodeList = ((Element)document.getDocumentElement().
getElementsByTagName("FormattedFields").item(0)).
getElementsByTagName("FormattedField");
for(int counter = 0; counter < nodeList.getLength(); counter++) {
Element formattedField = (Element)nodeList.item(counter);
try {
formattedFields.add(new FormattedField(
formattedField.getAttribute("name"),
Integer.parseInt(formattedField.getAttribute("size")),
fieldIndicies.get(formattedField.getAttribute(
"correspondingTo"))));
}
catch(NumberFormatException e) {
System.out.println("ERROR: Initialization file contains a " +
"FormattedField entry with an illegal size.\nAborting.\n");
return;
}
catch(NoSuchElementException e) {
System.out.println("ERROR: Initialization file contains a " +
"FormattedField entry with a nonexistent\ncorresponding " +
"Field entry. Aborting.\n");
return;
}
}
/* Now that we're done with initialization, locate files that match
* what we are looking for.
*/
String[] inputFileList = searchDirectory.list(
new InputFilenameFilter());
if(inputFileList == null) {
System.out.println("ERROR: An IO error occurred while attempting" +
" to access the\nfiles in the search directory. Aborting.");
return;
}
if(inputFileList.length == 0) {
System.out.println("ERROR: No star data text files with names " +
"in the form\nstarname_c(1+2+3)(e+s).txt were found!");
return;
}
starEntries = new HashMap>();
for(int counter = 0; counter < inputFileList.length; counter++) {
// Now, create an entries Vector for this star.
Vector entryVector = new Vector();
// Open up a reader and read all lines.
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(
new File(searchDirectory, inputFileList[counter])));
// Skip the header line.
reader.readLine();
// Now, read in lines until EOF.
String line = reader.readLine();
while(line != null) {
// Parse the current line into its respective entries.
StarEntry entry = new StarEntry();
for(int j = 0; j < fields.size(); j++) {
Field currentField = fields.get(j);
try {
entry.addEntry(currentField, line.substring(
currentField.getOffset(),
currentField.getOffset()+currentField.getSize()
).trim());
}
catch(StringIndexOutOfBoundsException e) {
/* We bumped into the end of the line. Just add
* whatever is at the end of the file.
*/
if(currentField.getOffset() < line.length()) {
entry.addEntry(currentField, line.substring(
currentField.getOffset(),
line.length()).trim());
}
else {
entry.addEntry(currentField, "");
}
}
}
entryVector.add(entry);
// Read another line.
line = reader.readLine();
}
reader.close();
}
catch(FileNotFoundException e) {
System.out.println("ERROR: File " + inputFileList[counter] +
" was located, but apparently does not exist.\n" +
"Skipping.\n");
continue;
}
catch(IOException e) {
System.out.println("ERROR: An IO error occurred while " +
"reading from the file " + inputFileList[counter] + ".\n" +
"Skipping.\n");
continue;
}
// Add the list of entries to the general HashMap for later access.
starEntries.put(inputFileList[counter], entryVector);
}
/* Now that we have read in all entries from all files, create the
* output text files and write the formatted data to it.
*/
for(String filename: starEntries.keySet()) {
// Create and open the file.
Formatter writer = null;
try {
writer = new Formatter(
new File(destinationDirectory, filename));
}
catch(FileNotFoundException e) {
System.out.println("ERROR: Unable to open " +
new File(destinationDirectory, filename).getPath() +
" for writing. Skipping.");
continue;
}
// Write the formatted header.
for(FormattedField field: formattedFields) {
writer.format("%" + field.getSize() + "s%" +
FIELD_SEPARATION_SIZE + "s", field.getName(), " ");
}
writer.format("\n");
// Write the entries, one at a time.
int numberOfEntries = 0;
for(StarEntry lineEntry: starEntries.get(filename)) {
for(FormattedField field: formattedFields) {
/* Is it the time field? If so, give it special treatment,
* for a conversion is required.
*/
if(field.getName().equalsIgnoreCase("time")) {
/* Convert the date from SMEI standard form
* (YYYY_DOY_HHMMSS) to floating-point day form
* (dddd.ddddd, where d is a digit).
*/
String SMEIDate = lineEntry.getEntry(
field.getCorrespondingField());
SMEIDate = SMEIDate.trim();
if(!java.util.regex.Pattern.matches(
"\\d\\d\\d\\d_\\d\\d\\d_\\d\\d\\d\\d\\d\\d",
SMEIDate)) {
System.out.println("ERROR: Corrupted SMEI date " +
"data found in file " + filename +
". Writing a blank date entry.");
writer.format("%" + field.getSize() + "s%1$" +
FIELD_SEPARATION_SIZE + "s", " ");
continue;
}
// Do the conversion.
double convertedTime = 0.0;
try {
/* Stage 1: Figure out the number of whole years
* between the zero date and the date given.
*/
int year = Integer.parseInt(SMEIDate.substring(
0, 4));
int middleYears = year - ZERO_TIME_YEAR - 1;
if(middleYears > 0) {
// Yes, we do have some middle years.
// Leap years
convertedTime += (middleYears +
(ZERO_TIME_YEAR % 4)) / 4 * 366;
// Regular years
convertedTime += (middleYears - (middleYears +
(ZERO_TIME_YEAR % 4)) / 4) * 365;
}
/* Stage 2: Figure out the number of days elapsed
* between the zero date and the end of that same
* year (if the year of the given date is not the
* same as the zero year).
*/
if(year > ZERO_TIME_YEAR) {
convertedTime += (ZERO_TIME_YEAR % 4 == 0 ?
366 : 365);
convertedTime -= ZERO_TIME_DOY +
(((ZERO_TIME_HOUR * 3600) +
(ZERO_TIME_MINUTE * 60) +
(ZERO_TIME_SECOND))
/ (24.0 * 60.0 * 60.0));
}
/* Stage 3: Figure out the number of days elapsed
* between the year of the given date and the
* given date.
*/
if(year >= ZERO_TIME_YEAR) {
convertedTime += Integer.parseInt(
SMEIDate.substring(5, 8));
convertedTime +=
((Integer.parseInt(SMEIDate.substring(
9, 11)) * 3600) +
(Integer.parseInt(SMEIDate.substring(
11, 13)) * 60) +
Integer.parseInt(SMEIDate.substring(
13, 15))) / (24.0 * 60.0 * 60.0);
if(year == ZERO_TIME_YEAR) {
// Do subtraction based on zero date.
convertedTime -= ZERO_TIME_DOY +
(((ZERO_TIME_HOUR * 3600) +
(ZERO_TIME_MINUTE * 60) +
(ZERO_TIME_SECOND))
/ (24.0 * 60.0 * 60.0));
}
}
else if(year < ZERO_TIME_YEAR) {
/* For some reason, the date entered has a year
* smaller than the zero time year. Take this
* into account and generate a negative time.
*/
/* Stage 1: Figure out the number of years
* between the two date years.
*/
convertedTime = 0.0;
middleYears = ZERO_TIME_YEAR - year - 1;
if(middleYears > 0) {
// Yes, we do have some middle years.
// Leap years
convertedTime -= (middleYears -
(ZERO_TIME_YEAR % 4) + 4) / 4 * 366;
// Regular years
convertedTime -= (middleYears -
(middleYears - (ZERO_TIME_YEAR % 4) +
4) / 4) * 365;
}
/* Stage 2: Subtract the zero date.
*/
convertedTime -= ZERO_TIME_DOY +
(((ZERO_TIME_HOUR * 3600) +
(ZERO_TIME_MINUTE * 60) +
(ZERO_TIME_SECOND))
/ (24.0 * 60.0 * 60.0));
/* Stage 3: Figure out the number of days
* elapsed between the year of the given
* date and the given date.
*/
convertedTime -= (year % 4 == 0 ?
366 : 365) - Integer.parseInt(
SMEIDate.substring(5, 8));
convertedTime -= ((24.0 * 60.0 * 60.0) -
((Integer.parseInt(SMEIDate.substring(
9, 11)) * 3600) +
(Integer.parseInt(SMEIDate.substring(
11, 13)) * 60) +
Integer.parseInt(SMEIDate.substring(
13, 15)))) / (24.0 * 60.0 * 60.0);
}
}
catch(NumberFormatException e) {
// This shouldn't happen. Do nothing.
}
// Output the result as an entry.
writer.format("%" + field.getSize() + "." +
TIME_DECIMAL_PRECISION + "g%" +
FIELD_SEPARATION_SIZE + "s",
convertedTime, " ");
}
else {
// Just write the entry and be done with it.
writer.format("%" + field.getSize() + "s%" +
FIELD_SEPARATION_SIZE + "s",
lineEntry.getEntry(field.getCorrespondingField()),
" ");
}
if(writer.ioException() != null) {
break;
}
}
writer.format("\n");
numberOfEntries++;
}
writer.close();
System.out.println("Wrote file: " + filename + " (" +
numberOfEntries + ")");
}
// We're done!
System.out.println("\nDone!");
}
/**
* Displays a usage message to standard output.
*
* @since 1.0
*/
public static void printUsageMessage() {
System.out.println(
"Usage:\n" +
" % starformat [Options]\n\n" +
"Options:\n" +
" -dir=directory Directory where input star data text " +
"files are\n" +
" located [default=" + DEFAULT_DIRECTORY +
"]\n" +
" -dest=destination Directory where formatted text files " +
"are outputted\n" +
" to [default=" + DEFAULT_DESTINATION +
"]\n" +
" -help Displays this usage message.\n\n" +
"Examples:\n" +
" starformat\n" +
" Searches for star data text files in the default search\n" +
" directory (" + DEFAULT_DIRECTORY + ") and outputs " +
"formatted\n" +
" text files to the default destination directory (" +
DEFAULT_DESTINATION + ")\n" +
" starformat -dir=/fake/directory -dest=/another/fake/dir\n" +
" Searches for star data text files in the directory\n" +
" /fake/directory and outputs formatted text files to the\n" +
" directory /another/fake/dir\n\n" +
"All star files found in the search directory are processed and\n"+
"the output file names are identical to those star data text\n" +
"files found. Configuration information can be modified in the\n"+
"XML file " + INI_FILE + ".");
}
/* Filename filter used to determine which files in a given directory
* are valid input files for StarFormat.
*
* @since 1.0
*/
private static class InputFilenameFilter implements FilenameFilter {
private static final String[] SUFFIXES =
{ "_c1e.txt", "_c1s.txt",
"_c2e.txt", "_c2s.txt",
"_c3e.txt", "_c3s.txt" };
private static final int SUFFIX_LENGTH = 8;
public InputFilenameFilter() { }
public boolean accept(File dir, String name) {
if(name == null || name.length() == 0) {
return false;
}
for(int counter = 0; counter < SUFFIXES.length; counter++) {
try {
if(name.substring(name.length() - SUFFIX_LENGTH).
equalsIgnoreCase(SUFFIXES[counter])) {
if(name.substring(0, name.length() - SUFFIX_LENGTH).
length() != 0) {
return true;
}
return false;
}
}
catch(StringIndexOutOfBoundsException e) {
return false;
}
}
return false;
}
}
/* ADT corresponding to a Field entry in the XML initialization file.
*
* @since 1.0
*/
private static class Field {
public static final int NULL = -1;
public static final int STRING = 0;
public static final int INT = 1;
public static final int FLOAT = 2;
public static final int FLOATE = 3;
public static final int DOUBLE = 4;
public static final int DOUBLEE = 5;
public static final String[] TYPE_STRINGS =
{ "string", "int", "float", "floate", "double", "doublee" };
private String name;
private int size;
private int type;
private int offset;
public Field(String name, int size, int offset, int type) {
if(name == null) {
throw new NullPointerException();
}
if(size <= 0 || offset < 0) {
throw new IllegalArgumentException();
}
this.name = name;
this.size = size;
this.offset = offset;
if(type != STRING && type != INT && type != FLOAT &&
type != FLOATE && type != DOUBLE && type != DOUBLEE) {
throw new IllegalArgumentException();
}
this.type = type;
}
public String getName() {
return name;
}
public int getOffset() {
return offset;
}
public int getSize() {
return size;
}
public int getType() {
return type;
}
public static int getTypeFlag(String type) {
if(type == null) {
throw new NullPointerException();
}
for(int counter = 0; counter < TYPE_STRINGS.length; counter++) {
if(type.equalsIgnoreCase(TYPE_STRINGS[counter])) {
return counter;
}
}
return NULL;
}
}
/* ADT corresponding to a FormattedField entry in the XML initialization
* file.
*/
private static class FormattedField {
private String name;
private int size;
private Field correspondingField;
public FormattedField(String name, int size,
Field correspondingField) {
if(name == null || correspondingField == null) {
throw new NullPointerException();
}
if(size <= 0) {
throw new IllegalArgumentException();
}
this.name = name;
this.size = size;
this.correspondingField = correspondingField;
}
public Field getCorrespondingField() {
return correspondingField;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
}
/* ADT representing a line entry in an input file. The data fields are
* stored in the HashMap fields, which can be accessed via Field objects
* (pass it the Field object corresponding to the field data that you
* wish to obtain).
*
* @since 1.0
*/
private static class StarEntry {
private HashMap fields;
public StarEntry() {
fields = new HashMap();
}
public void addEntry(Field field, String value) {
if(field == null || value == null) {
throw new NullPointerException();
}
fields.put(field, value);
}
public String getEntry(Field field) {
return fields.get(field);
}
}
}