5
\$\begingroup\$

I'm struggling to improve this command-line java application, which reads CSV or txt files, ands allows the user do some "queries" with the data. I can't use any external library, so I'm doing it all and now I'm trying to do some refactoring on it, to let it be scalable, and maybe in the future I could add some new commands / queries. I tried some design patterns like builders, interfaces and etc ..., but it does not seem right.

Do you have any tip to abstract this in some elegant way? I will post the first working version of the application to you guys give it a look:


Reader.java - this class reads the file and returns an array with all the rows and each row another array in which it will store each attribute of the row:

public class Reader {

    public ReaderConfig readerConfig;   

    public Reader(ReaderConfig readerConfig){
        this.readerConfig = readerConfig;
    }

    public List<String[]> read() {
        List<String[]> rows = new ArrayList<String[]>();
        try {
            BufferedReader br = new BufferedReader(new FileReader(readerConfig.getPath()));

            String row = "";
            while ((row = br.readLine()) != null) {
                String[] properties = row.split(readerConfig.getDelimiter());
                rows.add(properties);
            }

            br.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        }

        return rows;
    }
}

ReaderConfig.java - It will parameterize Reader.java, based on user input:

public class ReaderConfig {

    private String path;
    private String delimiter;
    private boolean hasHeader;

    private Scanner scanner = new Scanner(System.in);

    public void setPathFile() {
        System.out.println("Enter the full path of the file ");
        String input = scanner.nextLine();
        if(FileUtils.isFileExtensionValid(FileUtils.getFileExtension(input))){
            try {
                new FileReader(input);
                this.path = input;
            } catch (FileNotFoundException e) {
                System.out.println("Invalid file path!!");
                setPathFile();
            }
        }else{
            System.out.println("Invalid extension! Avaliable extension: .txt and .csv");
            setPathFile();
        }
    }

    public void setDelimiter() {
        System.out.println("Enter the delimiter used by file (Example ;)");
        String input = scanner.nextLine().trim();
        if(input.length() == 0){
            System.out.println("Invalid delimiter!");
            setDelimiter();
        }

        this.delimiter = input;
    }

    public void setHasHeader() {
        System.out.println("Does the file have header? (Y/N)");
        String input = scanner.nextLine().trim().toUpperCase();

        if(InputUtils.isBooleanInputValid(input)){
            this.hasHeader = InputUtils.evalInputToBoolean(input);
        }else{
            System.out.println("Invalid command!");
            setHasHeader();
        }

    }

    /** GETTERS */
    public String getPath() {
        return path;
    }

    public String getDelimiter() {
        return delimiter;
    }

    public boolean hasHeader() {
        return hasHeader;
    }
}

And last but not least, the main class that will run the application:

public class Main {

    public static void main(String[] args) {        
        ReaderConfig readerConfig = new ReaderConfig();           
        Reader reader = new Reader(readerConfig);

        List<String[]> data = reader.read();
        System.out.println("File loaded...");

        //Header information 
        String[] headers = {};
        if(readerConfig.hasHeader()){
            headers = data.get(0);
            System.out.println("Avaliable headers: " + Arrays.toString(headers));
        }

        //Listen console for commands:
        Scanner scanner = new Scanner(System.in);

         while (true) {
             System.out.println("Enter a command (or QUIT to exit): ");
             String input = scanner.nextLine();

             if(input.toLowerCase().equals("quit")){
                 scanner.close();
                 System.out.println("Bye.");
                 break;

             } else if(input.equals("select *")){
                 System.out.println("Total records: " + data.size());

             } else if(input.matches("select distinct")){

                 int headerIndex = -1;               
                 if(headers.length > 0) {
                     headerIndex = Arrays.asList(headers).indexOf(getParameter("select distinct", input));
                 }else{
                     //If not have a header, check if the column index has been passed as parameter
                     String isNumberRegex = "\\d+";
                     String parameter = getParameter("count distinct \\[(.*?)\\]", input);
                     if(parameter.matches(isNumberRegex)){
                         headerIndex = new Integer(parameter);
                     }
                 }
                 if(headerIndex != -1){
                     Set<String> set = new HashSet<String>();
                     for(String[] props : data){
                         set.add(props[headerIndex]);
                     }
                     System.out.println("Total records: " + set.size()); 
                 }else{
                     System.out.println("Header not found!");
                 }

             } else if(input.matches("filter") && headers.length > 0) {

                 int propIndex = Arrays.asList(headers).indexOf(getParameter("filter", input));
                 String value = getValue("filter", input);
                 System.out.println(headers[propIndex]);
                 int cont = 0;
                 for(String[] test : data){
                     if(Arrays.asList(test).contains(value)){
                         System.out.println(Arrays.toString(test));
                         cont++;
                     }
                 }
                 System.out.println("Total rows found: " + cont);

             } else if(input.equals("help")){

                System.out.println("Avaliable commands: \n"
                        + " select * - Display the total of rows imported.\n"
                        + " select distinct - Displays the total of distinct rows based on a entered header.\n"
                        + " select [header] [value] - Display the header and all his rows based in the value entered."
                        + " quit - Quit the application. \n");

             }else{
                 System.out.println("Invalid command!");
             }
        }
    }  

    public static String getParameter(String pattern, String input){
        String header = "";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(input);

        while(m.find()) {
            header = m.group(1);
        }

        return header;
    }

    public static String getValue(String pattern, String input){
        String value = "";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(input);

        while(m.find()) {
            value = m.group(2);
        }

        return value;
    }
}
\$\endgroup\$

locked by Jamal Jan 14 '17 at 21:06

This post has been locked while disputes about its content are being resolved. You may discuss this on meta if you have concerns.

Read more about locked posts here.

  • \$\begingroup\$ Please don't edit out your code out of your question. This is going against the rule of this site. \$\endgroup\$ – Marc-Andre Jan 9 '17 at 16:01
  • \$\begingroup\$ Welcome to Code Review! I have rolled back the last edit. Please see what you may and may not do after receiving answers. \$\endgroup\$ – Vogel612 Jan 10 '17 at 1:32
  • \$\begingroup\$ You were told before not to edit code out of the question, so I'm locking it for now to prevent further edits. You may discuss this further on chat. \$\endgroup\$ – Jamal Jan 14 '17 at 21:08
3
\$\begingroup\$

try-with-resources

        try {
            BufferedReader br = new BufferedReader(new FileReader(readerConfig.getPath()));

            String row = "";
            while ((row = br.readLine()) != null) {
                String[] properties = row.split(readerConfig.getDelimiter());
                rows.add(properties);
            }

            br.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        }

This is exactly the situation for which try-with-resources was designed:

        try (BufferedReader br = new BufferedReader(new FileReader(readerConfig.getPath()))) {
            String row = "";
            while ((row = br.readLine()) != null) {
                String[] properties = row.split(readerConfig.getDelimiter());
                rows.add(properties);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }

If you have to compile against an old version of Java, the old way of doing this was to use a finally block.

        } finally {
            if (br != null) {
                br.close;
            }

But that means that we have to define br before the try block. It's easier to just do the try-with-resources.

equalsIgnoreCase

             if(input.toLowerCase().equals("quit")){

Java has a method for this situation.

             if (input.equalsIgnoreCase("quit")) {

Perhaps that just does the same thing. Or maybe Java has a more efficient way to check. Note that if the strings are not equal, it doesn't have to lower case the whole string. Only the part that it checks.

Typo

At least twice you write "Avaliable" when you presumably meant "Available".

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.