// -*- mode:java; encoding:utf-8 -*-
// vim:set fileencoding=utf-8:
// @homepage@

package example;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.BitSet;
import java.util.Collections;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.swing.*;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEditSupport;

public class MainPanel extends JPanel {
  // Long.MAX_VALUE
  // 0b111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111
  // protected static final int BIT_LENGTH = 63;
  protected static final int BIT_LENGTH = 72;
  protected BitSet status = BitSet.valueOf(new long[] {Long.valueOf("111000111", 2)});
  protected static final String ZERO_PAD = String.join("", Collections.nCopies(BIT_LENGTH, "0"));
  protected final transient UndoableEditSupport undoSupport = new UndoableEditSupport();
  private final JLabel label = new JLabel(print(status));
  private final JPanel panel = new JPanel(new GridLayout(0, 8));

  private MainPanel() {
    super(new BorderLayout());
    UndoManager um = new UndoManager();
    undoSupport.addUndoableEditListener(um);

    Action undoAction = new UndoAction(um);
    Action redoAction = new RedoAction(um);
    Action selectAllAction = new AbstractAction("select all") {
      @Override public void actionPerformed(ActionEvent e) {
        BitSet newValue = new BitSet(BIT_LENGTH);
        newValue.set(0, BIT_LENGTH, true);
        undoSupport.postEdit(new StatusEdit(status, newValue));
        updateCheckBoxes(newValue);
      }
    };
    Action clearAllAction = new AbstractAction("clear all") {
      @Override public void actionPerformed(ActionEvent e) {
        BitSet newValue = new BitSet(BIT_LENGTH);
        undoSupport.postEdit(new StatusEdit(status, newValue));
        updateCheckBoxes(newValue);
      }
    };

    Box box = Box.createHorizontalBox();
    box.add(Box.createHorizontalGlue());
    Stream.of(undoAction, redoAction, selectAllAction, clearAllAction).map(JButton::new).forEach(b -> {
      box.add(b);
      box.add(Box.createHorizontalStrut(2));
    });

    IntStream.range(0, BIT_LENGTH).forEach(i -> {
      JCheckBox c = new JCheckBox(Integer.toString(i), status.get(i));
      c.addActionListener(e -> {
        JCheckBox cb = (JCheckBox) e.getSource();
        BitSet newValue = status.get(0, BIT_LENGTH);
        newValue.set(i, cb.isSelected());
        undoSupport.postEdit(new StatusEdit(status, newValue));
        status = newValue;
        label.setText(print(status));
      });
      panel.add(c);
    });

    label.setFont(label.getFont().deriveFont(8f));

    add(label, BorderLayout.NORTH);
    add(new JScrollPane(panel));
    add(box, BorderLayout.SOUTH);
    setPreferredSize(new Dimension(320, 240));
  }

  protected final void updateCheckBoxes(BitSet value) {
    status = value;
    for (int i = 0; i < BIT_LENGTH; i++) {
      ((JCheckBox) panel.getComponent(i)).setSelected(status.get(i));
    }
    label.setText(print(status));
  }

  private class StatusEdit extends AbstractUndoableEdit {
    private final BitSet oldValue;
    private final BitSet newValue;

    protected StatusEdit(BitSet oldValue, BitSet newValue) {
      super();
      this.oldValue = oldValue;
      this.newValue = newValue;
    }

    @Override public void undo() { // throws CannotUndoException {
      super.undo();
      updateCheckBoxes(oldValue);
    }

    @Override public void redo() { // throws CannotRedoException {
      super.redo();
      updateCheckBoxes(newValue);
    }
  }

  private static String print(BitSet bitSet) {
    StringBuilder buf = new StringBuilder();
    for (long lv: bitSet.toLongArray()) {
      buf.insert(0, Long.toUnsignedString(lv, 2));
    }
    String b = buf.toString();
    int count = bitSet.cardinality();
    return "<html>0b" + ZERO_PAD.substring(b.length()) + b + "<br/> count: " + count;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(MainPanel::createAndShowGui);
  }

  private static void createAndShowGui() {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
      ex.printStackTrace();
      Toolkit.getDefaultToolkit().beep();
    }
    JFrame frame = new JFrame("@title@");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new MainPanel());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

class UndoAction extends AbstractAction {
  private final UndoManager um;

  protected UndoAction(UndoManager um) {
    super("undo");
    this.um = um;
  }

  @Override public void actionPerformed(ActionEvent e) {
    if (um.canUndo()) {
      um.undo();
    }
  }
}

class RedoAction extends AbstractAction {
  private final UndoManager um;

  protected RedoAction(UndoManager um) {
    super("redo");
    this.um = um;
  }

  @Override public void actionPerformed(ActionEvent e) {
    if (um.canRedo()) {
      um.redo();
    }
  }
}

@@ -428,16 +428,22 @@
 %0Apublic 
+final 
 class Ma
@@ -467,16 +467,16 @@
 Panel %7B%0A
-
   // Lon
@@ -579,24 +579,21 @@
 1%0A  // p
-rotected
+ublic
  static 
@@ -618,32 +618,29 @@
 TH = 63;%0A  p
-rotected
+ublic
  static fina
@@ -665,24 +665,21 @@
  72;%0A  p
-rotected
+ublic
  BitSet 
@@ -749,24 +749,21 @@
 )%7D);%0A  p
-rotected
+ublic
  static 
@@ -840,32 +840,29 @@
 , %220%22));%0A  p
-rotected
+ublic
  final trans
@@ -2846,22 +2846,16 @@
 otected 
-final 
 void upd

use final class