4
\$\begingroup\$

(See the next iteration.)

I have that application for en-/decrypting files. Here, I will post the actual GUI and command line code. In order to run the program refer to this GitHub repository.

Some images:

The main view

Choosing files

Entering the key

My code is as follows:

App.java:

package net.coderodde.ciphertool;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import net.coderodde.encryption.CipherTools;
import net.coderodde.file.FileTools;

/**
 * This class implements the command line app for encrypting and decrypting 
 * files with a key.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Feb 29, 2016) 
 */
public class App {

    private static final String HELP_MESSAGE = 
     "Usage: java -jar File.jar [-e | -d <key> FILE1 [FILE2 [...]]]\n" +
     "Where       -e   encrypt the files.\n" +
     "            -d   decrypt the files.\n" + 
     "         <key>   the key in decimal; " + 
                      "use prefix \"0x\" for hexadecimal.\n" +
     "If you omit all arguments a GUI is started instead.";

    private boolean graphicalInterfaceRequested;
    private boolean printHelpMessage;
    private Mode mode;
    private int key;
    private String[] args;

    public void processArguments(String[] args) {
        this.args = args.clone();

        if (args.length == 0) {
            graphicalInterfaceRequested = true;
            return;
        }

        if (args.length < 3) {
            printHelpMessage = true;
            return;
        }

        switch (args[0]) {
            case "-e":
                mode = Mode.ENCRYPTING;
                break;

            case "-d":
                mode = Mode.DECRYPTING;
                break;
        }

        if (mode == null) {
            printHelpMessage = true;
            return;
        }

        if (args[1].length() >= 2 && 
           (args[1].startsWith("0x") || args[1].startsWith("0X"))) {
            String keyString = args[1].substring(2).trim().toLowerCase();

            try {
                key = Integer.parseInt(keyString, 16);
            } catch (NumberFormatException ex) {
                printHelpMessage = true;
                return;
            }
        } else {
            try {
                key = Integer.parseInt(args[1]);
            } catch (NumberFormatException ex) {
                printHelpMessage = true;
                return;
            }
        }
    }

    public boolean helpMessageRequested() {
        return printHelpMessage;
    }

    public void exec() {
        if (graphicalInterfaceRequested) {
            SwingUtilities.invokeLater(() -> { execAsGUI(); });
        } else {
            execAsCommandLine();
        }
    }

    private void execAsGUI() {
        JFrame frame = new JFrame("File cipher tool");
        JButton buttonEncrypt = new JButton("Encrypt");
        JButton buttonDecrypt = new JButton("Decrypt");

        buttonEncrypt.setPreferredSize(new Dimension(200, 40));
        buttonDecrypt.setPreferredSize(new Dimension(200, 40));

        buttonEncrypt.addActionListener(
                new MyActionListener(frame, Mode.ENCRYPTING));

        buttonDecrypt.addActionListener(
                new MyActionListener(frame, Mode.DECRYPTING));

        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(buttonEncrypt, BorderLayout.NORTH);
        frame.getContentPane().add(buttonDecrypt, BorderLayout.SOUTH);

        frame.pack();
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        moveToCenter(frame);
        frame.setVisible(true);
    }

    private static void moveToCenter(JFrame frame) {
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setLocation((screen.width  - frame.getWidth())  >> 1,
                          (screen.height - frame.getHeight()) >> 1);
    } 

    private void execAsCommandLine() {
        List<File> fileList = getFileList(args);

        switch (mode) {
            case ENCRYPTING:
                encryptAll(fileList, key);
                break;

            case DECRYPTING:
                decryptAll(fileList, key);
                break;
        }

    }

    static void encryptAll(List<File> fileList, int key) {
        fileList.stream().forEach((File file) -> {
            try {
                byte[] data = FileTools.readFile(file);
                byte[] encryptedData = CipherTools.encrypt(data, key);
                FileTools.writeFile(file, encryptedData);
            } catch (IOException ex) {
                System.err.println("ERROR: " + ex.getMessage());
            }
        });
    }

    static void decryptAll(List<File> fileList, int key) {
        fileList.stream().forEach((File file) -> {
            try {
                byte[] data = FileTools.readFile(file);
                byte[] decryptedData = CipherTools.decrypt(data, key);
                FileTools.writeFile(file, decryptedData);
            } catch (IOException ex) {
                System.err.println("ERROR: " + ex.getMessage());
            }
        });
    }

    private List<File> getFileList(String[] args) {
        // We subtract 2 in order to omit the fist two arguments in 'args' that
        // are the switch and the key.
        List<File> fileList = new ArrayList<>(args.length - 2);

        for (int i = 2; i < args.length; ++i) {
            fileList.add(new File(args[i]));
        }

        return fileList;
    }

    private String getHelpMessage() {
        return HELP_MESSAGE;
    }

    public static void main(String[] args) throws IOException {
        try {
            App app = new App();
            app.processArguments(args);

            if (app.helpMessageRequested()) {
                System.out.println(app.getHelpMessage());
                System.exit(0);
            }

            app.exec();
        } catch (RuntimeException ex) {
            System.err.println("ERROR: " + ex.getMessage());
            System.exit(1);
        }
    }
}

MyActionListener.java:

package net.coderodde.ciphertool;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import java.util.Objects;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import static net.coderodde.ciphertool.App.decryptAll;
import static net.coderodde.ciphertool.App.encryptAll;

/**
 * This class implements the action listener for the GUI buttons.
 *
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 1, 2016)
 */
final class MyActionListener implements ActionListener {

    private final JFrame ownerFrame;
    private final Mode mode;

    MyActionListener(JFrame ownerFrame, Mode mode) {
        this.mode = Objects.requireNonNull(mode, "The input mode is null.");
        this.ownerFrame = ownerFrame;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        File[] files = askUserToChooseFiles(mode == Mode.ENCRYPTING ? 
                                            "Choose files to encrypt" :
                                            "Choose files to decrypt",
                                            ownerFrame);
        if (files == null) {
            return;
        }

        String keyString = JOptionPane.showInputDialog(
                                       ownerFrame,
                                       mode == Mode.ENCRYPTING ?
                                       "Type in the encryption key:" :
                                       "Type in the decryption key:",
                                       "",
                                       JOptionPane.QUESTION_MESSAGE);

        if (keyString == null || keyString.isEmpty()) {
            // User cancelled the entry of the key.
            return;
        }

        if (keyString.length() >= 2
                && (keyString.startsWith("0x")
                || keyString.startsWith("0X"))) {
            String keyStringPrepared = keyString.substring(2)
                    .trim()
                    .toLowerCase();

            try {
                int key = Integer.parseInt(keyStringPrepared, 16);

                switch (mode) {
                    case ENCRYPTING: 
                        encryptAll(Arrays.asList(files), key);
                        break;

                    case DECRYPTING:
                        decryptAll(Arrays.asList(files), key);
                        break;

                    default:
                        throw new IllegalStateException(
                                "Should never get here.");
                }
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(
                        ownerFrame,
                        "\"" + keyString + "\" is an invalid key.",
                        "",
                        JOptionPane.ERROR_MESSAGE);
            }
        } else {
            try {
                int key = Integer.parseInt(keyString);
                switch (mode) {
                    case ENCRYPTING: 
                        encryptAll(Arrays.asList(files), key);
                        break;

                    case DECRYPTING:
                        decryptAll(Arrays.asList(files), key);
                        break;

                    default:
                        throw new IllegalStateException(
                                "Should never get here.");
                }
            } catch (NumberFormatException ex) {
                JOptionPane.showMessageDialog(
                        ownerFrame,
                        "\"" + keyString + "\" is an invalid key.",
                        "",
                        JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    private File[] askUserToChooseFiles(String title, JFrame ownerFrame) {
        JFileChooser chooser = new JFileChooser(title);
        chooser.setMultiSelectionEnabled(true);
        int status = chooser.showOpenDialog(ownerFrame);

        if (status == JFileChooser.APPROVE_OPTION) {
            return chooser.getSelectedFiles();
        }

        return null;
    }
}

Mode.java:

package net.coderodde.ciphertool;

/**
 * The enumeration enumerates the function modes of the program.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 1, 2016)
 */
enum Mode {
    ENCRYPTING, 
    DECRYPTING,
}

Please, tell me anything that comes to mind.

\$\endgroup\$
2
  • \$\begingroup\$ What method do you use to encrypt and decrypt? \$\endgroup\$
    – SCriLL
    Commented Apr 8, 2019 at 16:21
  • \$\begingroup\$ @SCriLL Caesar cipher. \$\endgroup\$
    – coderodde
    Commented Apr 9, 2019 at 6:45

1 Answer 1

4
\$\begingroup\$

Specifying a key

From a UX perspective, I think it will help if you can also indicate that the key should either be prefixed with 0x, so that you will treat it as a hexadecimal value, or it should be an integer.

Code duplication

And speaking about parsing the input keys, there's some code duplication in App.processArguments(String[]) and MyActionListener.actionPerformed(ActionEvent). You should consider refactoring the integer parsing part out so that it can be shared in both places.

Getting a file list

private List<File> getFileList(String[] args) {
    // We subtract 2 in order to omit the fist two arguments in 'args' that
    // are the switch and the key.
    List<File> fileList = new ArrayList<>(args.length - 2);

    for (int i = 2; i < args.length; ++i) {
        fileList.add(new File(args[i]));
    }

    return fileList;
}

I think it'll be better if a processed args array without the initial command-line arguments is passed in here. Anyways, assuming if you prefer to keep to the current approach, you can use the familiar stream processing for this too:

private List<File> getFileList(String[] args) {
    return Arrays.stream(args)
                    .skip(2)
                    .map(File::new)
                    .collect(Collectors.toList());
}

In fact, this will make refactoring easier when args no longer have the additional arguments: you can just remove the line .skip(2).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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