public void actionPerformed(ActionEvent event) {
      final Object item = fieldMortgageChoices.getSelectedItem();
      if (item instanceof Mortgage) {
        final Mortgage mortgageTerms = (Mortgage) item;

        // update the fields, but tell the document listener to
        // not react to those changes since we don't want the
        // combo box to then be changed in turn
        ignoreDocumentChange = true;
        fieldRate.setText(FMT_RATE.format(mortgageTerms.getRate()));
        fieldTerm.setText(FMT_TERM.format(mortgageTerms.getTerm()));
        ignoreDocumentChange = false;
      }
    }
  /**
   * Utility method that will calculate a new mortgage payment and payment breakdown based on the
   * user's current selection.
   */
  private void calculateMortgage() {
    boolean inputValid = true;

    // fetch the new values from the data entry fields and calculate a
    // new mortgage
    double principal = 0.0;
    try {
      final String principalText = fieldPrincipal.getText();
      principal = FMT_PRINCIPAL.parse(principalText).doubleValue();
      if (principal <= 0.0) {
        invalidInput(fieldPrincipal, MSG_PRINCIPAL_NEGATIVE);
        inputValid = false;
      }
    } catch (ParseException ex) {
      invalidInput(fieldPrincipal, MSG_INVALID_INPUT);
      inputValid = false;
    }

    double rate = 0.0;
    if (inputValid) {
      try {
        final String rateText = fieldRate.getText();
        rate = FMT_RATE.parse(rateText).doubleValue();
        if (rate <= 0.0) {
          invalidInput(fieldRate, MSG_RATE_NEGATIVE);
          inputValid = false;
        }
      } catch (ParseException ex) {
        invalidInput(fieldRate, MSG_INVALID_INPUT);
        inputValid = false;
      }
    }

    int term = 0;
    if (inputValid) {
      try {
        final String termText = fieldTerm.getText();
        term = FMT_TERM.parse(termText).intValue();
        if (term <= 0) {
          invalidInput(fieldTerm, MSG_TERM_NEGATIVE);
          inputValid = false;
        }
      } catch (ParseException ex) {
        invalidInput(fieldTerm, MSG_INVALID_INPUT);
        inputValid = false;
      }
    }

    if (inputValid) {
      // create a new Mortgage object from the selected mortgage terms and
      // then update the principal with the user's requested value
      final Mortgage mortgage = new Mortgage(principal, rate, term);

      // format the mortgage payment into a user-friendly message
      final Object[] values = {new Double(mortgage.getPayment())};
      final String message = MessageFormat.format(MSG_PAYMENT, values);

      // update our message label and payment data and force our UI
      // to repaint itself with the new mortgage results
      labelMessage.setText(message);
      paymentsModel.setPayments(mortgage.getPayments());

      // create a data set so the chart may be updated with the mortgage
      // payment detail
      final MortgagePayment[] payments = mortgage.getPayments();
      final double[][] chartData = new double[2][payments.length];
      for (int i = 0; i < payments.length; i++) {
        chartData[0][i] = payments[i].getInterest().doubleValue();
        chartData[1][i] = payments[i].getPrincipal().doubleValue();
      }

      final String[] chartDataNames = {"Interest", "Principal"};
      final NumberFormat[] formats = {new DecimalFormat("#,##0"), new DecimalFormat("$ #,##0.00")};

      paymentsChart.setData(chartData, chartDataNames, formats);

      repaint();
    }
  }