3
\$\begingroup\$

I have only dabbled in Java before (my previous experience being mostly with PHP and JavaScript) but a request come through for a small application that seemed like a good chance to get some experience.

We receive two types of .csv from a third party:

  • The first is the 'offer' file, which contains a collection of details about offers made to individuals. Each record comes with two IDs - the third party's ID (TPID), and our own internal ID (IID).
  • The second is a 'response' file, which contains details related to the acceptance/rejection of offers by individuals. Each record comes with only one ID - the TPID.

We need both .csvs to have records with the IID, as that's what our systems work off. Efforts to get the third party to add that to the 'response' .csv are ongoing, but in the meantime, I wrote a GUI application which:

  1. Has the user choose the two file paths and an output path;
  2. Goes through each entry on the 'response' .csv;
  3. Takes the TPID, matches it against an entry on the 'offer' .csv and retrieves the IID.
  4. Replaces the TPID on the 'response' .csv with the IID.
  5. Outputs the resulting modified .csv, as well as a seperate .csv containing any records existing on the 'response' file but not on the 'offer' file.

The code I've put together does what was asked, so it'll beat doing the replace job manually. Being new to Java, I'd be grateful for some pointers as to what I can do better next time.

This application uses the OpenCSV 3.5 library.

ReplaceUI.java

package my.numberreplace;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;


public class ReplaceUI extends javax.swing.JFrame {

/**
 * Creates new form ReplaceUI
 */
public ReplaceUI() {
    initComponents();
}

/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    jPanel1 = new javax.swing.JPanel();
    offerButton = new javax.swing.JButton();
    responseButton = new javax.swing.JButton();
    offerPath = new javax.swing.JTextField();
    responsePath = new javax.swing.JTextField();
    outputButton = new javax.swing.JButton();
    outputPath = new javax.swing.JTextField();
    replaceButton = new javax.swing.JButton();
    exitButton = new javax.swing.JButton();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("Replace-A-Tron 3000 - Student Number Edition");

    jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Choose files"));

    offerButton.setText("Offer File");
    offerButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            offerButtonActionPerformed(evt);
        }
    });

    responseButton.setText("Response File");
    responseButton.setPreferredSize(new java.awt.Dimension(77, 23));
    responseButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            responseButtonActionPerformed(evt);
        }
    });

    offerPath.setEditable(false);

    responsePath.setEditable(false);
    responsePath.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            responsePathActionPerformed(evt);
        }
    });

    outputButton.setText("Output Folder");
    outputButton.setPreferredSize(new java.awt.Dimension(77, 23));
    outputButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            outputButtonActionPerformed(evt);
        }
    });

    outputPath.setEditable(false);
    outputPath.setToolTipText("");
    outputPath.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            outputPathActionPerformed(evt);
        }
    });

    javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
    jPanel1.setLayout(jPanel1Layout);
    jPanel1Layout.setHorizontalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(outputButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(offerButton, javax.swing.GroupLayout.DEFAULT_SIZE, 99, Short.MAX_VALUE)
                .addComponent(responseButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(responsePath)
                .addComponent(offerPath, javax.swing.GroupLayout.DEFAULT_SIZE, 394, Short.MAX_VALUE)
                .addComponent(outputPath))
            .addContainerGap())
    );
    jPanel1Layout.setVerticalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(jPanel1Layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                .addComponent(offerPath)
                .addComponent(offerButton, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                .addComponent(responsePath)
                .addComponent(responseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                .addComponent(outputPath)
                .addComponent(outputButton, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );

    outputButton.getAccessibleContext().setAccessibleName("Output Folder");
    outputPath.getAccessibleContext().setAccessibleName("");

    replaceButton.setText("Replace");
    replaceButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            replaceButtonActionPerformed(evt);
        }
    });

    exitButton.setText("Exit");
    exitButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            exitButtonActionPerformed(evt);
        }
    });

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                    .addComponent(replaceButton)
                    .addGap(150, 150, 150)
                    .addComponent(exitButton)
                    .addGap(17, 17, 17)))
            .addContainerGap())
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(replaceButton)
                .addComponent(exitButton))
            .addGap(51, 51, 51))
    );

    pack();
}// </editor-fold>                        

private void exitButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
    System.exit(0);
}                                          

private void offerButtonActionPerformed(java.awt.event.ActionEvent evt) {                                            
    String filepath = getFilepath("File");
    if(filepath != "Cancelled") {
        offerPath.setText(filepath); 
    }
}                                           


private void responseButtonActionPerformed(java.awt.event.ActionEvent evt) {                                               
    String filepath = getFilepath("File");
        if(filepath != "Cancelled") {
            responsePath.setText(filepath); 
        }
}                                              

private void responsePathActionPerformed(java.awt.event.ActionEvent evt) {                                             
    // TODO add your handling code here:
}                                            

private void replaceButtonActionPerformed(java.awt.event.ActionEvent evt) {                                              
        File offer = new File(offerPath.getText().trim());
        File response = new File(responsePath.getText().trim());

        if( (offer.isFile() && offer.canRead()) && (response.isFile() && response.canRead())) {
            //If we've got two valid files, and can read from both of them
            try {
                replaceStudentNumbers(offerPath.getText().trim(), responsePath.getText().trim(), outputPath.getText().trim());
            } catch (IOException ex) {
                Logger.getLogger(ReplaceUI.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            JOptionPane.showMessageDialog(null, "Please enter valid offer and response file paths.");
        }
}                                             

private void outputButtonActionPerformed(java.awt.event.ActionEvent evt) {                                             
    String filepath = getFilepath("Directory");
    if(filepath != "Cancelled") {
        outputPath.setText(filepath); 
    }
}                                            

private void outputPathActionPerformed(java.awt.event.ActionEvent evt) {                                           

}                                          

private String getFilepath(String toSelect) {
    String filepath;

    final JFileChooser fc = new JFileChooser();

    //If we're dealing with the outpu file location, just show directories.
    if(toSelect == "Directory") {
        fc.setDialogTitle("Select target directory");
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

    } else {
        fc.setDialogTitle("Select target file");
    }

     //Handle open button action.
    int returnVal = fc.showOpenDialog(ReplaceUI.this);

    if (returnVal == JFileChooser.APPROVE_OPTION) {
        File file = fc.getSelectedFile();
        filepath = file.getAbsolutePath();
        return filepath;
    } else {
        return "Cancelled";
    }

}

private void replaceStudentNumbers (String pathOffer, String pathResponse, String pathOutput) throws IOException {

    //Get a datestamp in the correct timezone to add to the output .csvs
    Date myDate = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setTimeZone(TimeZone.getTimeZone("Australia/Brisbane"));
    String dateStamp = sdf.format(myDate);

    boolean permission = false;

    //Make sure the directory exists using mkdirs, and in the process check we've got permission to write to it.
    try {
        new File(pathOutput).mkdirs();        //Create the directory, if it doesn't already exist.
        permission = true;
    } catch(SecurityException se) {

    }

    if(permission) {
        //Read in the two .csvs
        String[][] InputOfferFile = readCSVToArray(pathOffer, "OfferFile");
        String[][] InputResponseFile =  readCSVToArray(pathResponse, "ResponseFile");

        //Set paths for the result files
        String outputChangedPath = pathOutput + "\\ChangedStudents" + dateStamp + ".csv";
        String outputNotFoundPath = pathOutput + "\\NotFoundStudents" + dateStamp + ".csv";

        String[] outputPaths = {outputChangedPath, outputNotFoundPath};

        //Create the output files at their designated locations
        CSVWriter writeChangedStudents = createWriter(outputChangedPath);
        CSVWriter writeNotFoundStudents = createWriter(outputNotFoundPath);

        //Add titles to the .csvs
        String[] title = {"ref_num",  "institution_code", "course_code", "offering_label", "round_num", "offer_date", "final_response"};
        writeChangedStudents.writeNext(title);
        writeNotFoundStudents.writeNext(title);

        //Go through and perform the replacement, returning useful statistics
        int[] stats = performReplacement(InputOfferFile, InputResponseFile, writeChangedStudents, writeNotFoundStudents);

        //Close the connections to the new .csvs
        writeChangedStudents.close();
        writeNotFoundStudents.close();

        JOptionPane.showMessageDialog(null, "Operation complete.\n\n" +  
                stats[0] + " student records changed.\n\n" + 
                stats[1] + " student records not found.\n\n "
                + "Your file is located at " + outputPaths[0]);
    } else {
        JOptionPane.showMessageDialog(null, "Please enter valid offer and response file paths.");
    }

}

//For each record in the Offer file, check whether the ref_num ([0]) of the record we're looking at on the Response file corresponds to a Stu_alt_id. {[0]) 
//If it does, set the Response file's ref_num to the Offer file's Stu_id ([1])
public static boolean checkChangeStudentID (String[][] InputOfferFile, String[][] InputResponseFile, int i) {
    for(int j = 0; j < InputOfferFile.length; j++) {
        if(InputResponseFile[i][0].equals(InputOfferFile[j][0])) {
            InputResponseFile[i][0] = InputOfferFile[j][1];
             return true;       //If it's found, end the loop and return true
        }
    }
    return false;   //If the loop isn't ended early, return false
}

//For each record in the Response file, the checkChangeStudentID method will check whether there's a matching record of the Offer file.
//If the record exists, write the (now changed) Response file record to the first output file.
//If it doesn't, write the unchanged Response file record to the second output file.
public static int[] performReplacement(String[][] InputOfferFile, String[][] InputResponseFile, CSVWriter writeChangedStudents, CSVWriter writeNotFoundStudents) {
     boolean exists;
     int found = 0;
     int missing = 0;

    for(int i = 0; i < InputResponseFile.length; i++) {
        exists = checkChangeStudentID (InputOfferFile, InputResponseFile, i);
                if(exists) {
                    writeChangedStudents.writeNext(InputResponseFile[i]);
                    found++;
                } else {
                   writeNotFoundStudents.writeNext(InputResponseFile[i]);
                   missing++;
                }
    }
    return new int[]{found, missing};
}

public static String[][] readCSVToArray (String fileName, String arrayName) throws IOException {
    CSVReader reader1 = new CSVReader(new FileReader(fileName), ',' , '"' , 1);
    List<String[]> lines = reader1.readAll();
    String[][] array = lines.toArray(new String[lines.size()][]);
    return array;
}

public static CSVWriter createWriter (String fileName) throws IOException {
    CSVWriter writer = new CSVWriter(new FileWriter(fileName));
    return writer;
}



/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(ReplaceUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(ReplaceUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(ReplaceUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(ReplaceUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new ReplaceUI().setVisible(true);
        }
    });
}

// Variables declaration - do not modify                     
private javax.swing.JButton exitButton;
private javax.swing.JPanel jPanel1;
private javax.swing.JButton offerButton;
private javax.swing.JTextField offerPath;
private javax.swing.JButton outputButton;
private javax.swing.JTextField outputPath;
private javax.swing.JButton replaceButton;
private javax.swing.JButton responseButton;
private javax.swing.JTextField responsePath;
// End of variables declaration                   
}

offer.csv

Stu_alt_id,Stu_id,gvn_name,family_name,stu_gender_cd,offer
123456,9876,John, Test,M,BB61
123457,9877,Sam,Bloop,F,SC42
123458,9878,Avishay,Florp,M,FA18

response.csv

ref_num,institution_code,course_code,offering_label,round_num,offer_date,final_response
123456,BB,11111,Jul-15,420,01/01/2015,A
123458,AC,22222,Jan-15,666,02/02/2015,AR
123459,DC,33333,Nov-11,123,03/03/2015,A
\$\endgroup\$
1
  • \$\begingroup\$ The general problem for a review is that you use a GUI editor that generates the most code. So if you are using a GUI editor then you should separate the logic from the UI because THAT (the logic) is the only thing that should be reviewed. \$\endgroup\$ – oopexpert Feb 12 '16 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.