Search code examples
javafileiobufferedreader

Checking which parameter is missing from file content


I have a TransferReader class which reads a file containing transfer data from bank account to another using the following form:

SenderAccountID,ReceiverAccountID,Amount,TransferDate

"473728292,474728298,1500.00,2019-10-17 12:34:12" (unmodified string)

Suppose that the file has been modified before being read so that one of the above mentioned paramaters are missing, and I want to check which of those are missing.

"474728298,1500.00,2019-10-17 12:34:12" (modified string)

I am using a BufferedReader to read each line, and then splitting each element into a String[] using String.split(",") as delimeter.


Solution

  • As already realized, because the Sender Account ID and Receiver Account ID are right next to one another within a record there is no real way of knowing which ID might be missing unless a delimiter remains in its' place indicating a Null value. There are however mechanisms available to determine that it is indeed one of the two that is missing, which one will need to be carried out through User scrutiny and even then, that may not be good enough. The other record column fields like Amount and Transfer Date can be easily validated or if missing can be implicated within a specific File Data Status Log.

    Below is some code that will read a data file (named Data.csv) and log potential data line (record) errors into a List Interface object which is iterated through and displayed within the Console Window when the read is complete. There are also some small helper methods. Here is the code:

    private void checkDataFile(String filePath) {
        String ls = System.lineSeparator();
        List<String> validationFailures = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
    
        // 'Try With Resources' used here to auto-close reader.
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            int lineCount = 0;
            // Read the file line-by-line.
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                lineCount++;
                if (lineCount == 1 || line.equals("")) {
                    continue;
                }
                sb.delete(0, sb.capacity()); // Clear the StringBuilder object
    
                // Start the Status Log
                sb.append("File Line Number: ").append(lineCount)
                        .append(" (\"").append(line).append("\")").append(ls);
                // Split line into an Array based on a comma delimiter
                // reguardless of the delimiter's spacing situation.
                String[] lineParts = line.split("\\s{0,},\\s{0,}");
                /* Validate each file line. Log any line that fails
                   any validation for any record column data into a
                   List Interface object named: validationFailures
                 */
                // Are there 4 Columns of data in each line...
                if (lineParts.length < 4) {
                    sb.append("\t- Invalid Column Count!").append(ls);
                    // Which column is missing...
                    // *** You may need to add more conditions to suit your needs. ***
                    if (checkAccountIDs(lineParts[0]) && lineParts.length >= 2 && !checkAccountIDs(lineParts[1])) {
                        sb.append("\t- Either the 'Sender Account ID' or the "
                                + "'ReceiverAccountID' is missing!").append(ls);
                    }
                    else if (lineParts.length >= 3 && !checkAmount(lineParts[2])) {
                        sb.append("\t- The 'Amount' value is missing!").append(ls);
                    }
                    else if (lineParts.length < 4) {
                        sb.append("\t- The 'Transfer Date' is missing!").append(ls);
                    }
                }
                else {
                    // Is SenderAccountID data valid...
                    if (!checkAccountIDs(lineParts[0])) {
                        sb.append("\t- Invalid Sender Account ID in column 1! (")
                                .append(lineParts[0].equals("") ? "Null" : 
                                        lineParts[0]).append(")");
                        if (lineParts[0].length() < 9) {
                            sb.append(" <-- Not Enough Or No Digits!").append(ls);
                        }
                        else if (lineParts[0].length() > 9) {
                            sb.append(" <-- Too Many Digits!").append(ls);
                        }
                        else {
                            sb.append(" <-- Not All Digits!").append(ls);
                        }
                    }
                    // Is ReceiverAccountID data valid...
                    if (!checkAccountIDs(lineParts[1])) {
                        sb.append("\t- Invalid Receiver Account ID in coloun 2! (")
                                .append(lineParts[1].equals("") ? "Null" : 
                                        lineParts[1]).append(")");
                        if (lineParts[1].length() < 9) {
                            sb.append(" <-- Not Enough Or No Digits!").append(ls);
                        }
                        else if (lineParts[1].length() > 9) {
                            sb.append(" <-- Too Many Digits!").append(ls);
                        }
                        else {
                            sb.append(" <-- Not All Digits!").append(ls);
                        }
                    }
                    // Is Amount data valid...
                    if (!checkAmount(lineParts[2])) {
                        sb.append("\t- Invalid Amount Value in column 3! (")
                                .append(lineParts[2].equals("") ? "Null" : 
                                        lineParts[2]).append(")").append(ls);
                    }
                    // Is TransferDate data valid...
                    if (!checkTransferDate(lineParts[3], "yyyy-MM-dd HH:mm:ss")) {
                        sb.append("\t- Invalid Transfer Date Timestamp in column 4! (")
                                .append(lineParts[3].equals("") ? "Null" : 
                                        lineParts[3]).append(")").append(ls);
                    }
                }
                if (!sb.toString().equals("")) {
                    validationFailures.add(sb.toString());
                }
            }
        }
        catch (FileNotFoundException ex) {
            System.err.println(ex.getMessage());
        }
        catch (IOException ex) {
            System.err.println(ex.getMessage());
        }
    
        // Display the Log...
        String timeStamp = new SimpleDateFormat("yyyy/MM/dd - hh:mm:ssa").
                format(new Timestamp(System.currentTimeMillis()));
        String dispTitle = "File Data Status at " + timeStamp.toLowerCase() 
                         + " <:-:> (" + filePath + "):";
        System.out.println(dispTitle + ls + String.join("", 
                Collections.nCopies(dispTitle.length(), "=")) + ls);
        if (validationFailures.size() > 0) {
            for (String str : validationFailures) {
                if (str.split(ls).length > 1) {
                    System.out.println(str);
                    System.out.println(String.join("", Collections.nCopies(80, "-")) + ls);
                }
            }
        }
        else {
            System.out.println("No Issues Detected!" + ls); 
        } 
    }
    
    private boolean checkAccountIDs(String accountID) {
        return (accountID.matches("\\d+") && accountID.length() == 9);
    }
    
    private boolean checkAmount(String amount) {
        return amount.matches("-?\\d+(\\.\\d+)?");
    }
    
    private boolean checkTransferDate(String transferDate, String format) {
        return isValidDateString(transferDate, format);
    }
    
    private boolean isValidDateString(String dateToValidate, String dateFromat) {
        if (dateToValidate == null || dateToValidate.equals("")) {
            return false;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
        sdf.setLenient(false);
        try {
            // If not valid, it will throw a ParseException
            Date date = sdf.parse(dateToValidate);
            return true;
        }
        catch (ParseException e) {
            return false;
        }
    }
    

    I'm not exactly sure what your particular application process will ultimately entail but if other processes are accessing the file and making modifications to it then it may be wise utilize a locking mechanism to Lock the file during your particular process and Unlock the file when it is done. This however will most likely require you to utilize a different reading algorithm since locking a file must be done through a writable channel. Using the FileChannel and FileLock classes from the java.nio package could possibly assist you here. There would be examples of how to utilize these classes within the StackOverflow forum.