/* CSVQuery.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 fields in a CSV-record are understood to be numbered ** beginning at zero. To run a query on a CSV-record is simply to enter an ** integer k for the purpose of asking for field #k of that record to be ** displayed. ** Initially, the record that is subject to being queried is the first one in ** the file. 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. ** ** Author: P.M.J. and R.W.M. ** Date: October 2024 */ import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; public class CSVQuery { // 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 COMMA = ","; static final String QUESTION_MARK = "?"; /* 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, making it the record initially // subject to having queries applied to it. String line = fileInput.nextLine(); // 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'), or a nonnegative integer k // that serves to request that the k-th field of the current record be // displayed. boolean done = false; do { String query = getString("\nEnter query:>",keyboard).trim(); if(isValidQuery(query)) { if(query.startsWith(QUIT)) { done = true; } else if(query.startsWith(NEXT)) { if(fileInput.hasNext()) { line = fileInput.nextLine(); } else { done = true; System.out.println("---End of file reached!!!"); } } else { int position = Integer.parseInt(query); System.out.println(fieldOf(line,position)); } } else { System.out.println("--->Invalid query; try again."); } } while(!done); System.out.println("\nDone!"); } /* Reports whether or not the given query string is valid, which is to ** say that it specifies an integer numeral or the code either for ** quitting or for advancing to the next record in the input file. */ public static boolean isValidQuery(String query) { return (query.length() > 0) && (query.startsWith(QUIT) || query.startsWith(NEXT) || isValidInt(query)); } /* Reports whether or not the given query string describes an integer. */ public static boolean isValidInt(String query) { boolean result = true; try { // This call to Integer.parseInt() results in Integer.parseInt(query); // an exception being thrown unless 'query' } catch (Exception e) { // has the form of an integer literal result = false; } return result; } /* Returns the field occupying the specified position within the given ** CSV-record. If the given CSV-record has no such field (e.g., because ** the value of 'position' exceeds the number of fields in the record), ** QUESTION_MARK is returned. */ public static String fieldOf(String record, int position) { String result = QUESTION_MARK; int numCommas = numOccurrences(record, COMMA.charAt(0)); if (position >= 0 && position < numCommas) { int whereFieldStarts = 0; int whereFieldEnds = record.indexOf(COMMA); // Loop invariant: // record.substring(whereFieldStarts, whereFieldEnds) // is field #i of record. for (int i = 0; i != position; i = i+1) { whereFieldStarts = whereFieldEnds + 1; whereFieldEnds = record.indexOf(COMMA, whereFieldStarts); } result = record.substring(whereFieldStarts, whereFieldEnds); } return result; } /* Alternative version of the method above. */ public static String fieldOfAlternative(String record, int position) { String result = QUESTION_MARK; int startIndex = 0; int index = record.indexOf(COMMA); // Loop invariant: // Let POS be the original value of formal parameter 'position' // and let k = POS - position. // Then record.substring(startIndex, index) is the k-th field // in 'record'. while((position > 0) && (index >= 0)) { if(index >= 0) { position = position - 1; startIndex = index + 1; index = record.indexOf(COMMA,startIndex); } } if(index >= 0) { result = record.substring(startIndex,index); } return result; } /* 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); return input.nextLine(); } /* Returns the # of occurrences of the given character within ** the given string. */ private static int numOccurrences(String s, char ch) { int count = 0; for (int i=0; i != s.length(); i++) { if (s.charAt(i) == ch) { count = count+1; } } return count; } }