Newer
Older
jrex / src / main / java / jrex / ui / MainFrame.java
package jrex.ui;

import jrex.ui.model.MutableTableModel;
import jrex.ui.model.PivotTableModel;
import com.google.common.collect.ListMultimap;
import com.google.gson.Gson;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Stream;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SpinnerNumberModel;
import javax.swing.UIManager;
import javax.swing.table.TableRowSorter;
import javax.swing.text.DefaultEditorKit;
import jrex.Expressions;

/**
 * Primary user interface frame.
 *
 * @author Mark George <mark.george@otago.ac.nz>
 */
public class MainFrame extends javax.swing.JFrame {

   private final Color defaultTextColor;

   public MainFrame() {
      initComponents();
      splitPane.setResizeWeight(0.66);
      spnColumns.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1));

      JPopupMenu rightClickMenu = new JPopupMenu();

      Action cut = new DefaultEditorKit.CutAction();
      cut.putValue(Action.NAME, "Cut");
      cut.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control X"));
      rightClickMenu.add(cut);

      Action copy = new DefaultEditorKit.CopyAction();
      copy.putValue(Action.NAME, "Copy");
      copy.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control C"));
      rightClickMenu.add(copy);

      Action paste = new DefaultEditorKit.PasteAction();
      paste.putValue(Action.NAME, "Paste");
      paste.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control V"));
      rightClickMenu.add(paste);

      Action tab = new DefaultEditorKit.InsertTabAction();
      tab.putValue(Action.NAME, "Insert Tab");
      rightClickMenu.add(tab);

      txtInput.setComponentPopupMenu(rightClickMenu);
      txtOutput.setComponentPopupMenu(rightClickMenu);
      txtRegex.setComponentPopupMenu(rightClickMenu);
      txtFormat.setComponentPopupMenu(rightClickMenu);
      txtBlanks.setComponentPopupMenu(rightClickMenu);

      defaultTextColor = txtRegex.getBackground();
      pnlReplace.setVisible(false);
      pnlReplace.setComponents(txtInput, txtOutput);

      Icon folderIcon = desaturateIcon(UIManager.getIcon("FileView.directoryIcon"));
      Icon floppyIcon = desaturateIcon(UIManager.getIcon("FileView.floppyDriveIcon"));
      Icon fileIcon = desaturateIcon(UIManager.getIcon("FileView.fileIcon"));

      btnSave.setIcon(floppyIcon);
      btnLoad.setIcon(folderIcon);
      btnClear.setIcon(fileIcon);
   }

   private Icon desaturateIcon(Icon icon) {
      int w = icon.getIconWidth();
      int h = icon.getIconHeight();
      GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice gd = ge.getDefaultScreenDevice();
      GraphicsConfiguration gc = gd.getDefaultConfiguration();
      BufferedImage iconImage = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
      Graphics2D g2d = iconImage.createGraphics();
      icon.paintIcon(null, g2d, 0, 0);
      ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
      op.filter(iconImage, iconImage);
      return new ImageIcon(iconImage);
   }

   /**
    * 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() {

      bgQuotedOut = new javax.swing.ButtonGroup();
      bgQuotedIn = new javax.swing.ButtonGroup();
      bgCase = new javax.swing.ButtonGroup();
      splitPane = new javax.swing.JSplitPane();
      pnlInput = new javax.swing.JPanel();
      btnClear = new javax.swing.JButton();
      btnLoad = new javax.swing.JButton();
      scrollInput = new javax.swing.JScrollPane();
      txtInput = new javax.swing.JTextArea();
      lblExpression = new javax.swing.JLabel();
      txtRegex = new javax.swing.JTextField();
      spnColumns = new javax.swing.JSpinner();
      btnTsvInput = new javax.swing.JButton();
      btnCsvInput = new javax.swing.JButton();
      btnMatch = new javax.swing.JButton();
      scrollTable = new javax.swing.JScrollPane();
      jTable = new javax.swing.JTable();
      cbReplace = new javax.swing.JCheckBox();
      btnPivotWideToLong = new javax.swing.JButton();
      btnPivotLongToWide = new javax.swing.JButton();
      btnTranspose = new javax.swing.JButton();
      lblColumns = new javax.swing.JLabel();
      btnSaveExpr = new javax.swing.JButton();
      btnLoadExpr = new javax.swing.JButton();
      cbQuotedIn = new javax.swing.JCheckBox();
      rbDoubleIn = new javax.swing.JRadioButton();
      rbSingleIn = new javax.swing.JRadioButton();
      cbGuess = new javax.swing.JCheckBox();
      cbHasHeadings = new javax.swing.JCheckBox();
      pnlOutput = new javax.swing.JPanel();
      lblFormat = new javax.swing.JLabel();
      txtFormat = new javax.swing.JTextField();
      btnFormat = new javax.swing.JButton();
      scrollOutput = new javax.swing.JScrollPane();
      txtOutput = new javax.swing.JTextArea();
      lblArgumentMarker = new javax.swing.JLabel();
      cbBlanks = new javax.swing.JCheckBox();
      txtBlanks = new javax.swing.JTextField();
      btnSwap = new javax.swing.JButton();
      btnCsvOutput = new javax.swing.JButton();
      btnTsvOutput = new javax.swing.JButton();
      txtMarker = new javax.swing.JTextField();
      btnSave = new javax.swing.JButton();
      cbTrim = new javax.swing.JCheckBox();
      cbQuotedOut = new javax.swing.JCheckBox();
      rbDoubleOut = new javax.swing.JRadioButton();
      rbSingleOut = new javax.swing.JRadioButton();
      cbCase = new javax.swing.JCheckBox();
      rbUpper = new javax.swing.JRadioButton();
      rbLower = new javax.swing.JRadioButton();
      rbUnique = new javax.swing.JCheckBox();
      pnlReplace = new jrex.ui.ReplacePanel();

      setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

      splitPane.setBorder(null);
      splitPane.setDividerLocation(366);
      splitPane.setDividerSize(15);
      splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
      splitPane.setName("splitPane"); // NOI18N
      splitPane.setOneTouchExpandable(true);

      pnlInput.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Input", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
      pnlInput.setName("pnlInput"); // NOI18N

      btnClear.setText("Clear");
      btnClear.setName("btnClear"); // NOI18N
      btnClear.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnClearActionPerformed(evt);
         }
      });

      btnLoad.setText("Open");
      btnLoad.setName("btnLoad"); // NOI18N
      btnLoad.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnLoadActionPerformed(evt);
         }
      });

      scrollInput.setName("scrollInput"); // NOI18N

      txtInput.setColumns(20);
      txtInput.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
      txtInput.setRows(5);
      txtInput.setName("txtInput"); // NOI18N
      scrollInput.setViewportView(txtInput);

      lblExpression.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
      lblExpression.setText("Capture Expression: ");
      lblExpression.setName("lblExpression"); // NOI18N

      txtRegex.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
      txtRegex.setName("txtRegex"); // NOI18N
      txtRegex.addKeyListener(new java.awt.event.KeyAdapter() {
         public void keyPressed(java.awt.event.KeyEvent evt) {
            txtRegexKeyPressed(evt);
         }
      });

      spnColumns.setName("spnColumns"); // NOI18N
      spnColumns.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
         public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
            spnColumnsMouseWheelMoved(evt);
         }
      });

      btnTsvInput.setText("TSV");
      btnTsvInput.setName("btnTsvInput"); // NOI18N
      btnTsvInput.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnTsvInputActionPerformed(evt);
         }
      });

      btnCsvInput.setText("CSV");
      btnCsvInput.setName("btnCsvInput"); // NOI18N
      btnCsvInput.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnCsvInputActionPerformed(evt);
         }
      });

      btnMatch.setMnemonic('c');
      btnMatch.setText("Capture");
      btnMatch.setName("btnMatch"); // NOI18N
      btnMatch.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnMatchActionPerformed(evt);
         }
      });

      scrollTable.setName("scrollTable"); // NOI18N

      jTable.setName("jTable"); // NOI18N
      scrollTable.setViewportView(jTable);

      cbReplace.setText("Search/Replace ");
      cbReplace.setName("cbReplace"); // NOI18N
      cbReplace.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            cbReplaceActionPerformed(evt);
         }
      });

      btnPivotWideToLong.setText("Pivot Wide to Long");
      btnPivotWideToLong.setName("btnPivotWideToLong"); // NOI18N
      btnPivotWideToLong.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnPivotWideToLongActionPerformed(evt);
         }
      });

      btnPivotLongToWide.setText("Pivot Long to Wide");
      btnPivotLongToWide.setName("btnPivotLongToWide"); // NOI18N
      btnPivotLongToWide.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnPivotLongToWideActionPerformed(evt);
         }
      });

      btnTranspose.setText("Transpose");
      btnTranspose.setName("btnTranspose"); // NOI18N
      btnTranspose.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnTransposeActionPerformed(evt);
         }
      });

      lblColumns.setText("Columns: ");
      lblColumns.setName("lblColumns"); // NOI18N

      btnSaveExpr.setText("Save Expressions");
      btnSaveExpr.setName("btnSaveExpr"); // NOI18N
      btnSaveExpr.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnSaveExprActionPerformed(evt);
         }
      });

      btnLoadExpr.setText("Load Expressions");
      btnLoadExpr.setName("btnLoadExpr"); // NOI18N
      btnLoadExpr.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnLoadExprActionPerformed(evt);
         }
      });

      cbQuotedIn.setText("Strip Quotes?");
      cbQuotedIn.setName("cbQuotedIn"); // NOI18N

      bgQuotedIn.add(rbDoubleIn);
      rbDoubleIn.setSelected(true);
      rbDoubleIn.setText("\"");
      rbDoubleIn.setName("rbDoubleIn"); // NOI18N

      bgQuotedIn.add(rbSingleIn);
      rbSingleIn.setText("'");
      rbSingleIn.setName("rbSingleIn"); // NOI18N

      cbGuess.setSelected(true);
      cbGuess.setText("Guess?");
      cbGuess.setName("cbGuess"); // NOI18N

      cbHasHeadings.setText("Has Header Row");
      cbHasHeadings.setName("cbHasHeadings"); // NOI18N
      cbHasHeadings.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            cbHasHeadingsActionPerformed(evt);
         }
      });

      javax.swing.GroupLayout pnlInputLayout = new javax.swing.GroupLayout(pnlInput);
      pnlInput.setLayout(pnlInputLayout);
      pnlInputLayout.setHorizontalGroup(
         pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(pnlInputLayout.createSequentialGroup()
            .addComponent(lblColumns)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(spnColumns, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(cbGuess)
            .addGap(18, 18, 18)
            .addComponent(cbQuotedIn)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(rbDoubleIn)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(rbSingleIn)
            .addGap(18, 18, 18)
            .addComponent(cbHasHeadings)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnCsvInput)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnTsvInput)
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         .addComponent(scrollInput)
         .addGroup(pnlInputLayout.createSequentialGroup()
            .addComponent(btnLoad)
            .addGap(18, 18, 18)
            .addComponent(cbReplace)
            .addGap(18, 18, 18)
            .addComponent(btnSaveExpr)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnLoadExpr)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 151, Short.MAX_VALUE)
            .addComponent(btnClear))
         .addGroup(pnlInputLayout.createSequentialGroup()
            .addGroup(pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addGroup(pnlInputLayout.createSequentialGroup()
                  .addComponent(btnPivotWideToLong)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(btnPivotLongToWide)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(btnTranspose)
                  .addGap(0, 0, Short.MAX_VALUE))
               .addGroup(pnlInputLayout.createSequentialGroup()
                  .addComponent(lblExpression)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(txtRegex)))
            .addGap(18, 18, 18)
            .addComponent(btnMatch))
         .addComponent(scrollTable)
      );

      pnlInputLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btnPivotLongToWide, btnPivotWideToLong, btnTranspose});

      pnlInputLayout.setVerticalGroup(
         pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(pnlInputLayout.createSequentialGroup()
            .addGroup(pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
               .addComponent(btnLoad, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
               .addComponent(btnClear, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
               .addComponent(cbReplace)
               .addComponent(btnSaveExpr)
               .addComponent(btnLoadExpr))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(scrollInput, javax.swing.GroupLayout.DEFAULT_SIZE, 20, Short.MAX_VALUE)
            .addGap(11, 11, 11)
            .addGroup(pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
               .addComponent(lblColumns)
               .addComponent(spnColumns, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(cbQuotedIn)
               .addComponent(btnCsvInput)
               .addComponent(btnTsvInput)
               .addComponent(rbDoubleIn)
               .addComponent(rbSingleIn)
               .addComponent(cbGuess)
               .addComponent(cbHasHeadings))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
               .addComponent(lblExpression)
               .addComponent(txtRegex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(btnMatch))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(scrollTable, javax.swing.GroupLayout.DEFAULT_SIZE, 24, Short.MAX_VALUE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(pnlInputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
               .addComponent(btnPivotWideToLong)
               .addComponent(btnPivotLongToWide)
               .addComponent(btnTranspose)))
      );

      splitPane.setTopComponent(pnlInput);

      pnlOutput.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Output", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 0, 12))); // NOI18N
      pnlOutput.setName("pnlOutput"); // NOI18N

      lblFormat.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
      lblFormat.setText("Format Expression: ");
      lblFormat.setName("lblFormat"); // NOI18N

      txtFormat.setName("txtFormat"); // NOI18N

      btnFormat.setMnemonic('f');
      btnFormat.setText("Format");
      btnFormat.setName("btnFormat"); // NOI18N
      btnFormat.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnFormatActionPerformed(evt);
         }
      });

      scrollOutput.setName("scrollOutput"); // NOI18N

      txtOutput.setColumns(20);
      txtOutput.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
      txtOutput.setRows(5);
      txtOutput.setName("txtOutput"); // NOI18N
      scrollOutput.setViewportView(txtOutput);

      lblArgumentMarker.setText("Column Prefix: ");
      lblArgumentMarker.setName("lblArgumentMarker"); // NOI18N

      cbBlanks.setText("Replace blanks? ");
      cbBlanks.setName("cbBlanks"); // NOI18N

      txtBlanks.setText("null");
      txtBlanks.setName("txtBlanks"); // NOI18N

      btnSwap.setMnemonic('o');
      btnSwap.setText("Output to Input");
      btnSwap.setName("btnSwap"); // NOI18N
      btnSwap.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnSwapActionPerformed(evt);
         }
      });

      btnCsvOutput.setText("CSV");
      btnCsvOutput.setName("btnCsvOutput"); // NOI18N
      btnCsvOutput.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnCsvOutputActionPerformed(evt);
         }
      });

      btnTsvOutput.setText("TSV");
      btnTsvOutput.setName("btnTsvOutput"); // NOI18N
      btnTsvOutput.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnTsvOutputActionPerformed(evt);
         }
      });

      txtMarker.setText("?");
      txtMarker.setName("txtMarker"); // NOI18N

      btnSave.setText("Save");
      btnSave.setName("btnSave"); // NOI18N
      btnSave.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnSaveActionPerformed(evt);
         }
      });

      cbTrim.setText("Trim?");
      cbTrim.setName("cbTrim"); // NOI18N

      cbQuotedOut.setText("Add Quotes?");
      cbQuotedOut.setName("cbQuotedOut"); // NOI18N

      bgQuotedOut.add(rbDoubleOut);
      rbDoubleOut.setSelected(true);
      rbDoubleOut.setText("\"");
      rbDoubleOut.setName("rbDoubleOut"); // NOI18N

      bgQuotedOut.add(rbSingleOut);
      rbSingleOut.setText("'");
      rbSingleOut.setName("rbSingleOut"); // NOI18N

      cbCase.setText("Transform Case?");
      cbCase.setName("cbCase"); // NOI18N

      bgCase.add(rbUpper);
      rbUpper.setSelected(true);
      rbUpper.setText("Upper");
      rbUpper.setName("rbUpper"); // NOI18N

      bgCase.add(rbLower);
      rbLower.setText("Lower");
      rbLower.setName("rbLower"); // NOI18N

      rbUnique.setText("Make Unique?");
      rbUnique.setName("rbUnique"); // NOI18N

      javax.swing.GroupLayout pnlOutputLayout = new javax.swing.GroupLayout(pnlOutput);
      pnlOutput.setLayout(pnlOutputLayout);
      pnlOutputLayout.setHorizontalGroup(
         pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addComponent(scrollOutput)
         .addGroup(pnlOutputLayout.createSequentialGroup()
            .addGroup(pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addGroup(pnlOutputLayout.createSequentialGroup()
                  .addComponent(lblArgumentMarker)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(txtMarker, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addGap(18, 18, 18)
                  .addComponent(cbBlanks)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(txtBlanks, javax.swing.GroupLayout.PREFERRED_SIZE, 62, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                  .addComponent(btnCsvOutput)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(btnTsvOutput, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                  .addComponent(btnSwap)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                  .addComponent(btnSave))
               .addGroup(pnlOutputLayout.createSequentialGroup()
                  .addComponent(lblFormat)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(txtFormat)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(btnFormat))
               .addGroup(pnlOutputLayout.createSequentialGroup()
                  .addComponent(cbTrim)
                  .addGap(18, 18, 18)
                  .addComponent(cbQuotedOut)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(rbDoubleOut)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(rbSingleOut)
                  .addGap(18, 18, 18)
                  .addComponent(cbCase)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(rbUpper)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(rbLower)
                  .addGap(18, 18, 18)
                  .addComponent(rbUnique)
                  .addGap(0, 0, Short.MAX_VALUE)))
            .addContainerGap())
      );

      pnlOutputLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btnCsvOutput, btnTsvOutput});

      pnlOutputLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {txtBlanks, txtMarker});

      pnlOutputLayout.setVerticalGroup(
         pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(pnlOutputLayout.createSequentialGroup()
            .addGroup(pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
               .addComponent(lblArgumentMarker)
               .addComponent(txtMarker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(cbBlanks)
               .addComponent(txtBlanks, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(btnCsvOutput)
               .addComponent(btnTsvOutput)
               .addComponent(btnSave)
               .addComponent(btnSwap))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
               .addComponent(cbTrim)
               .addComponent(cbQuotedOut)
               .addComponent(rbDoubleOut)
               .addComponent(rbSingleOut)
               .addComponent(cbCase)
               .addComponent(rbUpper)
               .addComponent(rbLower)
               .addComponent(rbUnique))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addGroup(pnlOutputLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
               .addComponent(lblFormat)
               .addComponent(txtFormat, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(btnFormat))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(scrollOutput, javax.swing.GroupLayout.DEFAULT_SIZE, 165, Short.MAX_VALUE)
            .addContainerGap())
      );

      splitPane.setRightComponent(pnlOutput);

      pnlReplace.setName("pnlReplace"); // NOI18N

      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()
            .addComponent(splitPane)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(pnlReplace, javax.swing.GroupLayout.DEFAULT_SIZE, 250, Short.MAX_VALUE))
      );
      layout.setVerticalGroup(
         layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addComponent(pnlReplace, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
               .addComponent(splitPane))
            .addContainerGap())
      );

      pack();
   }// </editor-fold>//GEN-END:initComponents

   private void match() {
      jTable.setRowSorter(null);
      
      txtOutput.setText("");
      String regex = txtRegex.getText();
      String text = txtInput.getText();

      String[] input = null;

      try {

         Pattern pattern = Pattern.compile(regex);

         int flagsMask = pattern.flags();

         StringBuilder flags = new StringBuilder();

         boolean dotall = false;

         // work out which flags are being used
         if ((flagsMask & Pattern.DOTALL) == Pattern.DOTALL) {
            flags.append("DOTALL ");
            dotall = true;
         }

         if ((flagsMask & Pattern.MULTILINE) == Pattern.MULTILINE) {
            flags.append("MULTILINE ");
         }

         if ((flagsMask & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE) {
            flags.append("CASE_INSENSITIVE ");
         }

         PivotTableModel model = new PivotTableModel();
         jTable.setModel(model);

         int numMatches = 0;

         // it DOTALL mode is on then grab the entire text rather then seperating by line
         if (dotall) {
            input = new String[1];
            input[0] = text;
         } else {
            input = text.split("\n");
         }

         for (String line : input) {
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()) {

               // group() matches the entire line if there was a match.
               // This if-statment is to suppress extra empty groups from being
               // produced if the regex matches the entire line
               if (matcher.group().length() != line.length()) {
                  continue;
               }

               numMatches++;

               txtOutput.append(matcher.group(0) + "\n");

               String[] groups = new String[matcher.groupCount()];
               for (int j = 1; j <= matcher.groupCount(); j++) {
                  String s = matcher.group(j);
                  groups[j - 1] = s;
               }

               model.addRow(groups);
            }
         }

         model.fireTableStructureChanged();
         TableRowSorter<PivotTableModel> sorter = new TableRowSorter<>();
         jTable.setRowSorter(sorter);
         sorter.setModel(model);

         txtOutput.append("\nMatched " + numMatches + " line(s) out of "
                 + input.length + " line(s) of input.");

         if (!flags.toString().isEmpty()) {
            txtOutput.append("\n\nFlags: " + flags.toString().trim());
         }

      } catch (PatternSyntaxException ex) {
         txtOutput.setText("");
         txtOutput.append(ex.getMessage());
         txtRegex.setBackground(new Color(1f, 0.3f, 0.3f));
      }

   }

   private void format() {
      String marker = txtMarker.getText();

      txtOutput.setText("");

      String formatter = this.txtFormat.getText().replaceAll("\\" + marker + "(\\d*)", "%$1\\$s");

      for (int row = 0; row < jTable.getRowCount(); row++) {

         Object[] values = new Object[jTable.getColumnCount()];

         for (int col = 0; col < jTable.getColumnCount(); col++) {
            String val = (String) jTable.getValueAt(row, col);

            if (cbBlanks.isSelected() && val.isEmpty()) {
               val = txtBlanks.getText();
            }

            if (cbTrim.isSelected()) {
               val = val.trim();
            }

            if (cbCase.isSelected()) {
               if (rbUpper.isSelected()) {
                  val = val.toUpperCase();
               } else {
                  val = val.toLowerCase();
               }
            }

            values[col] = val;
         }

         String formatted = String.format(formatter, values);
         txtOutput.append(formatted + "\n");
      }

      if (rbUnique.isSelected()) {

         String output = txtOutput.getText();
         txtOutput.setText("");

         Stream.of(output.split("\n"))
                 .distinct()
                 .forEach(line -> txtOutput.append(line + "\n"));
      }
   }

   private void transpose() {
      PivotTableModel model = (PivotTableModel) jTable.getModel();
      model.transpose();
   }

   private void pivotWideToLong() {
      PivotTableModel model = (PivotTableModel) jTable.getModel();
      ListMultimap<String, String> columns = new WideToLongPivotDialog(this).mapColumns(model);
      if (columns != null) {
         model.pivotWideToLong(columns.get("preserved"), columns.get("values"));
      }
   }

   private void pivotLongToWide() {
      PivotTableModel model = (PivotTableModel) jTable.getModel();
      ListMultimap<String, String> columns = new LongToWidePivotDialog(this).mapColumns(model);
      if (columns != null) {
         model.pivotLongToWide(columns.get("repeating"), columns.get("header").get(0), columns.get("value").get(0));
      }
   }

   private void csv() {
      Integer cols = (Integer) spnColumns.getValue();
      Boolean quoted = cbQuotedIn.isSelected();
      String quote = rbDoubleIn.isSelected() ? "\"" : "'";

      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < cols - 1; i++) {
         if (quoted) {
            sb.append(quote).append("(.*?)").append(quote).append(",");
         } else {
            sb.append("(.*?),");
         }
      }
      if (quoted) {

         sb.append(quote).append("(.*?)").append(quote);
      } else {
         sb.append("(.*)");
      }

      txtRegex.setText(sb.toString());
   }

   private void tsv() {
      Integer cols = (Integer) spnColumns.getValue();
      Boolean quoted = cbQuotedIn.isSelected();
      String quote = rbDoubleIn.isSelected() ? "\"" : "'";

      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < cols - 1; i++) {
         if (quoted) {
            sb.append(quote).append("(.*?)").append(quote).append("\\t");
         } else {
            sb.append("(.*?)\\t");
         }
      }
      if (quoted) {
         sb.append(quote).append("(.*?)").append(quote);
      } else {
         sb.append("(.*)");
      }

      txtRegex.setText(sb.toString());
   }

   private void load() {
      JFileChooser jfc = new JFileChooser();
      if (jfc.showOpenDialog(MainFrame.this) == JFileChooser.APPROVE_OPTION) {
         try {
            String data = readFile(jfc.getSelectedFile());

            txtInput.setText(data);

         } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Error", ex.getMessage(), JOptionPane.ERROR_MESSAGE);
         }
      }
   }

   private void save() {
      JFileChooser jfc = new JFileChooser();
      int result = jfc.showSaveDialog(this);
      if (result == JFileChooser.APPROVE_OPTION) {
         File f = jfc.getSelectedFile();
         if (f.exists()) {
            int confirm = JOptionPane.showConfirmDialog(this, "Overwrite existing file?");
            if (confirm == JOptionPane.YES_OPTION) {
               try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
                  writer.write(txtOutput.getText());
               } catch (Exception e) {
                  JOptionPane.showMessageDialog(this, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
               }
            }
         } else {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
               writer.write(txtOutput.getText());
            } catch (Exception e) {
               JOptionPane.showMessageDialog(this, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
            }
         }
      }
   }

   public String readFile(File file) throws Exception {
      StringBuilder fileContents;
      try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
         @SuppressWarnings("UnusedAssignment")
         String line = null;
         fileContents = new StringBuilder();
         while ((line = reader.readLine()) != null) {
            fileContents.append(line);
            fileContents.append(System.getProperty("line.separator"));
         }
      }
      return fileContents.toString();
   }

   private void btnFormatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFormatActionPerformed
      format();
   }//GEN-LAST:event_btnFormatActionPerformed

   private void btnTransposeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTransposeActionPerformed
      transpose();
   }//GEN-LAST:event_btnTransposeActionPerformed

   private void btnMatchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMatchActionPerformed
      match();
   }//GEN-LAST:event_btnMatchActionPerformed

   private void btnCsvInputActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCsvInputActionPerformed
      if (cbGuess.isSelected()) {
         String line = txtInput.getText().split("\n")[0];
         String commas = line.replaceAll("[^,]", "");
         spnColumns.setValue(commas.length() + 1);
      }
      csv();
      match();
   }//GEN-LAST:event_btnCsvInputActionPerformed

   private void btnTsvInputActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTsvInputActionPerformed
      if (cbGuess.isSelected()) {
         String line = txtInput.getText().split("\n")[0];
         String commas = line.replaceAll("[^\t]", "");
         spnColumns.setValue(commas.length() + 1);
      }
      tsv();
      match();
   }//GEN-LAST:event_btnTsvInputActionPerformed

   private void btnLoadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLoadActionPerformed
      load();
   }//GEN-LAST:event_btnLoadActionPerformed

   private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearActionPerformed
      txtInput.setText("");
   }//GEN-LAST:event_btnClearActionPerformed

   private void spnColumnsMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_spnColumnsMouseWheelMoved
      Integer value = (Integer) spnColumns.getValue();
      value -= evt.getUnitsToScroll();
      spnColumns.setValue(value > 0 ? value : 0);
   }//GEN-LAST:event_spnColumnsMouseWheelMoved

   private void btnPivotWideToLongActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPivotWideToLongActionPerformed
      pivotWideToLong();
   }//GEN-LAST:event_btnPivotWideToLongActionPerformed

   private void txtRegexKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtRegexKeyPressed
      // restore default color
      txtRegex.setBackground(defaultTextColor);
   }//GEN-LAST:event_txtRegexKeyPressed

   private void cbReplaceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbReplaceActionPerformed
      pnlReplace.setVisible(cbReplace.isSelected());
   }//GEN-LAST:event_cbReplaceActionPerformed

   private void btnSwapActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSwapActionPerformed
      txtInput.setText(txtOutput.getText());
   }//GEN-LAST:event_btnSwapActionPerformed

   private void btnCsvOutputActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCsvOutputActionPerformed
      String marker = txtMarker.getText();

      Integer cols = (Integer) jTable.getColumnCount();

      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < cols; i++) {
         if (cbQuotedOut.isSelected()) {
            String quote = rbDoubleOut.isSelected() ? "\"" : "'";
            sb.append(quote).append(marker).append(i + 1).append(quote).append(",");
         } else {
            sb.append(marker).append(i + 1).append(",");
         }
      }

      String format = sb.toString();

      txtFormat.setText(format.substring(0, format.length() - 1));

      format();
   }//GEN-LAST:event_btnCsvOutputActionPerformed

   private void btnTsvOutputActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTsvOutputActionPerformed
      String marker = txtMarker.getText();

      Integer cols = (Integer) jTable.getColumnCount();

      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < cols; i++) {
         if (cbQuotedOut.isSelected()) {
            String quote = rbDoubleOut.isSelected() ? "\"" : "'";
            sb.append(quote).append(marker).append(i + 1).append(quote).append("\t");
         } else {
            sb.append(marker).append(i + 1).append("\t");
         }
      }

      String format = sb.toString();

      txtFormat.setText(format.substring(0, format.length() - 1));

      format();
   }//GEN-LAST:event_btnTsvOutputActionPerformed

   private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveActionPerformed
      save();
   }//GEN-LAST:event_btnSaveActionPerformed

   private void btnPivotLongToWideActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPivotLongToWideActionPerformed
      pivotLongToWide();
   }//GEN-LAST:event_btnPivotLongToWideActionPerformed

   private void btnSaveExprActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveExprActionPerformed

      Expressions exp = new Expressions();
      exp.setBlanks(txtBlanks.getText());
      exp.setBlanksEnabled(cbBlanks.isSelected());
      exp.setCapture(txtRegex.getText());
      exp.setFormat(txtFormat.getText());
      exp.setPrefix(txtMarker.getText());
      exp.setReplacements(pnlReplace.getModel().getRows());
      String json = new Gson().toJson(exp);

      JFileChooser jfc = new JFileChooser();
      int result = jfc.showSaveDialog(this);
      if (result == JFileChooser.APPROVE_OPTION) {
         File f = jfc.getSelectedFile();
         if (f.exists()) {
            int confirm = JOptionPane.showConfirmDialog(this, "Overwrite existing file?");
            if (confirm == JOptionPane.YES_OPTION) {
               try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
                  writer.write(json);
               } catch (Exception e) {
                  JOptionPane.showMessageDialog(this, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
               }
            }
         } else {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
               writer.write(json);
            } catch (Exception e) {
               JOptionPane.showMessageDialog(this, "Error", e.getMessage(), JOptionPane.ERROR_MESSAGE);
            }
         }
      }

   }//GEN-LAST:event_btnSaveExprActionPerformed

   private void btnLoadExprActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLoadExprActionPerformed
      String json = null;

      JFileChooser jfc = new JFileChooser();
      if (jfc.showOpenDialog(MainFrame.this) == JFileChooser.APPROVE_OPTION) {
         try {
            json = readFile(jfc.getSelectedFile());
         } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Error", ex.getMessage(), JOptionPane.ERROR_MESSAGE);
         }
      }

      Expressions exp = new Gson().fromJson(json, Expressions.class);
      txtBlanks.setText(exp.getBlanks());
      cbBlanks.setSelected(exp.getBlanksEnabled());
      txtRegex.setText(exp.getCapture());
      txtFormat.setText(exp.getFormat());
      txtMarker.setText(exp.getPrefix());

      List<List<String>> replacements = exp.getReplacements();
      MutableTableModel model = pnlReplace.getModel();
      model.getTable().clear();
      for (List<String> replacement : replacements) {
         model.addRow(replacement);
      }

   }//GEN-LAST:event_btnLoadExprActionPerformed

   private void cbHasHeadingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbHasHeadingsActionPerformed
      // TODO add your handling code here:
   }//GEN-LAST:event_cbHasHeadingsActionPerformed

   public String getPrefix() {
      return txtMarker.getText();
   }


   // Variables declaration - do not modify//GEN-BEGIN:variables
   private javax.swing.ButtonGroup bgCase;
   private javax.swing.ButtonGroup bgQuotedIn;
   private javax.swing.ButtonGroup bgQuotedOut;
   private javax.swing.JButton btnClear;
   private javax.swing.JButton btnCsvInput;
   private javax.swing.JButton btnCsvOutput;
   private javax.swing.JButton btnFormat;
   private javax.swing.JButton btnLoad;
   private javax.swing.JButton btnLoadExpr;
   private javax.swing.JButton btnMatch;
   private javax.swing.JButton btnPivotLongToWide;
   private javax.swing.JButton btnPivotWideToLong;
   private javax.swing.JButton btnSave;
   private javax.swing.JButton btnSaveExpr;
   private javax.swing.JButton btnSwap;
   private javax.swing.JButton btnTranspose;
   private javax.swing.JButton btnTsvInput;
   private javax.swing.JButton btnTsvOutput;
   private javax.swing.JCheckBox cbBlanks;
   private javax.swing.JCheckBox cbCase;
   private javax.swing.JCheckBox cbGuess;
   private javax.swing.JCheckBox cbHasHeadings;
   private javax.swing.JCheckBox cbQuotedIn;
   private javax.swing.JCheckBox cbQuotedOut;
   private javax.swing.JCheckBox cbReplace;
   private javax.swing.JCheckBox cbTrim;
   private javax.swing.JTable jTable;
   private javax.swing.JLabel lblArgumentMarker;
   private javax.swing.JLabel lblColumns;
   private javax.swing.JLabel lblExpression;
   private javax.swing.JLabel lblFormat;
   private javax.swing.JPanel pnlInput;
   private javax.swing.JPanel pnlOutput;
   private jrex.ui.ReplacePanel pnlReplace;
   private javax.swing.JRadioButton rbDoubleIn;
   private javax.swing.JRadioButton rbDoubleOut;
   private javax.swing.JRadioButton rbLower;
   private javax.swing.JRadioButton rbSingleIn;
   private javax.swing.JRadioButton rbSingleOut;
   private javax.swing.JCheckBox rbUnique;
   private javax.swing.JRadioButton rbUpper;
   private javax.swing.JScrollPane scrollInput;
   private javax.swing.JScrollPane scrollOutput;
   private javax.swing.JScrollPane scrollTable;
   private javax.swing.JSplitPane splitPane;
   private javax.swing.JSpinner spnColumns;
   private javax.swing.JTextField txtBlanks;
   private javax.swing.JTextField txtFormat;
   private javax.swing.JTextArea txtInput;
   private javax.swing.JTextField txtMarker;
   private javax.swing.JTextArea txtOutput;
   private javax.swing.JTextField txtRegex;
   // End of variables declaration//GEN-END:variables

}