Incrementally Updating Software 
by John Keogh

Listing One

SimpleMortgageCalculator() {
  super();
  this.setUndecorated(true);
  //this is where the settings from the last use are retrieved
  try{
    applicationProperties.load(new FileInputStream(CALCULATOR_CONFIG_FILE));
  }
  catch(Exception ex){
    System.out.println("problem loading properties
                                     file on startup "+ex.toString());
  }
  // Add the window listener
  addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent evt) {
      dispose();
      System.exit(0);
  }});

  addMouseListener(this);
  addMouseMotionListener(this);

  //don't want this resizable, as it makes the background look odd
  this.setResizable(false);

  buttons=new Button[4];//calculate, skins, languages, quit
  fields=new TextField[4];//Amount, Interest Term, Length in years, Results
  labels=new Label[4];//Amount, Interest Term, Length in years, Results

  buttons[CALCULATE]=new Button("");//calculate
  buttons[SKINS]=new Button("");//skins
  buttons[LANGUAGES]=new Button("");//languages
  buttons[QUIT]=new Button("");//quit
  buttons[CALCULATE].addActionListener(this);
  buttons[SKINS].addActionListener(this);
  buttons[LANGUAGES].addActionListener(this);
  buttons[QUIT].addActionListener(this);
  this.add(buttons[CALCULATE]);
  this.add(buttons[SKINS]);
  this.add(buttons[LANGUAGES]);
  this.add(buttons[QUIT]);

  fields[AMOUNT]=new TextField(15);//amount
  fields[INTEREST]=new TextField(6);//interest
  fields[TERM]=new TextField(4);//term in years
  fields[RESULTS]=new TextField(10);//results
  this.add(fields[AMOUNT]);
  this.add(fields[INTEREST]);
  this.add(fields[TERM]);
  this.add(fields[RESULTS]);

  labels[AMOUNT]=new Label("");//amount
  labels[INTEREST]=new Label("");//interest
  labels[TERM]=new Label("");//terms
  labels[RESULTS]=new Label("");//results
  this.add(labels[AMOUNT]);
  this.add(labels[INTEREST]);
  this.add(labels[TERM]);
  this.add(labels[RESULTS]);

  //this is where the last skin and language are reloaded
  String lastSkin=applicationProperties.getProperty(LAST_SKIN_PROPERTY);
  String lastLanguage=
              applicationProperties.getProperty(LAST_LANGUAGE_PROPERTY);
  if(lastSkin==null) lastSkin="downtown.jar";
  if(lastLanguage==null) lastLanguage="english.properties";
  SkinLoader.getInstance().loadNewSkin(lastSkin);
  LanguageLoader.getInstance().loadNewLanguage(this, lastLanguage);
  Dimension d=SkinLoader.getInstance().getSize();
  setSize((int)d.getWidth(), (int)d.getHeight());
  //Center the window
  Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
  Rectangle winDim = getBounds();
  setLocation((screenDim.width - winDim.width) / 2,
         (screenDim.height - winDim.height) / 2);
  setVisible(true);
  //need a visible window to render the image onto.
  backgroundImage = SkinLoader.getInstance().getImage(this);
  SkinLoader.getInstance().addWidgets(this, buttons, fields, labels);
}


Listing Two

public void actionPerformed(ActionEvent e){
  if(e.getSource()==buttons[CALCULATE]){//calculate button
    double dResult=Calculate.crunch(fields[AMOUNT].getText(),
                    fields[INTEREST].getText(), fields[TERM].getText());
    if(dResult>0.0){
      fields[RESULTS].setText(""+(int)dResult);
    }
    else{
      fields[RESULTS].setText(LanguageLoader.
                               getInstance().lookUpString("badresult"));
    }
  }
  else if(e.getSource()==buttons[SKINS]){//skins button
    SkinLoader.getInstance().chooseSkin(this);

    //size is in the skin
    Dimension d=SkinLoader.getInstance().getSize();
    setSize((int)d.getWidth(), (int)d.getHeight());
    //as is the image
    backgroundImage = SkinLoader.getInstance().getImage(this);
    //and the display area
    this.paint(this.getGraphics());
    //add controls
    SkinLoader.getInstance().addWidgets(this, buttons, fields, labels);
  }
  else if(e.getSource()==buttons[LANGUAGES]){//languages
    LanguageLoader.getInstance().chooseLanguage(this);
  }
  else if(e.getSource()==buttons[QUIT]){//quit button
    try{
      //remember what the last skin and language were
      String lastSkin=SkinLoader.getInstance().getCurrentSkin();
      String lastLanguage=LanguageLoader.getInstance().getCurrentLanguage();
      applicationProperties.setProperty(LAST_SKIN_PROPERTY, lastSkin);
      applicationProperties.setProperty(LAST_LANGUAGE_PROPERTY, lastLanguage);
      applicationProperties.
              store(new FileOutputStream(CALCULATOR_CONFIG_FILE),
                                  "SimpleMortageCaculator properties file");
    }
    catch(Exception ex){
      System.out.println("problem storing properties
                                          file on shutdown "+ex.toString());
    }
    //write out the configuration file, then quit
    exit();
  }
}


Listing Three

  public void loadComponent(String skinFile){
    try{
      URL u = new URL("jar:file:skins/"+skinFile+"!/");
      int index=skinFile.indexOf(".jar");
      String thisClass=skinFile.substring(0, index);
      String strClass="skins."+thisClass;//jar and main class files match
      ucl = new URLClassLoader(new URL[] { u });
      c= (Class) Class.forName(strClass, true, ucl);
      m = c.getMethod(SKIN_METHOD, SKIN_METHOD_ARGS);
      currentSkin=skinFile;
      image=null;
    }
    catch(Exception ex){
      System.out.println("exception in SkinLoader.loadComponent() "+ex.toString());
    }
  }
