richtercloud.document.scanner.gui.OCRPanel.java Source code

Java tutorial

Introduction

Here is the source code for richtercloud.document.scanner.gui.OCRPanel.java

Source

/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package richtercloud.document.scanner.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JTextArea;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import richtercloud.document.scanner.gui.conf.DocumentScannerConf;
import richtercloud.document.scanner.setter.ValueSetter;
import richtercloud.reflection.form.builder.ClassInfo;
import richtercloud.reflection.form.builder.FieldInfo;
import richtercloud.reflection.form.builder.ReflectionFormBuilder;
import richtercloud.reflection.form.builder.ReflectionFormPanel;
import richtercloud.reflection.form.builder.message.Message;
import richtercloud.reflection.form.builder.message.MessageHandler;

/**
 * The counterpart of every {@link OCRSelectPanel} which contains a text field
 * to display OCR selection results and a toolbar with buttons and popup menus
 * to select different number formats for setting OCR selections on fields.
 * @author richter
 */
/*
internal implementation notes:
- popup menu has a single menu "Paste into" rather than a disabled menu item
which is (mis)used as label for the following menu items because that's more
elegant even if less easy to use (means one click more)
- Since NumberFormats are hard to compare (NumberFormat.equals isn't
implemented and a lot of properties aren't suitable for comparison (comparing
groupingUsed, parseIntegerOnly, maximumFractionDigits, maximumIntegerDigits,
minimumFractionDigits and minimumIntegerDigits for equality results in 2
different number formats which doesn't make sense and doesn't match with a list
of all available formats); providing selection for all 160 available locales is
overkill and strangely results in > 20 identical format results of "-12345,987"
-> compare format result of "-12345,987"
*/
public class OCRPanel extends javax.swing.JPanel {
    private static final long serialVersionUID = 1L;
    private final static Logger LOGGER = LoggerFactory.getLogger(OCRPanel.class);
    private final MessageHandler messageHandler;
    private final DocumentScannerConf documentScannerConf;
    private final static double FORMAT_VALUE = -12345.987;

    /**
     * Creates new form OCRResultPanel
     * @param reflectionFormPanelMap A map with references to the
     * {@link ReflectionFormPanel} for each entity class which is manipulated by
     * the context menu items
     */
    public OCRPanel(Set<Class<?>> entityClasses, Map<Class<?>, ReflectionFormPanel<?>> reflectionFormPanelMap,
            Map<Class<? extends JComponent>, ValueSetter<?, ?>> valueSetterMapping, EntityManager entityManager,
            MessageHandler messageHandler, ReflectionFormBuilder reflectionFormBuilder,
            DocumentScannerConf documentScannerConf) {
        this.initComponents();
        if (messageHandler == null) {
            throw new IllegalArgumentException("messageHandler mustn't be null");
        }
        this.messageHandler = messageHandler;
        if (documentScannerConf == null) {
            throw new IllegalArgumentException("documentScannerConf mustn't be " + "null");
        }
        this.documentScannerConf = documentScannerConf;
        List<Class<?>> entityClassesSort = EntityPanel.sortEntityClasses(entityClasses);
        for (Class<?> entityClass : entityClassesSort) {
            ReflectionFormPanel<?> reflectionFormPanel = reflectionFormPanelMap.get(entityClass);
            if (reflectionFormPanel == null) {
                throw new IllegalArgumentException(
                        String.format("entityClass %s has no %s mapped in reflectionFormPanelMap", entityClass,
                                ReflectionFormPanel.class));
            }
            String className;
            ClassInfo classInfo = entityClass.getAnnotation(ClassInfo.class);
            if (classInfo != null) {
                className = classInfo.name();
            } else {
                className = entityClass.getSimpleName();
            }
            JMenu entityClassMenu = new JMenu(className);
            List<Field> relevantFields = reflectionFormBuilder.getFieldRetriever()
                    .retrieveRelevantFields(entityClass);
            for (Field relevantField : relevantFields) {
                String fieldName;
                FieldInfo fieldInfo = relevantField.getAnnotation(FieldInfo.class);
                if (fieldInfo != null) {
                    fieldName = fieldInfo.name();
                } else {
                    fieldName = relevantField.getName();
                }
                JMenuItem relevantFieldMenuItem = new JMenuItem(fieldName);
                relevantFieldMenuItem.addActionListener(
                        new FieldActionListener(reflectionFormPanel, relevantField, valueSetterMapping));
                entityClassMenu.add(relevantFieldMenuItem);
            }
            this.oCRResultPopupPasteIntoMenu.add(entityClassMenu);
        }
        Map<String, Pair<NumberFormat, Set<Locale>>> numberFormats = new HashMap<>();
        Map<String, Pair<NumberFormat, Set<Locale>>> percentFormats = new HashMap<>();
        Map<String, Pair<NumberFormat, Set<Locale>>> currencyFormats = new HashMap<>();
        Iterator<Locale> localeIterator = new ArrayList<>(Arrays.asList(Locale.getAvailableLocales())).iterator();
        Locale firstLocale = localeIterator.next();
        String numberString = NumberFormat.getNumberInstance(firstLocale).format(FORMAT_VALUE);
        String percentString = NumberFormat.getPercentInstance(firstLocale).format(FORMAT_VALUE);
        String currencyString = NumberFormat.getCurrencyInstance(firstLocale).format(FORMAT_VALUE);
        numberFormats.put(numberString, new ImmutablePair<NumberFormat, Set<Locale>>(
                NumberFormat.getNumberInstance(firstLocale), new HashSet<>(Arrays.asList(firstLocale))));
        percentFormats.put(percentString, new ImmutablePair<NumberFormat, Set<Locale>>(
                NumberFormat.getPercentInstance(firstLocale), new HashSet<>(Arrays.asList(firstLocale))));
        currencyFormats.put(currencyString, new ImmutablePair<NumberFormat, Set<Locale>>(
                NumberFormat.getCurrencyInstance(firstLocale), new HashSet<>(Arrays.asList(firstLocale))));
        while (localeIterator.hasNext()) {
            Locale locale = localeIterator.next();
            numberString = NumberFormat.getNumberInstance(locale).format(FORMAT_VALUE);
            percentString = NumberFormat.getPercentInstance(locale).format(FORMAT_VALUE);
            currencyString = NumberFormat.getCurrencyInstance(locale).format(FORMAT_VALUE);
            Pair<NumberFormat, Set<Locale>> numberFormatsPair = numberFormats.get(numberString);
            if (numberFormatsPair == null) {
                numberFormatsPair = new ImmutablePair<NumberFormat, Set<Locale>>(
                        NumberFormat.getNumberInstance(locale), new HashSet<Locale>());
                numberFormats.put(numberString, numberFormatsPair);
            }
            Set<Locale> numberFormatsLocales = numberFormatsPair.getValue();
            numberFormatsLocales.add(locale);
            Pair<NumberFormat, Set<Locale>> percentFormatsPair = percentFormats.get(percentString);
            if (percentFormatsPair == null) {
                percentFormatsPair = new ImmutablePair<NumberFormat, Set<Locale>>(
                        NumberFormat.getPercentInstance(locale), new HashSet<Locale>());
                percentFormats.put(percentString, percentFormatsPair);
            }
            Set<Locale> percentFormatsLocales = percentFormatsPair.getValue();
            percentFormatsLocales.add(locale);
            Pair<NumberFormat, Set<Locale>> currencyFormatsPair = currencyFormats.get(currencyString);
            if (currencyFormatsPair == null) {
                currencyFormatsPair = new ImmutablePair<NumberFormat, Set<Locale>>(
                        NumberFormat.getCurrencyInstance(locale), new HashSet<Locale>());
                currencyFormats.put(currencyString, currencyFormatsPair);
            }
            Set<Locale> currencyFormatsLocales = currencyFormatsPair.getValue();
            currencyFormatsLocales.add(locale);
        }
        for (Map.Entry<String, Pair<NumberFormat, Set<Locale>>> numberFormat : numberFormats.entrySet()) {
            JRadioButtonMenuItem menuItem = new NumberFormatMenuItem(numberFormat.getValue().getKey());
            numberFormatPopup.add(menuItem);
            numberFormatPopupButtonGroup.add(menuItem);
            if (numberFormat.getValue().getValue().contains(this.documentScannerConf.getLocale())) {
                menuItem.setSelected(true);
            }
        }
        for (Map.Entry<String, Pair<NumberFormat, Set<Locale>>> percentFormat : percentFormats.entrySet()) {
            JRadioButtonMenuItem menuItem = new NumberFormatMenuItem(percentFormat.getValue().getKey());
            percentFormatPopup.add(menuItem);
            percentFormatPopupButtonGroup.add(menuItem);
            if (percentFormat.getValue().getValue().contains(this.documentScannerConf.getLocale())) {
                menuItem.setSelected(true);
            }
        }
        for (Map.Entry<String, Pair<NumberFormat, Set<Locale>>> currencyFormat : currencyFormats.entrySet()) {
            JRadioButtonMenuItem menuItem = new NumberFormatMenuItem(currencyFormat.getValue().getKey());
            currencyFormatPopup.add(menuItem);
            currencyFormatPopupButtonGroup.add(menuItem);
            if (currencyFormat.getValue().getValue().contains(this.documentScannerConf.getLocale())) {
                menuItem.setSelected(true);
            }
        }
    }

    private class NumberFormatMenuItem extends JRadioButtonMenuItem {
        private static final long serialVersionUID = 1L;
        private final NumberFormat numberFormat;

        NumberFormatMenuItem(NumberFormat numberFormat) {
            super(numberFormat.format(FORMAT_VALUE));
            this.numberFormat = numberFormat;
        }
    }

    public JTextArea getoCRResultTextArea() {
        return this.oCRResultTextArea;
    }

    private class FieldActionListener implements ActionListener {
        private final Field field;
        private final ReflectionFormPanel reflectionFormPanel;
        private final Map<Class<? extends JComponent>, ValueSetter<?, ?>> valueSetterMapping;

        FieldActionListener(ReflectionFormPanel reflectionFormPanel, Field field,
                Map<Class<? extends JComponent>, ValueSetter<?, ?>> valueSetterMapping) {
            this.field = field;
            this.reflectionFormPanel = reflectionFormPanel;
            this.valueSetterMapping = valueSetterMapping;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JComponent comp = this.reflectionFormPanel.getComponentByField(this.field);
            String oCRSelection = OCRPanel.this.oCRResultTextArea.getSelectedText();
            ValueSetter valueSetter = this.valueSetterMapping.get(comp.getClass());
            if (valueSetter == null) {
                throw new IllegalArgumentException(
                        String.format("No %s mapped " + "to component %s", ValueSetter.class, comp));
            }
            //cast outside try-catch block in order to cause a
            //ClassCastException in case of concept error which shouldn't be
            //caught
            NumberFormat numberFormat = null;
            NumberFormat percentFormat = null;
            NumberFormat currencyFormat = null;
            for (AbstractButton button : Collections.list(numberFormatPopupButtonGroup.getElements())) {
                NumberFormatMenuItem menuItem = (NumberFormatMenuItem) button;
                if (menuItem.isSelected()) {
                    numberFormat = menuItem.numberFormat;
                    break;
                }
            }
            for (AbstractButton button : Collections.list(percentFormatPopupButtonGroup.getElements())) {
                NumberFormatMenuItem menuItem = (NumberFormatMenuItem) button;
                if (menuItem.isSelected()) {
                    percentFormat = menuItem.numberFormat;
                    break;
                }
            }
            for (AbstractButton button : Collections.list(currencyFormatPopupButtonGroup.getElements())) {
                NumberFormatMenuItem menuItem = (NumberFormatMenuItem) button;
                if (menuItem.isSelected()) {
                    currencyFormat = menuItem.numberFormat;
                    break;
                }
            }
            try {
                valueSetter.setValue(new FormatOCRResult(numberFormat, percentFormat, currencyFormat, oCRSelection),
                        comp);
            } catch (Exception ex) {
                LOGGER.error("An exception during setting the OCR value on " + "component occured", ex);
                messageHandler.handle(new Message(
                        String.format(
                                "The " + "following exception occured while setting the "
                                        + "selected value on the field: %s",
                                ExceptionUtils.getRootCauseMessage(ex)),
                        JOptionPane.ERROR_MESSAGE, "Exception occured"));
            }
        }
    }

    /**
     * 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">//GEN-BEGIN:initComponents
    private void initComponents() {

        oCRResultPopup = new javax.swing.JPopupMenu();
        oCRResultPopupPasteIntoMenu = new javax.swing.JMenu();
        numberFormatPopup = new javax.swing.JPopupMenu();
        numberFormatPopupButtonGroup = new javax.swing.ButtonGroup();
        percentFormatPopup = new javax.swing.JPopupMenu();
        currencyFormatPopup = new javax.swing.JPopupMenu();
        percentFormatPopupButtonGroup = new javax.swing.ButtonGroup();
        currencyFormatPopupButtonGroup = new javax.swing.ButtonGroup();
        oCRResultLabel = new javax.swing.JLabel();
        oCRResultTextAreaScrollPane = new javax.swing.JScrollPane();
        oCRResultTextArea = new javax.swing.JTextArea();
        toolbar = new javax.swing.JToolBar();
        numberFormatButton = new javax.swing.JButton();
        percentFormatButton = new javax.swing.JButton();
        currencyFormatButton = new javax.swing.JButton();

        oCRResultPopupPasteIntoMenu.setText("Paste into");
        oCRResultPopup.add(oCRResultPopupPasteIntoMenu);

        oCRResultLabel.setText("OCR result");

        oCRResultTextArea.setColumns(20);
        oCRResultTextArea.setRows(5);
        oCRResultTextArea.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                oCRResultTextAreaMouseClicked(evt);
            }
        });
        oCRResultTextAreaScrollPane.setViewportView(oCRResultTextArea);

        toolbar.setRollover(true);
        toolbar.setToolTipText("formats");

        numberFormatButton.setText("Number");
        numberFormatButton.setFocusable(false);
        numberFormatButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        numberFormatButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        numberFormatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                numberFormatButtonActionPerformed(evt);
            }
        });
        toolbar.add(numberFormatButton);

        percentFormatButton.setText("Percentage");
        percentFormatButton.setFocusable(false);
        percentFormatButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        percentFormatButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        percentFormatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                percentFormatButtonActionPerformed(evt);
            }
        });
        toolbar.add(percentFormatButton);

        currencyFormatButton.setText("Currency");
        currencyFormatButton.setFocusable(false);
        currencyFormatButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        currencyFormatButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        currencyFormatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                currencyFormatButtonActionPerformed(evt);
            }
        });
        toolbar.add(currencyFormatButton);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup().addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(oCRResultTextAreaScrollPane)
                                .addGroup(layout.createSequentialGroup().addComponent(oCRResultLabel).addGap(0, 0,
                                        Short.MAX_VALUE)))
                        .addContainerGap())
                .addComponent(toolbar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE,
                        Short.MAX_VALUE));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(
                javax.swing.GroupLayout.Alignment.TRAILING,
                layout.createSequentialGroup().addContainerGap().addComponent(oCRResultLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(oCRResultTextAreaScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 141,
                                Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(toolbar,
                                javax.swing.GroupLayout.PREFERRED_SIZE, 25,
                                javax.swing.GroupLayout.PREFERRED_SIZE)));
    }// </editor-fold>//GEN-END:initComponents

    private void oCRResultTextAreaMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_oCRResultTextAreaMouseClicked
        if (evt.getButton() == MouseEvent.BUTTON3) {
            //right click
            this.oCRResultPopup.show(this.oCRResultTextArea, evt.getX(), evt.getY());
        }
    }//GEN-LAST:event_oCRResultTextAreaMouseClicked

    private void numberFormatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numberFormatButtonActionPerformed
        numberFormatPopup.show(toolbar, 0, 0);
    }//GEN-LAST:event_numberFormatButtonActionPerformed

    private void percentFormatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentFormatButtonActionPerformed
        percentFormatPopup.show(toolbar, 0, 0);
    }//GEN-LAST:event_percentFormatButtonActionPerformed

    private void currencyFormatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_currencyFormatButtonActionPerformed
        currencyFormatPopup.show(toolbar, 0, 0);
    }//GEN-LAST:event_currencyFormatButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton currencyFormatButton;
    private javax.swing.JPopupMenu currencyFormatPopup;
    private javax.swing.ButtonGroup currencyFormatPopupButtonGroup;
    private javax.swing.JButton numberFormatButton;
    private javax.swing.JPopupMenu numberFormatPopup;
    private javax.swing.ButtonGroup numberFormatPopupButtonGroup;
    private javax.swing.JLabel oCRResultLabel;
    private javax.swing.JPopupMenu oCRResultPopup;
    private javax.swing.JMenu oCRResultPopupPasteIntoMenu;
    private javax.swing.JTextArea oCRResultTextArea;
    private javax.swing.JScrollPane oCRResultTextAreaScrollPane;
    private javax.swing.JButton percentFormatButton;
    private javax.swing.JPopupMenu percentFormatPopup;
    private javax.swing.ButtonGroup percentFormatPopupButtonGroup;
    private javax.swing.JToolBar toolbar;
    // End of variables declaration//GEN-END:variables
}