import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; /* CSVQueryR.java ** Java program that allows the user to run "queries" on the records in ** a Comma Separated Values (CSV) file, the name of which is expected to ** be provided via a command line argument (aka a "run argument" in jGrasp's ** terminology). The file is assumed to have a "header" record (itself in ** the form of a CSV-string) containing the titles of the fields of the ** remaining records. ** To run a query on a CSV-record is simply to enter a field title for the ** purpose of asking for the value of the named field of that record to be ** displayed. ** Initially, the record that is subject to being queried is the second one ** in the file (i.e., the one immediately following the header record). The ** user can advance from one record to the next by entering an ** appropriate response at the program's prompt. The user can also quit. ** ** Authors: P.M.J. and R.W.M. ** Date: November 2024 ** Last modified: December 2024 */ public class CSVQueryR { // Constants static final String QUIT = "Q"; // command to quit static final String NEXT = "N"; // command to advance to next record in file static final String TITLES = "T"; // command to display field titles static final String COMMA = ","; static final String QUESTION_MARK = "?"; static CSV_String fieldTitles; static CSV_String currentRec; /* The name of a correctly formatted "CSV file" is expected as the single ** command-line argument (a.k.a. run argument, in jGrasp's terminology). */ public static void main(String[] args) throws FileNotFoundException { // Establish a Scanner that is able to read from the file whose name // was provided via the command-line argument. String fileName = args[0]; Scanner fileInput = new Scanner(new File(fileName)); // Read the first line from the file, which is the header record // containing the titles of the fields of the remaining records.. fieldTitles = new CSV_String(fileInput.nextLine()); // Read the second line from the file and make a CSV_String object // from it. It becomes the record initially subject to being queried. currentRec = new CSV_String(fileInput.nextLine()); // Display the newly-established "current record". printCurrentRec(); // Establish a Scanner to read input from the keyboard. Scanner keyboard = new Scanner(System.in); // Each iteration of the following loop prompts the user to enter a // command, which is expected to be either the code to advance to the // next record ('N'), the code to quit ('Q'), the code to ask for the // titles of the fields ('T'), or the title of one of the fields. In // the last case, the value of that field (within the current record) // is displayed. boolean done = false; do { String query = getString("\nEnter query:>",keyboard).trim(); if (query.equals(QUIT)) { done = true; } else if (query.equals(TITLES)) { System.out.println(fieldTitles); } else if(query.equals(NEXT)) { if(fileInput.hasNext()) { // Read the next record and use it to make a CSV_String object currentRec = new CSV_String(fileInput.nextLine()); // Display the newly-established "current" record. printCurrentRec(); } else { done = true; System.out.println("---End of file reached!!!"); } } else { // The query is (probably) the title of a field; // find the position of that field. int fieldPos = title2Position(query); // Retrieve the value of the field from within the current record. String fieldVal = currentRec.elementAt(fieldPos); // Display the value of the retrieved field or, if appropriate, // report that the query was not valid if (fieldVal == null) { System.out.println("--->Invalid query; try again."); } else { System.out.println(fieldVal); } } } while (!done); System.out.println("\nDone!"); } private static void printCurrentRec() { System.out.println("Currect record is now: " + currentRec); } /* Returns the position of the field within the 'fieldTitles' record ** that matches the given 'title', or -1 if there is no such field. ** This version of the method utilizes the "iterator" methods of the ** CSV_String class. */ private static int title2Position(String title) { fieldTitles.reset(); int k = 0; boolean keepGoing = true; // Loop invariant: None among field titles 0, 1, ... , k-1 matches 'title'. // If k == -1, no field title matches 'title' while (keepGoing) { if (!fieldTitles.hasNext()) { keepGoing = false; k = -1; } else if (fieldTitles.next().equals(title)) { keepGoing = false; } else { k = k+1; } } // Assert: If k == -1, no field title matches 'title'; // otherwise, field title #k matches 'title'. return k; } /* Alternative version of the method above. ** This version uses the CSV_String class's elementAt() method ** rather than its "iterator" methods. */ private static int title2PositionAlt(String title) { final int N = fieldTitles.numberOfFields(); int k = -1; // Loop invariant: // None among fields 0, 1, ..., k-1 of 'fieldTitles' matches 'title' do { k = k+1; } while (k != N && !title.equals(fieldTitles.elementAt(k))); // Assert: either 0 <= k < N and the k-th field of 'fieldTitles' matches // 'title', or else k == N and there is no such matching field. if (k == N) { k = -1; } // there is no matching title return k; } /* Prints the given prompt and then reads and returns the full line of ** input (as a string) entered by the interactive user. */ public static String getString(String prompt, Scanner input) { System.out.print(prompt); String line = input.nextLine(); // System.out.println(line); return line; } }