Convert numbers between binary, octal, decimal and hexadecimal.
/*
* Copyright (c) 2002, 2003, 2009 Jochen Kupperschmidt
* Released under the terms of the MIT License
* _ _
* | |_ ___ _____ ___ _ _ _ ___ ___| |_
* | | . | | ._| | | | . | _| . /
* |_|_|___|_|_|_|___|_____|___|_| |_|_\
* http://homework.nwsnet.de/
*
* Convert numbers between binary, octal, decimal and hexadecimal.
*
* This is a Groovy remake (24-Dec-2009) of the original Java application
* (12-Jan-2003) and requires significantly less code.
*/
import groovy.swing.SwingBuilder
import java.awt.GridLayout
import javax.swing.ButtonGroup
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.JRadioButton
import javax.swing.JTextField
import javax.swing.text.AbstractDocument
import javax.swing.text.AttributeSet
import javax.swing.text.BadLocationException
import javax.swing.text.PlainDocument
enum NumberSystem {
BINARY(2, '01', { Integer.toBinaryString(it) }),
OCTAL(8, '01234567', { Integer.toOctalString(it) }),
DECIMAL(10, '0123456789', { Integer.toString(it) }),
HEXADECIMAL(16, '0123456789abcdefABCDEF',
{ Integer.toHexString(it).toUpperCase() })
final int base
final Set<String> validCharacters
final Closure toStringValue
NumberSystem(int base, String validCharacters, Closure toStringValue) {
this.base = base
this.validCharacters = new HashSet<String>(validCharacters.collect { it })
this.toStringValue = toStringValue
}
}
class NumberDocument extends PlainDocument {
private Set<String> validCharacters
private Closure actionHandler
def NumberDocument(Set<String> validCharacters, Closure actionHandler) {
this.validCharacters = validCharacters
this.actionHandler = actionHandler
}
// Fire action on text insertion.
public void insertUpdate(AbstractDocument.DefaultDocumentEvent event,
AttributeSet attrSet) {
actionHandler()
}
// Fire action on text removal.
public void postRemoveUpdate(AbstractDocument.DefaultDocumentEvent event) {
actionHandler()
}
// Validate the entered characters.
public void insertString(int offset, String str, AttributeSet attrSet)
throws BadLocationException {
super.insertString(offset,
str.findAll { validCharacters.contains(it) }.join(''), attrSet)
}
}
/**
* A panel containing a labeled radio button and a special text field.
*/
class NumberPanel extends JPanel {
NumberSystem system
JRadioButton radioButton
JTextField field
def NumberPanel(NumberSystem system, boolean selected) {
this.system = system
layout = new GridLayout(0, 2)
// Create a radio button.
radioButton = new JRadioButton(selected: selected,
text: system.name()[0] + system.name()[1..-1].toLowerCase() + ':')
add(radioButton)
// Create a text field that only accepts specified characters.
field = new JTextField(document: new NumberDocument(system.validCharacters,
{ field.fireActionPerformed() }), enabled: selected,
horizontalAlignment: JTextField.RIGHT)
add(field)
}
def void changeInputSystem() {
field.enabled = radioButton.selected
}
def void clear() {
field.text = ''
}
def boolean isEmpty() {
field.text.trim().isEmpty()
}
def int getValue() throws NumberFormatException {
Integer.valueOf(field.text.trim(), system.base)
}
def void setValue(int value) {
field.text = system.toStringValue(value)
}
}
class NumberPanelGroup {
def panels = []
ButtonGroup radioButtonGroup = new ButtonGroup()
boolean locked = false
def NumberPanelGroup(NumberSystem selectedSystem) {
NumberSystem.values().each {system ->
def numberPanel = new NumberPanel(system, system == selectedSystem)
radioButtonGroup.add(numberPanel.radioButton)
numberPanel.radioButton.actionPerformed = { panels*.changeInputSystem() }
numberPanel.field.actionPerformed = {
// Locking is needed since it would cause another
// action event otherwise, resulting in a loop.
if (locked) {
return
}
locked = true
try {
if (!numberPanel.isEmpty()) {
// Re-calculate values.
panels*.value = numberPanel.value
} else {
// Clear all fields.
panels*.clear()
}
} finally {
locked = false
}
}
panels << numberPanel
}
}
}
// Program starts here.
def swing = new SwingBuilder()
swing.lookAndFeel('gtk')
swing.frame(defaultCloseOperation: JFrame.EXIT_ON_CLOSE,
locationRelativeTo: null, pack: true, resizable: false, show: true,
title: 'Number Converter') {
panel(border: compoundBorder(emptyBorder(4),
compoundBorder(etchedBorder(), emptyBorder(2)))) {
gridLayout(cols: 1, rows: 0)
new NumberPanelGroup(NumberSystem.DECIMAL).panels.each { widget(it) }
}
}