/* From http://java.sun.com/docs/books/tutorial/index.html */
/*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* InputVerificationDialogDemo.java is a 1.4 example that
* requires no other files.
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.BorderFactory;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
* InputVerificationDialogDemo.java is a 1.4 example that requires no other
* files.
*
* Yet another mortgage calculator. However, instead of using a formatted text
* field, as shown in FormattedTextFieldDemo, this example uses input
* verification to validate user input. This one uses a dialog to warn people
* when their input is bad.
*/
public class InputVerificationDialogDemo extends JPanel {
//Default values
private static double DEFAULT_AMOUNT = 100000;
private static double DEFAULT_RATE = 7.5; //7.5 %
private static int DEFAULT_PERIOD = 30;
//Labels to identify the text fields
private JLabel amountLabel;
private JLabel rateLabel;
private JLabel numPeriodsLabel;
private JLabel paymentLabel;
//Strings for the labels
private static String amountString = "Loan Amount (10,000 - 10,000,000): ";
private static String rateString = "APR (>= 0%): ";
private static String numPeriodsString = "Years (1-40): ";
private static String paymentString = "Monthly Payment: ";
//Text fields for data entry
private JTextField amountField;
private JTextField rateField;
private JTextField numPeriodsField;
private JTextField paymentField;
//Formats to format and parse numbers
private NumberFormat moneyFormat;
private NumberFormat percentFormat;
private DecimalFormat decimalFormat;
private DecimalFormat paymentFormat;
private NumberFormat integerFormat;
private MyVerifier verifier = new MyVerifier();
public InputVerificationDialogDemo() {
super(new BorderLayout());
setUpFormats();
double payment = computePayment(DEFAULT_AMOUNT, DEFAULT_RATE,
DEFAULT_PERIOD);
//Create the labels.
amountLabel = new JLabel(amountString);
rateLabel = new JLabel(rateString);
numPeriodsLabel = new JLabel(numPeriodsString);
paymentLabel = new JLabel(paymentString);
//Create the text fields and set them up.
amountField = new JTextField(moneyFormat.format(DEFAULT_AMOUNT), 10);
amountField.setInputVerifier(verifier);
rateField = new JTextField(percentFormat.format(DEFAULT_RATE), 10);
rateField.setInputVerifier(verifier);
numPeriodsField = new JTextField(decimalFormat.format(DEFAULT_PERIOD),
10);
numPeriodsField.setInputVerifier(verifier);
paymentField = new JTextField(paymentFormat.format(payment), 10);
paymentField.setInputVerifier(verifier);
paymentField.setEditable(false);
//Remove this component from the focus cycle.
paymentField.setFocusable(false);
paymentField.setForeground(Color.red);
//Register an action listener to handle Return.
amountField.addActionListener(verifier);
rateField.addActionListener(verifier);
numPeriodsField.addActionListener(verifier);
//Tell accessibility tools about label/textfield pairs.
amountLabel.setLabelFor(amountField);
rateLabel.setLabelFor(rateField);
numPeriodsLabel.setLabelFor(numPeriodsField);
paymentLabel.setLabelFor(paymentField);
//Lay out the labels in a panel.
JPanel labelPane = new JPanel(new GridLayout(0, 1));
labelPane.add(amountLabel);
labelPane.add(rateLabel);
labelPane.add(numPeriodsLabel);
labelPane.add(paymentLabel);
//Layout the text fields in a panel.
JPanel fieldPane = new JPanel(new GridLayout(0, 1));
fieldPane.add(amountField);
fieldPane.add(rateField);
fieldPane.add(numPeriodsField);
fieldPane.add(paymentField);
//Put the panels in this panel, labels on left,
//text fields on right.
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
add(labelPane, BorderLayout.CENTER);
add(fieldPane, BorderLayout.LINE_END);
}
class MyVerifier extends InputVerifier implements ActionListener {
double MIN_AMOUNT = 10000.0;
double MAX_AMOUNT = 10000000.0;
double MIN_RATE = 0.0;
int MIN_PERIOD = 1;
int MAX_PERIOD = 40;
String message = null;
public boolean shouldYieldFocus(JComponent input) {
boolean inputOK = verify(input);
makeItPretty(input);
updatePayment();
if (inputOK) {
return true;
}
//Avoid possible focus-transfer problems when bringing up
//the dialog by temporarily removing the input verifier.
//This is a workaround for bug #4532517.
input.setInputVerifier(null);
//Pop up the message dialog.
message += ".\nPlease try again.";
JOptionPane.showMessageDialog(null, //no owner frame
message, //text to display
"Invalid Value", //title
JOptionPane.WARNING_MESSAGE);
//Reinstall the input verifier.
input.setInputVerifier(this);
//Beep and then tell whoever called us that we don't
//want to yield focus.
Toolkit.getDefaultToolkit().beep();
return false;
}
protected void updatePayment() {
double amount = DEFAULT_AMOUNT;
double rate = DEFAULT_RATE;
int numPeriods = DEFAULT_PERIOD;
double payment = 0.0;
//Parse the values.
try {
amount = moneyFormat.parse(amountField.getText()).doubleValue();
} catch (ParseException pe) {
}
try {
rate = percentFormat.parse(rateField.getText()).doubleValue();
} catch (ParseException pe) {
}
try {
numPeriods = decimalFormat.parse(numPeriodsField.getText())
.intValue();
} catch (ParseException pe) {
}
//Calculate the result and update the GUI.
payment = computePayment(amount, rate, numPeriods);
paymentField.setText(paymentFormat.format(payment));
}
//This method checks input, but should cause no side effects.
public boolean verify(JComponent input) {
return checkField(input, false);
}
protected void makeItPretty(JComponent input) {
checkField(input, true);
}
protected boolean checkField(JComponent input, boolean changeIt) {
if (input == amountField) {
return checkAmountField(changeIt);
} else if (input == rateField) {
return checkRateField(changeIt);
} else if (input == numPeriodsField) {
return checkNumPeriodsField(changeIt);
} else {
return true; //shouldn't happen
}
}
//Checks that the amount field is valid. If it is valid,
//it returns true, otherwise it sets the message field and
//returns false. If the change argument is true, set
//the textfield to the parsed number so that it looks
//good -- no letters, for example.
public boolean checkAmountField(boolean change) {
boolean wasValid = true;
double amount = DEFAULT_AMOUNT;
//Parse the value.
try {
amount = moneyFormat.parse(amountField.getText()).doubleValue();
} catch (ParseException pe) {
message = "Invalid money format in Loan Amount field";
return false;
}
//Value was invalid.
if ((amount < MIN_AMOUNT) || (amount > MAX_AMOUNT)) {
wasValid = false;
if (amount < MIN_AMOUNT) {
message = "Loan Amount was < "
+ integerFormat.format(MIN_AMOUNT);
} else { //amount is greater than MAX_AMOUNT
message = "Loan Amount was > "
+ integerFormat.format(MAX_AMOUNT);
}
}
//Whether value was valid or not, format it nicely.
if (change) {
amountField.setText(moneyFormat.format(amount));
amountField.selectAll();
}
return wasValid;
}
//Checks that the rate field is valid. If it is valid,
//it returns true, otherwise it sets the message field and
//returns false. If the change argument is true, set the
//textfield to the parsed number so that it looks good -- no
//letters, for example.
public boolean checkRateField(boolean change) {
boolean wasValid = true;
double rate = DEFAULT_RATE;
//Parse the value.
try {
rate = percentFormat.parse(rateField.getText()).doubleValue();
} catch (ParseException pe) {
message = "Invalid percent format in APR field";
return false;
}
//Value was invalid.
if (rate < MIN_RATE) {
wasValid = false;
message = "Bad value: APR was < " + MIN_RATE;
}
//Whether value was valid or not, format it nicely.
if (change) {
rateField.setText(percentFormat.format(rate));
rateField.selectAll();
}
return wasValid;
}
//Checks that the numPeriods field is valid. If it is valid,
//it returns true, otherwise it sets the message field and
//returns false. If the change argument is true, set the
//textfield to the parsed number so that it looks good -- no
//letters, for example.
public boolean checkNumPeriodsField(boolean change) {
boolean wasValid = true;
int numPeriods = DEFAULT_PERIOD;
//Parse the value.
try {
numPeriods = decimalFormat.parse(numPeriodsField.getText())
.intValue();
} catch (ParseException pe) {
message = "Invalid decimal format in Years field";
return false;
}
//Value was invalid.
if (numPeriods < MIN_PERIOD) {
wasValid = false;
message = "Bad value: Number of years was < "
+ integerFormat.format(MIN_PERIOD);
} else if (numPeriods > MAX_PERIOD) {
wasValid = false;
message = "Bad value: Number of years was > "
+ integerFormat.format(MAX_PERIOD);
}
//Whether value was valid or not, format it nicely.
if (change) {
numPeriodsField.setText(decimalFormat.format(numPeriods));
numPeriodsField.selectAll();
}
return wasValid;
}
public void actionPerformed(ActionEvent e) {
JTextField source = (JTextField) e.getSource();
shouldYieldFocus(source); //ignore return value
source.selectAll();
}
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
//We can't set this due to bug #4819813.
//This bug causes the Java look and feel window
//decorations to be focusable.
//JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window.
JFrame frame = new JFrame("InputVerificationDialogDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new InputVerificationDialogDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
//Compute the monthly payment based on the loan amount,
//APR, and length of loan.
double computePayment(double loanAmt, double rate, int numPeriods) {
double I, partial1, denominator, answer;
numPeriods *= 12; //get number of months
if (rate > 0.01) {
I = rate / 100.0 / 12.0; //get monthly rate from annual
partial1 = Math.pow((1 + I), (0.0 - numPeriods));
denominator = (1 - partial1) / I;
} else { //rate ~= 0
denominator = numPeriods;
}
answer = (-1 * loanAmt) / denominator;
return answer;
}
//Create and set up number formats. These objects also
//parse numbers input by user.
private void setUpFormats() {
moneyFormat = (NumberFormat) NumberFormat.getNumberInstance();
percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(3);
decimalFormat = (DecimalFormat) NumberFormat.getNumberInstance();
decimalFormat.setParseIntegerOnly(true);
paymentFormat = (DecimalFormat) NumberFormat.getNumberInstance();
paymentFormat.setMaximumFractionDigits(2);
paymentFormat.setNegativePrefix("(");
paymentFormat.setNegativeSuffix(")");
integerFormat = NumberFormat.getIntegerInstance();
}
}