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;
}
}