se.trixon.toolbox.checksum.ChecksumTopComponent.java Source code

Java tutorial

Introduction

Here is the source code for se.trixon.toolbox.checksum.ChecksumTopComponent.java

Source

/*
 * Copyright 2015 Patrik Karlsson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.trixon.toolbox.checksum;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JFileChooser;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.StatusDisplayer;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.TaskListener;
import org.openide.windows.TopComponent;
import se.trixon.almond.SystemHelper;
import se.trixon.almond.dialogs.FileChooserPanel;
import se.trixon.almond.dialogs.Message;
import se.trixon.almond.dialogs.SimpleDialog;
import se.trixon.almond.dictionary.Dict;
import se.trixon.almond.icon.Pict;
import se.trixon.toolbox.core.Toolbox;
import se.trixon.toolbox.core.base.ToolTopComponent;

/**
 * Checksum top component.
 */
@ConvertAsProperties(dtd = "-//se.trixon.toolbox.checksum//Checksum//EN", autostore = false)
@TopComponent.Description(preferredID = "ChecksumTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE",
        persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
public final class ChecksumTopComponent extends ToolTopComponent
        implements FileChooserPanel.FileChooserButtonListener {

    private static final int ICON_SIZE = TOOLBAR_ICON_SIZE;
    private final ArrayList<ChecksumRow> mChecksumRows = new ArrayList<>();
    private final ChecksumController mController;
    private String mAlgorithm;
    private File mChecksumFilePrefix;

    public ChecksumTopComponent() {
        mBundle = NbBundle.getBundle(ChecksumTopComponent.class);
        mToolName = mBundle.getString("Tool-Name");
        initComponents();
        setName(mToolName);
        mController = new ChecksumController(this);
        initAlgorithms();
        init();
    }

    @Override
    public void onFileChooserCancel(FileChooserPanel fileChooserPanel) {
        // nvm
    }

    @Override
    public void onFileChooserCheckBoxChange(FileChooserPanel fileChooserPanel, boolean isSelected) {
        // nvm
    }

    @Override
    public void onFileChooserDrop(FileChooserPanel fileChooserPanel) {
        if (fileChooserPanel == sourceChooserPanel) {
            logPanel.clear();
        }
    }

    @Override
    public void onFileChooserOk(FileChooserPanel fileChooserPanel, File file) {
        JFileChooser fileChooser = fileChooserPanel.getFileChooser();
        if (fileChooserPanel == sourceChooserPanel) {
            logPanel.clear();
            if (fileChooser.isMultiSelectionEnabled()) {
                String paths = StringUtils.join(fileChooser.getSelectedFiles(), SystemUtils.PATH_SEPARATOR);
                fileChooserPanel.setPath(paths);
            }
        }
    }

    @Override
    public void onFileChooserPreSelect(FileChooserPanel fileChooserPanel) {
        final String[] paths = sourceChooserPanel.getPath().split(SystemUtils.PATH_SEPARATOR);
        File[] files = new File[paths.length];

        for (int i = 0; i < files.length; i++) {
            files[i] = new File(paths[i]);
        }

        sourceChooserPanel.getFileChooser().setSelectedFiles(files);
    }

    private void init() {
        openButton.setIcon(Pict.Actions.DOCUMENT_OPEN.get(ICON_SIZE));
        openButton.setToolTipText(Dict.OPEN.getString());
        openButton.setVisible(false);

        saveButton.setIcon(Pict.Actions.DOCUMENT_SAVE.get(ICON_SIZE));
        saveButton.setToolTipText(Dict.SAVE_AS.getString());
        saveButton.setEnabled(false);

        clearButton.setIcon(Pict.Actions.EDIT_CLEAR.get(ICON_SIZE));
        clearButton.setToolTipText(Dict.CLEAR.getString());
        clearButton.setVisible(false);

        calcButton.setIcon(Pict.Actions.ARROW_RIGHT.get(ICON_SIZE));
        calcButton.setToolTipText(Dict.START.getString());

        compareButton.setIcon(Pict.Actions.MERGE.get(ICON_SIZE));
        compareButton.setToolTipText(Dict.COMPARE.getString());

        File file = new File(System.getProperty("user.home"), "checksum.zero");
        sourceChooserPanel.setDropMode(FileChooserPanel.DropMode.MULTI);
        sourceChooserPanel.setPath(file.getAbsolutePath());

        sourceChooserPanel.setHeader(Dict.SOURCE.getString());
        //        sourceChooserPanel.getButton().setVisible(false);
        sourceChooserPanel.setMode(JFileChooser.FILES_AND_DIRECTORIES);
        sourceChooserPanel.getFileChooser().setMultiSelectionEnabled(true);
        sourceChooserPanel.setButtonListener(this);

        checksumChooserPanel.setHeader(mBundle.getString("checksumChooser.header"));

        if (SystemUtils.IS_OS_WINDOWS) {
            algorithmComboBox.setMaximumSize(new Dimension(300, 32767));
        }
    }

    private void initAlgorithms() {
        String[] requests = new String[] { "MD2_disable", "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512" };
        ArrayList<String> algorithms = new ArrayList<>();

        for (String algorithm : requests) {
            try {
                MessageDigest md = MessageDigest.getInstance(algorithm);
                algorithms.add(algorithm);
            } catch (NoSuchAlgorithmException ex) {
                // nvm
            }
        }

        algorithms.add("CRC32");
        Collections.sort(algorithms);
        String[] array = algorithms.toArray(new String[algorithms.size()]);
        algorithmComboBox.setModel(new DefaultComboBoxModel(array));
        algorithmComboBox.setSelectedItem("MD5");
    }

    private String getRelativePath(File file, File base) {
        return StringUtils.substringAfter(file.getAbsolutePath(),
                base.getAbsolutePath() + SystemUtils.FILE_SEPARATOR);
    }

    /**
     * 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.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        toolBar = new javax.swing.JToolBar();
        openButton = new javax.swing.JButton();
        clearButton = new javax.swing.JButton();
        algorithmComboBox = new javax.swing.JComboBox();
        calcButton = new javax.swing.JButton();
        saveButton = new javax.swing.JButton();
        compareButton = new javax.swing.JButton();
        filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0),
                new java.awt.Dimension(32767, 0));
        sourceChooserPanel = new se.trixon.almond.dialogs.FileChooserPanel();
        checksumChooserPanel = new se.trixon.almond.dialogs.FileChooserPanel();
        logPanel = new se.trixon.almond.swing.LogPanel();

        toolBar.setFloatable(false);

        openButton.setFocusable(false);
        openButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        openButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openButtonActionPerformed(evt);
            }
        });
        toolBar.add(openButton);

        clearButton.setFocusable(false);
        clearButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        clearButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        clearButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                clearButtonActionPerformed(evt);
            }
        });
        toolBar.add(clearButton);

        algorithmComboBox.setToolTipText(org.openide.util.NbBundle.getMessage(ChecksumTopComponent.class,
                "ChecksumTopComponent.algorithmComboBox.toolTipText")); // NOI18N
        algorithmComboBox.setMaximumSize(new java.awt.Dimension(200, 32767));
        toolBar.add(algorithmComboBox);

        calcButton.setFocusable(false);
        calcButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                calcButtonActionPerformed(evt);
            }
        });
        toolBar.add(calcButton);

        saveButton.setFocusable(false);
        saveButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        saveButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        saveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveButtonActionPerformed(evt);
            }
        });
        toolBar.add(saveButton);

        compareButton.setFocusable(false);
        compareButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        compareButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolBar.add(compareButton);
        toolBar.add(filler1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(toolBar, javax.swing.GroupLayout.DEFAULT_SIZE, 546, Short.MAX_VALUE)
                .addGroup(layout.createSequentialGroup().addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(sourceChooserPanel, javax.swing.GroupLayout.Alignment.TRAILING,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(checksumChooserPanel, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(logPanel, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addContainerGap()));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                        .addComponent(toolBar, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(sourceChooserPanel, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(checksumChooserPanel, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(logPanel,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE,
                                Short.MAX_VALUE)));
    }// </editor-fold>//GEN-END:initComponents

    private void calcButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_calcButtonActionPerformed
        logPanel.clear();
        new CalculateChecksumAction().actionPerformed(evt);
    }//GEN-LAST:event_calcButtonActionPerformed

    private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
        logPanel.clear();
    }//GEN-LAST:event_clearButtonActionPerformed

    private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
        SimpleDialog.setParent(openButton.getTopLevelAncestor());
        if (SimpleDialog.openFileAndDirectoy(true)) {
            String paths = StringUtils.join(SimpleDialog.getPaths(), SystemUtils.PATH_SEPARATOR);
            sourceChooserPanel.setPath(paths);
            logPanel.clear();
        }
    }//GEN-LAST:event_openButtonActionPerformed

    private void requestFileObject() {
        SimpleDialog.setParent(this);
        String fileName = String.format("%s.%s", mChecksumFilePrefix.getAbsolutePath(), mAlgorithm);
        File file = new File(fileName);
        SimpleDialog.setPath(file.getParentFile());
        SimpleDialog.setSelectedFile(file);

        if (SimpleDialog.saveFile()) {
            saveFile(SimpleDialog.getPath());
        }
    }

    private void saveFile(File file) {
        Calendar calendar = Calendar.getInstance();

        String header = String.format("#Created by %s@%s on %s with TT Checksum, www.trixon.se/toolbox\n",
                System.getProperty("user.name"), SystemHelper.getHostname(), calendar.getTime());
        try {
            FileUtils.write(file, header);
            FileUtils.writeLines(file, mChecksumRows, true);
            FileUtils.writeStringToFile(file, "\n", true);
        } catch (IOException ex) {
            Message.error(Dict.IO_ERROR_TITLE.getString(),
                    String.format(Dict.FILE_ERROR_WRITE_MESSAGE.getString(), file.getAbsolutePath()));
        }
    }

    private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed
        requestFileObject();
    }//GEN-LAST:event_saveButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox algorithmComboBox;
    private javax.swing.JButton calcButton;
    private se.trixon.almond.dialogs.FileChooserPanel checksumChooserPanel;
    private javax.swing.JButton clearButton;
    private javax.swing.JButton compareButton;
    private javax.swing.Box.Filler filler1;
    private se.trixon.almond.swing.LogPanel logPanel;
    private javax.swing.JButton openButton;
    private javax.swing.JButton saveButton;
    private se.trixon.almond.dialogs.FileChooserPanel sourceChooserPanel;
    private javax.swing.JToolBar toolBar;
    // End of variables declaration//GEN-END:variables

    @Override
    public void componentOpened() {
    }

    @Override
    public void componentClosed() {
    }

    void writeProperties(java.util.Properties p) {
        p.setProperty("version", "1.0");
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
    }

    class CalculateChecksumAction implements ActionListener {

        private File mBaseDir = null;
        private final List<Exception> mExceptions = new ArrayList<>();
        private boolean mInterrupted = false;
        private final List<String> mRelativePaths = new ArrayList<>();
        private final RequestProcessor mRequestProcessor = new RequestProcessor("interruptible tasks", 1, true);
        private String mTextPaths;

        @Override
        public void actionPerformed(ActionEvent e) {
            mChecksumRows.clear();
            mTextPaths = sourceChooserPanel.getTextField().getText().trim();
            if (mTextPaths.length() == 0) {
                return;
            }

            CalculateRunnable runnable = new CalculateRunnable();
            final RequestProcessor.Task task = mRequestProcessor.create(runnable);
            final ProgressHandle progressHandle = ProgressHandleFactory.createHandle(mToolName, task);

            task.addTaskListener(new TaskListener() {
                @Override
                public void taskFinished(org.openide.util.Task task) {
                    progressHandle.finish();
                    enableToolbar(true);
                    saveButton.setEnabled(!mChecksumRows.isEmpty() && !mInterrupted);
                }
            });

            progressHandle.start();
            task.schedule(0);
        }

        private String calculateFile(File file, String algorithm) throws FileNotFoundException, IOException {
            String sum = null;
            InputStream inputStream = null;

            try {
                if (!algorithm.equalsIgnoreCase("crc32")) {
                    inputStream = new FileInputStream(file);
                }
                switch (algorithm) {
                case "crc32":
                    sum = String.format("%08x", FileUtils.checksumCRC32(file));
                    break;
                case "md2":
                    sum = DigestUtils.md2Hex(inputStream);
                    break;
                case "md5":
                    sum = DigestUtils.md5Hex(inputStream);
                    break;
                case "sha1":
                    sum = DigestUtils.sha1Hex(inputStream);
                    break;
                case "sha256":
                    sum = DigestUtils.sha256Hex(inputStream);
                    break;
                case "sha384":
                    sum = DigestUtils.sha384Hex(inputStream);
                    break;
                case "sha512":
                    sum = DigestUtils.sha512Hex(inputStream);
                    break;

                default:
                }
            } catch (FileNotFoundException ex) {
                throw ex;
            } catch (IOException ex) {
                throw ex;
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (IOException ex) {
                    throw ex;
                }
            }

            return sum;
        }

        private void enableToolbar(boolean state) {
            openButton.setEnabled(state);
            saveButton.setEnabled(state);
            clearButton.setEnabled(state);
            compareButton.setEnabled(state);
            calcButton.setEnabled(state);
            algorithmComboBox.setEnabled(state);
        }

        private boolean initFileList() {
            logPanel.println(Dict.GENERATING_FILELIST.getString());

            boolean invalidPath = false;

            File[] files = sourceChooserPanel.getPaths();
            if (files != null) {
                for (File file : files) {
                    String path = file.getAbsolutePath();
                    if (path.contains(SystemUtils.PATH_SEPARATOR)) {
                        invalidPath = true;
                        String message = String.format(Dict.INVALID_PATH.getString(), file.getAbsolutePath());
                        logPanel.println(message);
                    }
                }

                if (invalidPath) {
                    String message = String.format(Dict.ERROR_PATH_SEPARATOR.getString(),
                            SystemUtils.PATH_SEPARATOR);
                    Message.error(Dict.IO_ERROR_TITLE.getString(), message);
                }
            }

            final String[] paths = mTextPaths.split(SystemUtils.PATH_SEPARATOR);

            File file0 = new File(paths[0]);
            if (paths.length == 1 && file0.isDirectory()) {
                mBaseDir = file0.getAbsoluteFile();
            } else {
                mBaseDir = file0.getParentFile();
            }

            if (paths.length == 1) {
                if (file0.isDirectory()) {
                    mChecksumFilePrefix = new File(file0, file0.getName());
                } else if (file0.isFile()) {
                    mChecksumFilePrefix = file0.getAbsoluteFile();
                }
            } else {
                mChecksumFilePrefix = new File(mBaseDir, mBaseDir.getName());
            }

            EnumSet<FileVisitOption> fileVisitOptions = EnumSet.of(FileVisitOption.FOLLOW_LINKS);

            for (String path : paths) {
                File file = new File(path);
                if (file.isDirectory()) {
                    FileVisitor fileVisitor = new FileVisitor(mBaseDir, mRelativePaths);
                    try {
                        Files.walkFileTree(file.toPath(), fileVisitOptions, Integer.MAX_VALUE, fileVisitor);
                        if (fileVisitor.isInterrupted()) {
                            return false;
                        }
                    } catch (IOException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                } else if (file.isFile()) {
                    mRelativePaths.add(getRelativePath(file, mBaseDir));
                }
            }

            if (mRelativePaths.isEmpty()) {
                logPanel.println(Dict.FILELIST_EMPTY.getString());
            } else {
                Collections.sort(mRelativePaths);
            }

            //            for (String s : mRelativePaths) {
            //                logPanel.println(s);
            //            }
            return true;
        }

        class CalculateRunnable implements Runnable {

            @Override
            public void run() {
                long startTime = System.currentTimeMillis();
                enableToolbar(false);
                mInterrupted = !initFileList();
                String status;

                if (!mInterrupted && !mRelativePaths.isEmpty()) {
                    status = String.format("\n%s\n", mBundle.getString("calculating_checksums"));
                    logPanel.println(status);
                    mAlgorithm = algorithmComboBox.getSelectedItem().toString().toLowerCase().replace("-", "");

                    for (String relativePath : mRelativePaths) {
                        File file = new File(mBaseDir, relativePath);
                        Toolbox.setStatusText(relativePath, StatusDisplayer.IMPORTANCE_ERROR_HIGHLIGHT);

                        try {
                            String sum = calculateFile(file, mAlgorithm);
                            ChecksumRow checksumRow = new ChecksumRow(sum, relativePath);
                            mChecksumRows.add(checksumRow);
                            logPanel.println(new ChecksumRow(sum, relativePath).toString());
                        } catch (FileNotFoundException ex) {
                            mExceptions.add(ex);
                        } catch (IOException ex) {
                            mExceptions.add(ex);
                        }

                        if (Thread.interrupted()) {
                            mInterrupted = true;
                            break;
                        }
                    }
                }

                if (mInterrupted) {
                    status = Dict.TASK_ABORTED.getString();
                } else {
                    for (Exception exception : mExceptions) {
                        logPanel.println(String.format("#%s", exception.getLocalizedMessage()));
                    }

                    long millis = System.currentTimeMillis() - startTime;
                    long min = TimeUnit.MILLISECONDS.toMinutes(millis);
                    long sec = TimeUnit.MILLISECONDS.toSeconds(millis)
                            - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));
                    status = String.format("%s (%d %s, %d %s)", Dict.TASK_COMPLETED.getString(), min,
                            Dict.TIME_MIN.getString(), sec, Dict.TIME_SEC.getString());
                }

                logPanel.println("\n" + status);
                Toolbox.setStatusText("", StatusDisplayer.IMPORTANCE_ERROR_HIGHLIGHT);
            }
        }
    }
}