public PrintfEditor(Frame frame, String title) {
    super(frame, title, false);

    originalTemplate = new PrintfTemplate();
    editableTemplate = new PrintfTemplate();
    try {
      jbInit();
      pack();
      initMatches();
    } catch (Exception exc) {
      String logMessage = "Could not set up the printf editor";
      logger.critical(logMessage, exc);
      JOptionPane.showMessageDialog(frame, logMessage, "Printf Editor", JOptionPane.ERROR_MESSAGE);
    }
  }
public class PrintfEditor extends JDialog implements EDPEditor, ConfigParamListener {
  protected PrintfTemplate originalTemplate;
  protected PrintfTemplate editableTemplate;
  private EDPCellData m_data;
  private HashMap paramKeys;
  private HashMap matchesKeys = new HashMap();
  static char[] RESERVED_CHARS = {'[', '\\', '^', '$', '.', '|', '?', '*', '+', '(', ')'};
  static String RESERVED_STRING = new String(RESERVED_CHARS);
  static SimpleAttributeSet PLAIN_ATTR = new SimpleAttributeSet();

  static {
    StyleConstants.setForeground(PLAIN_ATTR, Color.black);
    StyleConstants.setBold(PLAIN_ATTR, false);
    StyleConstants.setFontFamily(PLAIN_ATTR, "Helvetica");
    StyleConstants.setFontSize(PLAIN_ATTR, 14);
  }

  int numParameters = 0;

  JPanel printfPanel = new JPanel();
  ButtonGroup buttonGroup = new ButtonGroup();
  JPanel buttonPanel = new JPanel();
  JButton cancelButton = new JButton();
  JButton saveButton = new JButton();
  JLabel formatLabel = new JLabel();
  FlowLayout flowLayout1 = new FlowLayout();
  JTextArea formatTextArea = new JTextArea();
  JPanel parameterPanel = new JPanel();
  JLabel parameterLabel = new JLabel();
  JTextArea parameterTextArea = new JTextArea();
  JButton insertButton = new JButton();
  JComboBox paramComboBox = new JComboBox();
  GridBagLayout gridBagLayout1 = new GridBagLayout();
  ButtonGroup buttonGroup1 = new ButtonGroup();

  TitledBorder titledBorder2;
  JComboBox matchComboBox = new JComboBox();
  JButton insertMatchButton = new JButton();
  JPanel matchPanel = new JPanel();
  JPanel InsertPanel = new JPanel();
  GridLayout gridLayout1 = new GridLayout();
  GridBagLayout gridBagLayout2 = new GridBagLayout();
  GridBagLayout gridBagLayout3 = new GridBagLayout();
  JTabbedPane printfTabPane = new JTabbedPane();
  JTextPane editorPane = new JTextPane();
  JScrollPane editorPanel = new JScrollPane();
  int selectedPane = 0;
  private boolean m_isCrawlRuleEditor = false;
  private static final String STRING_LITERAL = "String Literal";

  protected static Logger logger = Logger.getLogger("PrintfEditor");

  public PrintfEditor(Frame frame, String title) {
    super(frame, title, false);

    originalTemplate = new PrintfTemplate();
    editableTemplate = new PrintfTemplate();
    try {
      jbInit();
      pack();
      initMatches();
    } catch (Exception exc) {
      String logMessage = "Could not set up the printf editor";
      logger.critical(logMessage, exc);
      JOptionPane.showMessageDialog(frame, logMessage, "Printf Editor", JOptionPane.ERROR_MESSAGE);
    }
  }

  private void initMatches() {
    matchesKeys.put(STRING_LITERAL, "");
    matchesKeys.put("Any number", "[0-9]+");
    matchesKeys.put("Anything", ".*");
    matchesKeys.put("Start", "^");
    matchesKeys.put("End", "$");
    matchesKeys.put("Single path component", "[^/]+");

    for (Iterator it = matchesKeys.keySet().iterator(); it.hasNext(); ) {
      matchComboBox.addItem(it.next());
    }
  }

  private void jbInit() throws Exception {

    saveButton.setText("Save");
    saveButton.addActionListener(new PrintfTemplateEditor_saveButton_actionAdapter(this));
    cancelButton.setText("Cancel");
    cancelButton.addActionListener(new PrintfTemplateEditor_cancelButton_actionAdapter(this));
    this.setTitle(this.getTitle() + " Template Editor");
    printfPanel.setLayout(gridBagLayout1);
    formatLabel.setFont(new java.awt.Font("DialogInput", 0, 12));
    formatLabel.setText("Format String:");
    buttonPanel.setLayout(flowLayout1);
    printfPanel.setBorder(BorderFactory.createEtchedBorder());
    printfPanel.setMinimumSize(new Dimension(100, 160));
    printfPanel.setPreferredSize(new Dimension(380, 160));
    parameterPanel.setLayout(gridBagLayout2);
    parameterLabel.setText("Parameters:");
    parameterLabel.setFont(new java.awt.Font("DialogInput", 0, 12));
    parameterTextArea.setMinimumSize(new Dimension(100, 25));
    parameterTextArea.setPreferredSize(new Dimension(200, 25));
    parameterTextArea.setEditable(true);
    parameterTextArea.setText("");
    insertButton.setMaximumSize(new Dimension(136, 20));
    insertButton.setMinimumSize(new Dimension(136, 20));
    insertButton.setPreferredSize(new Dimension(136, 20));
    insertButton.setToolTipText(
        "insert the format in the format string and add parameter to list.");
    insertButton.setText("Insert Parameter");
    insertButton.addActionListener(new PrintfTemplateEditor_insertButton_actionAdapter(this));
    formatTextArea.setMinimumSize(new Dimension(100, 25));
    formatTextArea.setPreferredSize(new Dimension(200, 15));
    formatTextArea.setText("");
    parameterPanel.setBorder(null);
    parameterPanel.setMinimumSize(new Dimension(60, 40));
    parameterPanel.setPreferredSize(new Dimension(300, 40));
    insertMatchButton.addActionListener(
        new PrintfTemplateEditor_insertMatchButton_actionAdapter(this));
    insertMatchButton.setText("Insert Match");
    insertMatchButton.setToolTipText(
        "insert the match in the format string and add parameter to list.");
    insertMatchButton.setPreferredSize(new Dimension(136, 20));
    insertMatchButton.setMinimumSize(new Dimension(136, 20));
    insertMatchButton.setMaximumSize(new Dimension(136, 20));
    matchPanel.setPreferredSize(new Dimension(300, 40));
    matchPanel.setBorder(null);
    matchPanel.setMinimumSize(new Dimension(60, 60));
    matchPanel.setLayout(gridBagLayout3);
    InsertPanel.setLayout(gridLayout1);
    gridLayout1.setColumns(1);
    gridLayout1.setRows(2);
    gridLayout1.setVgap(0);
    InsertPanel.setBorder(BorderFactory.createEtchedBorder());
    InsertPanel.setMinimumSize(new Dimension(100, 100));
    InsertPanel.setPreferredSize(new Dimension(380, 120));
    editorPane.setText("");
    editorPane.addKeyListener(new PrintfEditor_editorPane_keyAdapter(this));
    printfTabPane.addChangeListener(new PrintfEditor_printfTabPane_changeAdapter(this));
    parameterPanel.add(
        insertButton,
        new GridBagConstraints(
            1,
            0,
            1,
            1,
            0.0,
            0.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.NONE,
            new Insets(8, 6, 13, 8),
            0,
            10));
    parameterPanel.add(
        paramComboBox,
        new GridBagConstraints(
            0,
            0,
            1,
            1,
            1.0,
            0.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL,
            new Insets(8, 8, 13, 0),
            258,
            11));
    paramComboBox.setRenderer(new MyCellRenderer());
    InsertPanel.add(matchPanel, null);
    InsertPanel.add(parameterPanel, null);
    buttonPanel.add(cancelButton, null);
    buttonPanel.add(saveButton, null);
    this.getContentPane().add(printfTabPane, BorderLayout.NORTH);
    this.getContentPane().add(InsertPanel, BorderLayout.CENTER);
    matchPanel.add(
        insertMatchButton,
        new GridBagConstraints(
            1,
            0,
            1,
            1,
            0.0,
            0.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.NONE,
            new Insets(8, 6, 13, 8),
            0,
            10));
    matchPanel.add(
        matchComboBox,
        new GridBagConstraints(
            0,
            0,
            1,
            1,
            1.0,
            0.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL,
            new Insets(8, 8, 13, 0),
            258,
            11));
    printfPanel.add(
        parameterLabel,
        new GridBagConstraints(
            0,
            2,
            1,
            1,
            0.0,
            0.0,
            GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(7, 5, 0, 5),
            309,
            0));
    printfPanel.add(
        formatLabel,
        new GridBagConstraints(
            0,
            0,
            1,
            1,
            0.0,
            0.0,
            GridBagConstraints.WEST,
            GridBagConstraints.NONE,
            new Insets(4, 5, 0, 5),
            288,
            0));
    printfPanel.add(
        formatTextArea,
        new GridBagConstraints(
            0,
            1,
            1,
            1,
            1.0,
            1.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.BOTH,
            new Insets(6, 5, 0, 5),
            300,
            34));
    printfPanel.add(
        parameterTextArea,
        new GridBagConstraints(
            0,
            3,
            1,
            1,
            1.0,
            1.0,
            GridBagConstraints.CENTER,
            GridBagConstraints.BOTH,
            new Insets(6, 5, 6, 5),
            300,
            34));
    printfTabPane.addTab("Editor View", null, editorPanel, "View in Editor");
    printfTabPane.addTab("Printf View", null, printfPanel, "Vies as Printf");
    editorPane.setCharacterAttributes(PLAIN_ATTR, true);
    editorPane.addStyle("PLAIN", editorPane.getLogicalStyle());
    editorPanel.getViewport().add(editorPane, null);
    this.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    buttonGroup.add(cancelButton);
  }
  /** notifiyParamsChanged */
  public void notifiyParamsChanged() {
    updateParams(m_data);
  }

  /**
   * setEDPData
   *
   * @param data EDPCellData
   */
  public void setCellData(EDPCellData data) {
    m_data = data;
    paramKeys = data.getPlugin().getPrintfDescrs(!m_isCrawlRuleEditor);
    data.getPlugin().addParamListener(this);
    setTemplate((PrintfTemplate) data.getData());
    m_isCrawlRuleEditor = data.getKey().equals(DefinableArchivalUnit.KEY_AU_CRAWL_RULES);
    // initialize the combobox
    updateParams(data);
    if (m_isCrawlRuleEditor) {
      matchPanel.setVisible(true);
    } else {
      matchPanel.setVisible(false);
    }
  }

  void saveButton_actionPerformed(ActionEvent e) {
    updateEditableTemplate(selectedPane);
    originalTemplate.setFormat(editableTemplate.m_format);
    originalTemplate.setTokens(editableTemplate.m_tokens);
    m_data.updateTemplateData(originalTemplate);
    setVisible(false);
  }

  void cancelButton_actionPerformed(ActionEvent e) {
    setVisible(false);
  }

  void printfTabPane_stateChanged(ChangeEvent e) {
    updateEditableTemplate(selectedPane);
    selectedPane = printfTabPane.getSelectedIndex();
    updatePane(selectedPane);
  }

  void updateEditableTemplate(int pane) {
    switch (pane) {
      case 0: // use the editor to update the template
        updateTemplateFromEditor(editableTemplate);
        break;
      case 1: // use the printf text areas to update the template.
        updateTemplateFromPrintf();
        break;
    }
  }

  void insertButton_actionPerformed(ActionEvent e) {
    Object selected = paramComboBox.getSelectedItem();
    ConfigParamDescr descr;
    String key;
    int type = 0;
    String format = "";
    if (selected instanceof ConfigParamDescr) {
      descr = (ConfigParamDescr) selected;
      key = descr.getKey();
      type = descr.getType();
      switch (type) {
        case ConfigParamDescr.TYPE_STRING:
        case ConfigParamDescr.TYPE_URL:
        case ConfigParamDescr.TYPE_BOOLEAN:
          format = "%s";
          break;
        case ConfigParamDescr.TYPE_INT:
        case ConfigParamDescr.TYPE_LONG:
        case ConfigParamDescr.TYPE_POS_INT:
          NumericPaddingDialog dialog = new NumericPaddingDialog();
          Point pos = this.getLocationOnScreen();
          dialog.setLocation(pos.x, pos.y);
          dialog.pack();
          dialog.setVisible(true);
          StringBuilder fbuf = new StringBuilder("%");
          int width = dialog.getPaddingSize();
          boolean is_zero = dialog.useZero();
          if (width > 0) {
            fbuf.append(".");
            if (is_zero) {
              fbuf.append(0);
            }
            fbuf.append(width);
          }
          if (type == ConfigParamDescr.TYPE_LONG) {
            fbuf.append("ld");
          } else {
            fbuf.append("d");
          }
          format = fbuf.toString();
          break;
        case ConfigParamDescr.TYPE_YEAR:
          if (key.startsWith(DefinableArchivalUnit.PREFIX_AU_SHORT_YEAR)) {
            format = "%02d";
          } else {
            format = "%d";
          }
          break;
        case ConfigParamDescr.TYPE_RANGE:
        case ConfigParamDescr.TYPE_NUM_RANGE:
        case ConfigParamDescr.TYPE_SET:
          format = "%s";
          break;
      }
      if (selectedPane == 0) {
        insertParameter(descr, format, editorPane.getSelectionStart());
      } else if (selectedPane == 1) {
        // add the combobox data value to the edit box
        int pos = formatTextArea.getCaretPosition();
        formatTextArea.insert(format, pos);

        pos = parameterTextArea.getCaretPosition();
        parameterTextArea.insert(", " + key, pos);
      }
    } else {
      key = selected.toString();
      format =
          escapePrintfChars(
              (String)
                  JOptionPane.showInputDialog(
                      this,
                      "Enter the string you wish to input",
                      "String Literal Input",
                      JOptionPane.OK_CANCEL_OPTION));
      if (StringUtil.isNullString(format)) {
        return;
      }
      if (selectedPane == 0) {
        insertText(format, PLAIN_ATTR, editorPane.getSelectionStart());
      } else if (selectedPane == 1) {
        // add the combobox data value to the edit box
        formatTextArea.insert(format, formatTextArea.getCaretPosition());
      }
    }
  }

  void insertMatchButton_actionPerformed(ActionEvent e) {
    String key = (String) matchComboBox.getSelectedItem();
    String format = (String) matchesKeys.get(key);
    if (key.equals(STRING_LITERAL)) {
      format =
          escapeReservedChars(
              (String)
                  JOptionPane.showInputDialog(
                      this,
                      "Enter the string you wish to match",
                      "String Literal Input",
                      JOptionPane.OK_CANCEL_OPTION));
      if (StringUtil.isNullString(format)) {
        return;
      }
    }
    if (selectedPane == 0) {
      insertText(format, PLAIN_ATTR, editorPane.getSelectionStart());
    } else {
      // add the combobox data value to the edit box
      int pos = formatTextArea.getCaretPosition();
      formatTextArea.insert(format, pos);
    }
  }

  void editorPane_keyPressed(KeyEvent e) {
    StyledDocument doc = editorPane.getStyledDocument();
    int pos = editorPane.getCaretPosition();
    int code = e.getKeyCode();
    Element el;
    switch (code) {
      case KeyEvent.VK_BACK_SPACE:
      case KeyEvent.VK_DELETE:
      case KeyEvent.VK_LEFT:
      case KeyEvent.VK_KP_LEFT:
        if (pos == 0) return;
        // we want to get the element to the left of position.
        el = doc.getCharacterElement(pos - 1);
        break;
      case KeyEvent.VK_RIGHT:
      case KeyEvent.VK_KP_RIGHT:
        // we want to get the element to the right of position.
        el = doc.getCharacterElement(pos + 1);
        break;
      default:
        return; // bail we don't handle it.
    }
    AttributeSet attr = el.getAttributes();
    String el_name = (String) attr.getAttribute(StyleConstants.NameAttribute);
    int el_range = el.getEndOffset() - el.getStartOffset() - 1;
    if (el_name.startsWith("Parameter") && StyleConstants.getComponent(attr) != null) {
      try {
        switch (code) {
          case KeyEvent.VK_BACK_SPACE:
          case KeyEvent.VK_DELETE:
            doc.remove(el.getStartOffset(), el_range);
            break;
          case KeyEvent.VK_LEFT:
          case KeyEvent.VK_KP_LEFT:
            editorPane.setCaretPosition(pos - el_range);
            break;
          case KeyEvent.VK_RIGHT:
          case KeyEvent.VK_KP_RIGHT:
            editorPane.setCaretPosition(pos + (el_range));
            break;
        }
      } catch (BadLocationException ex) {
      }
    }
  }

  private void insertParameter(ConfigParamDescr descr, String format, int pos) {
    try {
      StyledDocument doc = (StyledDocument) editorPane.getDocument();

      // The component must first be wrapped in a style
      Style style = doc.addStyle("Parameter-" + numParameters, null);
      JLabel label = new JLabel(descr.getDisplayName());
      label.setAlignmentY(0.8f); // make sure we line up
      label.setFont(new Font("Helvetica", Font.PLAIN, 14));
      label.setForeground(Color.BLUE);
      label.setName(descr.getKey());
      label.setToolTipText("key: " + descr.getKey() + "    format: " + format);
      StyleConstants.setComponent(style, label);
      doc.insertString(pos, format, style);
      numParameters++;
    } catch (BadLocationException e) {
    }
  }

  private void insertText(String text, AttributeSet set, int pos) {
    try {
      editorPane.getDocument().insertString(pos, text, set);
    } catch (BadLocationException ex) {
    }
  }

  private void appendText(String text, AttributeSet set) {
    insertText(text, set, editorPane.getDocument().getLength());
  }

  private void updateTemplateFromPrintf() {
    String format = formatTextArea.getText();
    String parameters = parameterTextArea.getText();
    editableTemplate.setFormat(format);
    editableTemplate.setParameters(parameters);
  }

  private void updateTemplateFromEditor(PrintfTemplate template) {
    ArrayList params = new ArrayList();
    String format = null;
    int text_length = editorPane.getDocument().getLength();
    try {
      format = editorPane.getDocument().getText(0, text_length);
    } catch (BadLocationException ex1) {
    }
    Element section_el = editorPane.getDocument().getDefaultRootElement();
    // Get number of paragraphs.
    int num_para = section_el.getElementCount();
    for (int p_count = 0; p_count < num_para; p_count++) {
      Element para_el = section_el.getElement(p_count);
      // Enumerate the content elements
      int num_cont = para_el.getElementCount();
      for (int c_count = 0; c_count < num_cont; c_count++) {
        Element content_el = para_el.getElement(c_count);
        AttributeSet attr = content_el.getAttributes();
        // Get the name of the style applied to this content element; may be null
        String sn = (String) attr.getAttribute(StyleConstants.NameAttribute);
        // Check if style name match
        if (sn != null && sn.startsWith("Parameter")) {
          // we extract the label.
          JLabel l = (JLabel) StyleConstants.getComponent(attr);
          if (l != null) {
            params.add(l.getName());
          }
        }
      }
    }

    template.setFormat(format);
    template.setTokens(params);
  }

  protected void setTemplate(PrintfTemplate template) {
    originalTemplate = template;
    editableTemplate.setFormat(template.m_format);
    editableTemplate.setTokens(template.m_tokens);
    updatePane(selectedPane);
  }

  private void updateParams(EDPCellData data) {
    paramComboBox.removeAllItems();
    paramKeys = data.getPlugin().getPrintfDescrs(!m_isCrawlRuleEditor);
    if (!m_isCrawlRuleEditor) {
      paramComboBox.addItem(STRING_LITERAL);
    }

    for (Iterator it = paramKeys.values().iterator(); it.hasNext(); ) {
      ConfigParamDescr descr = (ConfigParamDescr) it.next();
      int type = descr.getType();
      if (!m_isCrawlRuleEditor
          && (type == ConfigParamDescr.TYPE_SET || type == ConfigParamDescr.TYPE_RANGE)) continue;
      paramComboBox.addItem(descr);
    }
    paramComboBox.setEnabled(true);
    paramComboBox.setSelectedIndex(0);
    paramComboBox.setToolTipText("Select a parameter to insert into the format string");
    insertButton.setEnabled(true);
  }

  /**
   * updatePane
   *
   * @param sel int
   */
  private void updatePane(int sel) {
    switch (sel) {
      case 0: // editor view
        updateEditorView();
        break;
      case 1: // printf view
        updatePrintfView();
        break;
    }
  }

  private void updatePrintfView() {
    formatTextArea.setText(editableTemplate.m_format);
    parameterTextArea.setText(editableTemplate.getTokenString());
  }

  private void updateEditorView() {
    editorPane.setText("");
    numParameters = 0;
    try {
      java.util.List elements = editableTemplate.getPrintfElements();
      for (Iterator it = elements.iterator(); it.hasNext(); ) {
        PrintfUtil.PrintfElement el = (PrintfUtil.PrintfElement) it.next();
        if (el.getFormat().equals(PrintfUtil.PrintfElement.FORMAT_NONE)) {
          appendText(el.getElement(), PLAIN_ATTR);
        } else {
          insertParameter(
              (ConfigParamDescr) paramKeys.get(el.getElement()),
              el.getFormat(),
              editorPane.getDocument().getLength());
        }
      }
    } catch (Exception ex) {
      JOptionPane.showMessageDialog(
          this,
          "Invalid Format: " + ex.getMessage(),
          "Invalid Printf Format",
          JOptionPane.ERROR_MESSAGE);

      selectedPane = 1;
      printfTabPane.setSelectedIndex(selectedPane);
      updatePane(selectedPane);
    }
  }

  /**
   * Return a copy of the string with all reserved regexp chars escaped by backslash.
   *
   * @param str the string to add escapes to
   * @return String return a string with escapes or "" if str is null
   */
  private String escapeReservedChars(String str) {
    if (str == null) return "";
    StringBuilder sb = new StringBuilder();
    for (int ci = 0; ci < str.length(); ci++) {
      char ch = str.charAt(ci);
      if (RESERVED_STRING.indexOf(ch) >= 0) {
        sb.append('\\');
      }
      sb.append(ch);
    }
    return escapePrintfChars(sb.toString());
  }

  private String escapePrintfChars(String str) {
    if (str == null) return "";
    StringBuilder sb = new StringBuilder();
    for (int ci = 0; ci < str.length(); ci++) {
      char ch = str.charAt(ci);
      if (ch == '%') {
        sb.append('%');
      }
      sb.append(ch);
    }
    return sb.toString();
  }
}