Chitika

Saturday, June 16, 2007

Sample Calculator GUI in Java Swing

Recently, I was giving my 1st-year IT students several Swing GUI exercises on OOP with Java lab sessions. One of the exercises was to create a simplified Calculator GUI that looks like the Calculator on standard Windows OS. The objective of the exercise was to make the students familiar with the basic Swing layout managers (note: I haven't thought them GridBag, Spring, or JGoodies Forms layouts), and the basic event-handling mechanism in Swing. The exercise was also meant for the students to learn more about the OOP concept in practice.




Here's the code that I gave the students at the end of the session:








public class Calculator {

  // constant values
  public static final int NONE = -1;
  public static final int ADD = 0;
  public static final int SUBTRACT = 1;
  public static final int MULTIPLY = 2;
  public static final int DIVIDE = 3;

  // values to be stored in the calculator memory
  private double leftValue = 0.0;
  private double rightValue = 0.0;
  private int lastOperation = NONE;
  private double multiplier = 1;
  private boolean DOT = false;

  // the user hits the + button
  public double add () {
    lastOperation = ADD;
    resetDOT();
    return leftValue;
  }

  // the user hits the - button
  public double subtract () {
    lastOperation = SUBTRACT;
    resetDOT();
    return leftValue;
  }

  // the user hits the * button
  public double multiply () {
    lastOperation = MULTIPLY;
    resetDOT();
    return leftValue;
  }

  // the user hits the / button
  public double divide () {
    lastOperation = DIVIDE;
    resetDOT();
    return leftValue;
  }

  // the user hits the = button
  public double equate () {
    switch (lastOperation) {
      case NONE: break;
      case ADD: leftValue = leftValue + rightValue; break;
      case SUBTRACT: leftValue = leftValue - rightValue; break;
      case MULTIPLY: leftValue = leftValue * rightValue; break;
      case DIVIDE: leftValue = leftValue / rightValue; break;
    }
    rightValue = 0;
    resetDOT();
    return leftValue;
  }

  // the user hits the 0-9 button
  public double number (int i) {
    double j = i * multiplier;
    if (DOT) {
      multiplier = multiplier / 10;
    }
    if (lastOperation == NONE) {
      leftValue = leftValue * (DOT ? 10(leftValue < (-j: j);
      return leftValue;
    else {
      rightValue = rightValue * (DOT ? 10(rightValue < (-j: j);
      return rightValue;
    }
  }

  // the user hits the +/- button
  public double plusMinus () {
    if (lastOperation == NONE) {
      leftValue = -leftValue;
      return leftValue;
    else {
      rightValue = -rightValue;
      return rightValue;
    }
  }

  // the user hits the C button
  public double reset () {
    lastOperation = NONE;
    leftValue = rightValue = 0;
    resetDOT();
    return leftValue;
  }

  // the user hits the sqrt button
  public double sqrt () {
    resetDOT();
    if (lastOperation == NONE) {
      leftValue = Math.sqrt (leftValue);
      return leftValue;
    else {
      rightValue = Math.sqrt (rightValue);
      return rightValue;
    }
  }

  // to reset the DOT & multiplier
  private void resetDOT() {
    DOT = false;
    multiplier = 1;
  }

  // the user hits the . button
  public double dot () {
    if (!DOT) {
      DOT = true;
      multiplier = 0.1;
    }
    return lastOperation == NONE ? leftValue : rightValue;
  }
}




I divided this small application into two different classes. The Calculator class above will act as the model for the application, and is responsible to maintain the state of the Calculator and to handle the user actions. Having a minimum amount of time duration for the lab session, many of the Calculator features were not implemented. Many exceptions were not caught nor handled properly, and there's a slightly different behavior with the sqrt button (when you compare it with the Windows OS version).

The whole point of this Calendar application was not to make a copycat of Windows OS version, rather it was to illustrate to the students what they can do with OOP and Swing.

Here's the second part of the application:








import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class CalculatorGUI implements ActionListener {

  private Calculator c = new Calculator ();

  private JFrame frame = new JFrame ("Calculator");
  private JPanel[] panels = new JPanel [6];
  private JTextField textField = new JTextField();
  private JButton resetButton = new JButton(" C ");
  private JButton[] numberButtons = new JButton[10];
  private JButton divideButton = new JButton ("/");
  private JButton multiplyButton = new JButton ("*");
  private JButton subtractButton = new JButton ("-");
  private JButton plusMinusButton = new JButton ("+/-");
  private JButton dotButton = new JButton (" . ");
  private JButton addButton = new JButton ("+");
  private JButton equateButton = new JButton (" = ");
  private JButton sqrtButton = new JButton ("sqrt");

  public void buildGUI () {

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel contentPane = (JPanelframe.getContentPane();

    // initialize panels
    for (int i = 0; i < panels.length; i++) {
      panels[inew JPanel ();
    }

    // initialize button 0-9
    for (int i = 0; i < numberButtons.length; i++) {
      numberButtons[inew JButton (" " + i + " ");
      numberButtons[i].setActionCommand (String.valueOf(i));
      numberButtons[i].addActionListener (this);
    }

    // default layout = BorderLayout.CENTER
    textField.setColumns(20);
    textField.setText ("0");
    textField.setHorizontalAlignment (JTextField.RIGHT);
    panels[0].add (textField);

    // layout = FlowLayout.RIGHT
    panels[1].setLayout (new FlowLayout (FlowLayout.RIGHT));
    panels[1].add (resetButton);
    resetButton.setActionCommand ("RESET");
    resetButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[2].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[2].add (numberButtons[7]);
    panels[2].add (numberButtons[8]);
    panels[2].add (numberButtons[9]);
    panels[2].add (divideButton);
    panels[2].add (sqrtButton);
    divideButton.setActionCommand ("DIVIDE");
    divideButton.addActionListener (this);
    sqrtButton.setActionCommand ("SQRT");
    sqrtButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[3].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[3].add (numberButtons[4]);
    panels[3].add (numberButtons[5]);
    panels[3].add (numberButtons[6]);
    panels[3].add (multiplyButton);
    multiplyButton.setActionCommand ("MULTIPLY");
    multiplyButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[4].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[4].add (numberButtons[1]);
    panels[4].add (numberButtons[2]);
    panels[4].add (numberButtons[3]);
    panels[4].add (subtractButton);
    subtractButton.setActionCommand ("SUBTRACT");
    subtractButton.addActionListener (this);

    // layout = FlowLayout.LEFT
    panels[5].setLayout (new FlowLayout (FlowLayout.LEFT));
    panels[5].add (numberButtons[0]);
    panels[5].add (plusMinusButton);
    panels[5].add (dotButton);
    panels[5].add (addButton);
    panels[5].add (equateButton);
    plusMinusButton.setActionCommand ("PLUSMINUS");
    plusMinusButton.addActionListener (this);
    dotButton.setActionCommand ("DOT");
    dotButton.addActionListener (this);
    addButton.setActionCommand ("ADD");
    addButton.addActionListener (this);
    equateButton.setActionCommand ("EQUATE");
    equateButton.addActionListener (this);

    contentPane.setLayout (new BoxLayout (contentPane, BoxLayout.Y_AXIS));
    for (JPanel jPanel : panels) {
      contentPane.add (jPanel);
    }

    frame.pack ();
    frame.setVisible (true);
  }

  public void actionPerformed (ActionEvent e) {
    String actionCommand = e.getActionCommand();
    if (actionCommand == null || actionCommand.trim().length() <= 0) {
      return;
    }

    int number = -1;
    try {
      number = Integer.parseInt (actionCommand);
    catch (NumberFormatException e1) {
    }

    if (number >= 0) {
      // this is a number
      textField.setText ("" (c.number (number)));
    else {
      // this is not a number
      if (actionCommand.equals ("RESET")) {
        textField.setText ("" (c.reset()));
      else if (actionCommand.equals ("DIVIDE")) {
        textField.setText ("" (c.divide()));
      else if (actionCommand.equals ("SQRT")) {
        textField.setText ("" (c.sqrt()));
      else if (actionCommand.equals ("MULTIPLY")) {
        textField.setText ("" (c.multiply()));
      else if (actionCommand.equals ("SUBTRACT")) {
        textField.setText ("" (c.subtract()));
      else if (actionCommand.equals ("PLUSMINUS")) {
        textField.setText ("" (c.plusMinus()));
      else if (actionCommand.equals ("DOT")) {
        textField.setText ("" (c.dot()));
      else if (actionCommand.equals ("ADD")) {
        textField.setText ("" (c.add()));
      else if (actionCommand.equals ("EQUATE")) {
        textField.setText ("" (c.equate()));
      }
    }
  }

  public static void main(String[] args) {
    CalculatorGUI gui = new CalculatorGUI();
    gui.buildGUI ();
  }
}




I'm pretty sure that there are many solution variants out there. By focusing on the layout managers that are easy to use and understand, such as FlowLayout, BorderLayout and BoxLayout, I think this exercise has nicely served its main objective.

If you have any suggestions on how to help the students to understand the OOP + Swing concepts better, please do let me know.

Sunday, May 20, 2007

Freedom Writer

I've just finished watching Freedom Writers. It's been a long time since I've watched anything with that quality. Not just because I am a lecturer myself, but even before I became one, I still would have thought the same. Simply put, the movie is awesome.

Through the 120+ minutes, my emotion was taken captive by the storyline that I learned a lot from the movie. I have pretty much the same ideals as the teacher in that movie, that as students, you'll learn more by having the family atmosphere in the class rather than having the old boring lecture styles. Most of my colleagues may not agree with me, but I firmly stand on my beliefs, which is why I would be recommending this movie for them to see.

Regardless whether you are a lecturer/teacher or not, or whether you are a parent, a student or none of the above, you'll still love the movie. It really worths 2 hours of your time. I will be exploring more on the Freedom Writers ideas, of how they are going to implement the same methodologies they used in their class in as many classes as possible in the US.

I hope that my colleagues would love the movie, and you would too (if you'd take my recommendation).

Amazon: Freedom Writers (Widescreen Edition)

Friday, January 20, 2006

Encrypted CVS Repository

We currently have a client that's very concerned with Information Security. It's a multinational company, and we're dealing with its Indonesian subsidiary. Its head office periodically performs scans throughout their subsidiaries' networks across the world, and identifies any possible information security breach. The problem is the network scans prove that when someone has read-access to the CVS repository file, he/she can immediately (without any significant effort) browse through the contents of our source codes.

They require us to propose for an immediate solution to the problem. Apparently, having the CVS access security (through encrypted password) and CVS data transfer security (through SSL) are not enough. We are required to prevent the exposure of CVS repository contents even when the CVS repository file itself has been read. David A. Wheeler mentioned this on his SCM Security article.

We currently have two project teams working at the client site. The first project uses CVS whereas the other uses Subversion. We will be having our third project at the client site within the next few weeks. I'm encouraging the third project team to use Subversion instead of CVS. So, considering the importance of this client, it's very important for us to come up with a solution.

After searching through the net, Greg A. Woods mentioned that it's not how CVS was designed. I've tried to search through Subversion site, and I cannot find any information whether Subversion support encrypted repository or not.

Then, it was Thomas Deselaers’ idea that I believe to be a viable solution.

My plan is to find a good open source CVS client implemented in Java. Then, I’ll intercept the commit command (encrypt line by line), and intercept the update command (decrypt line by line). Ensuring also that the encryption is a basic one (probably XOR operation on each character), I’ll have the merge command unmodified.

So, the end result is, the developers will see the un-encrypted source code at their local development environments, whereas the CVS repository will keep the encrypted source code. The diff command will show the diff of the encrypted lines, though.

Anyone has better solution for this?