/* Class that abstracts a Comma Separated Value String, which is a string ** consisting of a sequence of field values, each one separated from ** the next by a comma. ** Author: P.M.J. ** Updated by R.W.M. */ public class CSV_String { // class constant // -------------- // character terminating each field private static final char TERMINATOR = ','; // instance variables // ------------------ private String fields; // the "wrapped" CSV-string itself, with a // TERMINATOR character inserted at the end. private int numberOfFields; // # fields in this CSV_String // constructor // ----------- /* Establishes this CSV_String object as a "wrapper" for the given ** string, which is interpreted as a CSV-string. */ public CSV_String(String string) { // Insert a TERMINATOR character at the end to make it easier to // find the last field. In effect, the internal representation of // the CSV-string is a CTV-string ("Comma-Terminated"). fields = string + TERMINATOR; numberOfFields = numOccurrencesOf(fields, TERMINATOR); } // observers // --------- /* Returns the number of fields in this CSV_String. */ public int numberOfFields() { return numberOfFields; } /* Returns the k-th field within this CSV_String, or null if k ** is not a valid position number. (The valid position numbers ** are 0, 1, ..., N-1, where N is the number of fields in this ** CSV_String.) */ public String elementAt(int k) { String result; if (k < 0 || k >= numberOfFields()) { result = null; } else { int whereFieldStarts = 0; int whereFieldEnds = fields.indexOf(TERMINATOR); // Loop invariant: // fields.substring(whereFieldStarts, whereFieldEnds) // is the i-th field of this CSV_String. for (int i = 0; i != k; i = i+1) { whereFieldStarts = whereFieldEnds + 1; whereFieldEnds = fields.indexOf(TERMINATOR, whereFieldStarts); } result = fields.substring(whereFieldStarts, whereFieldEnds); } return result; } /* Returns the CSV-string that this object "wraps". */ public String toString() { String result; if (fields != null) { result = fields.substring(0,fields.length()-1); } else { result = null; } return result; } // Methods to support "iteration" over a CSV String // ------------------------------------------------- /* The following instance variable declaration and three methods form an ** "interface" similiar to Java's standard "Iterator" interface. They are ** meant to be used in conjunction with each other to sequentially access ** the fields in this CSV String. */ // Where the field to be returned by the next call to next() begins // within 'fields'. private int fieldBeginsAt = 0; /* Resets the iteration to the beginning of this CSV String (so that ** the next call to next() will return the field occupying position zero). */ public void reset() { fieldBeginsAt = 0; } /* Returns true if and only if the iteration is unfinished; that is, there ** remain one or more fields that have not yet been delivered by calls ** to next() since the most recent call to reset(). */ public boolean hasNext() { return fieldBeginsAt < fields.length(); } /* Standard method that returns the next field in the current iteration. ** If hasNext() returns false, indicating that the iteration is finished, ** then null is returned. */ public String next() { String result = null; if (hasNext()) { // Find the comma that terminates the field that begins // at position 'fieldBeginsAt' within 'fields'. int fieldEndsAt = fields.indexOf(TERMINATOR, fieldBeginsAt); result = fields.substring(fieldBeginsAt, fieldEndsAt); // The next field begins immediately following the comma // that was just found. fieldBeginsAt = fieldEndsAt + 1; } return result; } // private utility method // ---------------------- /* Returns the number of occurrences of the given character ** within the given string. */ private int numOccurrencesOf(String s, char ch) { int count = 0; for (int i=0; i != s.length(); i = i+1) { if (s.charAt(i) == ch) { count = count + 1; } } return count; } }