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?

Monday, January 09, 2006

Reading a character from the Console

Reading a character from the Console should not be a problem. I hadn't had that problem using C++ back in the college days, but somehow it's not that easy in Java. I have never found the need for it myself, but someone at the local JUG mailing list apparently needs it.

Java has always have the problem of reading a character from the Console, without buffering and echoing back to the Console. This has been an outstanding bug for so long, and it's said to be fixed on Mustang. Well, the bug is really about masking password input, not about un-buffering the character inputs. So, my friend there is not really getting his needs fulfilled.

Someone from the mailing list replied with a link to JCurses. So I tried it. I tried Enigma as well, but it doesn't seem to get the job done.

Here's the code:
import jcurses.system.InputChar;
import jcurses.system.Toolkit;

public class Console {
  public static void main (String[] args) {
    while (true) {
      InputChar c = Toolkit.readCharacter ();
    System.out.println (
        "you typed '" + c.getCharacter() + "' (" + c.getCode() + ")");

      // break on Ctrl-C (3)
      if (c.getCode() == 3) break;
    }
  }
}

It's quick & simple. Part of another surprise, there are some other bugs that still remained till Tiger release. The list includes not being able to change working directory.

What's the most irritating bug of JDK that has not been fixed, according to you?
Does the voting mechanism for bug fixes really work?

Monday, January 02, 2006

Understanding Fluent Interface

After reading Martin Fowler's post regarding fluent interface, I try to write small trivial implementations to understand the concept. Here are the common interfaces of Customer, Order & OrderLine classes, as required by Martin's "makeNormal" method:

Customer
  +addOrder(Order): void

Order
  +addLine(OrderLine): void
  +setRush(boolean): void

OrderLine
  +OrderLine(int, Product)
  +setSkippable(boolean): void

It's quite standard for OO design. Looking at Martin's "makeFluent" method, we need the following interfaces, which I've added some trivial implementations to help understanding the concept:

Customer
+newOrder(): Order
{
  Order o = new Order ();
  this.addOrder (o);
  return o;
}

Order
+with(qty, productId): OrderLine
{
  Product p = Product.find (productId);
  OrderLine line = new OrderLine (qty, p);
  this.addLine (line);
  line.setOrder (this);
  return line;
}
+priorityRush(): Order
{
  this.setRush (true);
  return this;
}

OrderLine
+with(qty, productId): OrderLine
  //note: return the last order added, instead of this
{
  Product p = Product.find (productId);
  OrderLine line = new OrderLine (qty, p);
  this.order.addLine (line);
  line.setOrder (this.order);
  return line;
}
+setOrder(order): void
{
  this.order = order;
}
+skippable(): OrderLine
{
  this.setSkippable (true);
  return this;
}
+priorityRush(): Order
{
  this.order.setRush (true);
  return this.order;
}

Looking at the above sample implementation codes, we need to design additional interfaces to help the client usage be more fluent. Things I've noted:
1. Fluent API can only be applicable for highly used API, otherwise the investment of designing the API may not be as effective as intended. Thus, not all API can be made fluent.
2. Fluent API may be good for "feature-envy" code smells. Feature-envy is when a "client" method calls methods on another class three or more times.
3. Fluent API is suitable for frameworks, e.g. Hibernate, libraries, e.g. Jakarta Commons, or related OO classes (as in Martin's example). But, the timing for designing Fluent API for OO classes is still a big question. If we design it too early, there may be useless API (or the API is not fluent enough). If we design it too late, the ROI may not be as expected.

The pendulum is still swinging.. :D
I hope I can think this through the week, and let you guys know what I think..

Thursday, December 15, 2005

Getting a credit card

I've always been a no-credit guy. I don't like to ask for any loan for any purpose. That's my principle since I was a kid, perhaps parts of what my dad has taught me. Now, I've created and/or implemented more than 2 automotive loan application systems (one of them is a product of my employer), and since I know exactly the formula they use for the loan installment payments, it further discourages me from taking any loan.

I have neither a car nor a house. I plan to have one of both in the next 2-3 years, and I know I may need to take a loan for them. One way is to apply for a loan in the local bank using my current employment records & employer's recommendation as part of the loan application attachments. The loan itself may be able to cover only 30% of a car's price though.

From my experience in deploying the loan application system on multiple funding companies, I know also that a good credit history is required for a large amount of loan. Even though credit card is new & highly unpopular as part of Indonesian life style, it's getting its popularity among the urbans in the recent couple of years.

Now, after looking at this very nice post about how to increase your credit score, I know that I need to get at least a credit card, and use it! :P. I did get myself a credit card 4 years ago, which I planned on using them to buy books from Amazon. Yet, it's never been the case. I paid the annual fee, but never use the credit limit even for once.

For an American whose credit card history is quite long, since the 30's I believe (a few years back I've read the credit card history in the US through some book, I forget the book's title though), it's very important to get a good credit card history for applying a larger amount of loan. But, for an Asian countryman, is it already accustomed to have it?

Is it true that a decent hard-working employee can never pay himself a good house/car in cash? I find it's rather hard to accept, yet easier to believe.

Tuesday, December 13, 2005

A better work in the next 5 years?

Since highschool, I always believe that I need to keep on learning in order to stay alive. The same with my line of work. I'm trying to keep myself up-to-date with what might be a valid source of income for the next years to come. I've been trying to keep myself to think that I need to be able to find myself a good, if not better, work in the next 5 years.

My current choice is a Bachelor Degree in Computer Science & Java. I know I need to upgrade myself with a Master Degree, either in Management or Business Adm., and I need to learn Ruby (from the look of it). And it must happen within the next 1-2 years.

Reading Joel's list of books that he requires an MBA candidate to learn, I notice that not a single book from that list I've read. It's been more than a year since my last intention to read one Java-related book, from cover to cover, in two months. But, it's never been a successful story.

Now, Joel is expecting an MBA candidate to read one book every two weeks at a constant pace, for the next three years. It's just incredible. The pace that every persons in this world are running is just incredible. I'm running all the time, and yet it seems the finish line is running faster than I am.

I wonder if anyone feels the same as I do..

Monday, December 12, 2005

Today's Reading - 12 Dec 05

Iranian blogger is rejected to visit the US due to his blog contents.
His lesson teaches us to be careful in saying our political views, to be careful NOT to bring up unnecessary information about our personality when we're crossing any border, and also to think again whether being searchable by Google is a good thing or not.

Erik Thauvin is receiving e-mails that says he has Java Trademark Infringement on his blog site.
I'm not too sure I understand what this means, I sure hope it's just part of the Sun Partner program, but the issues of trademarks & copyrights are not easy ones and they tend to limit people's freedom rather than to protect their own rights. Some folks in Indonesia even joke at the possibility of having Indonesian government pay Sun Microsystems because we have an island named Java :). By naming my blog as "java developer in java", I think I may just made myself a good candidate for the Java Trademark Infringement. We'll see.. :)

A good summary of the machine vs. humane interface
I've read the initial post by Martin, and read Cedric's comments. I believe that for an SDK release to have too many humane interface is not such a good thing. It may be making too many assumptions on different people's intuitivity. A good SDK release should only include things that 90% of its own developers agree that they'll use them. Convenient methods with more than 3 lines may be okay, but shorter methods may be unappropriate. A language change, as what we had with Java 5, which reduces the boilerplate codes and introduces generics & annotations, is better than having too many humane interface for an SDK release. Maybe a good research or survey on java.lang classes and utility classes created to help the usage of java.lang classes are possible objective ways to solve this debate.

Tuesday, May 17, 2005

recursive directory search

A friend of mine just asked me how to write a Java code that will perform a search on a directory and all of its subdirectories (recursively) matching a given file suffix, for example, all JSP files. I remembered that I was doing a small project a few months ago that had to deal with this functionality. And, I also remembered that it was a simple thing to do.

So I went to the jakarta-commons site, looking for the commons-io component. After looking at the javadoc for the next few seconds, suddenly it's all there. The solution is all there. Just one line of code, and you're done.

Collection jspFiles = FileUtils.listFiles
    (rootDirName, new String[] { "jsp" }, true);

I can't keep myself from the amusement caused by the great work that open source people have done. This is yet another prove that a lot of browsing could actually prove beneficial.. :D
It's nice to see the benefits of open source components. I'd certainly went and looked for jakarta-commons next time I'd ever need anything "common".. :D

Kudos to the great guys everywhere in the open source world.. :D

Further Reading:
Jakarta Commons Cookbook
Pro Jakarta Commons
Apache Jakarta Commons
Applied Software Engineering using Apache Jakarta Commons

Thursday, April 28, 2005

String bug

Apparently, there is a bug in the Sun JDK 1.4.2 or lower implementation of java.lang.String. This bug has been identified as 4310930, 4546734, 4637640, 4724129 in Sun's Bug Database. I've come across this bug while reading Charles Miller's post, when he was having trouble with the OutOfMemoryError in his JDBC code.

Please download this source code to see what I'm talking about. Try to run the source code, and continue until you reach the OutOfMemoryError state, as seen in my own trial here. Then, change the source code to the working alternatives, and test them out (see my result here).

This bug has been fixed in JDK 5.0 release, but I believe most of us are still working with Sun JDK 1.4.2 or lower. So I guess this is a problem that we must be very careful with.

Thursday, April 14, 2005

Petals around the Rose Puzzle

A friend of mine send me a link to the Petals around the Rose Puzzle. It's essentially a simulation of rolling 5 dice at the same time, then guess what is the number of Petals around the Rose. It's quite fun actually to guess them. I've had 16 tries (approx. 15 mins) before finding my solution to the problem. The solution has been proven within the next 30 successfull tries.

Try them out for yourself, and let me know how you did it.

Here's my solution in Spanish: (use Google Translator to translate them into English)
No haga caso de todo, a excepción de 5 y de 3. Cada 5 cuentas para 4 pétalos. Cada 3 cuentas para 2 pétalos. Resuma el número de pétalos.

Another friend of mine come up with a smarter (more visual) solution, also within 10-15 minutes time. Here's his solution in Spanish:
La rosa es el punto en el centro de el corto en cubos. Solamente 1/3/5 tiene ese punto. Por lo tanto, 2/4/6 no es se levantó. El pétalo es los otros puntos que rodean color de rosa. Así, solamente 3/5 rosa tiene 2/4 de los pétalos.

Let me know your solution.. :D
It's just a fun way to start the day.. :D

Monday, April 11, 2005

Immutable classes

Here's my opinion on immutable classes, answering Bayu's comment which asks for an explanation on when and how to implement them.

Making a Java class immutable usually make the overall design of a system slightly better compared to making all Java classes mutable. Whenever possible, making such class immutable will make it easier to be implemented and maintained.

First, don't provide any mutator methods, i.e. methods which are able to modify the internal of the class. If you have to provide a mutator method, make sure that it's thread-safe.

Then, make all fields private. If you need to expose a constant value through public static final field, make sure that the internal of the constant value cannot be modified. Otherwise, you'll only open a hidden vulnerability. For example, an array or list constant value's elements may be modified without changing the reference of the array or list itself.

Sometimes, making an immutable class is a pleasure by itself. It's almost very easy to test, sometimes it needs no testing because it just cannot possibly break. Then, it's nice also to create constant values which are instances of this immutable class, just like constant values for Boolean.TRUE, Boolean.FALSE, etc.

There are many great examples of immutable class usages. Even the immutability itself can be raised to multiple levels, providing event tighter access on each level, e.g. no methods can be overridden, etc. I suggest you to read Effective Java book by Joshua Bloch. The book discusses immutability in great length.

I hope this short introduction to immutability may lure many future design to favor it whenever possible.. :D

Monday, April 04, 2005

VB6 no longer supported

Since last Friday, 01-Apr-2005, Microsoft no longer supports VB6. Instead, VB6 will only be supported for another 3 years in a period called extended phase. After 01-Apr-2008, VB6 will no longer be supported. During extended phase, all supports & updates will be charged. All online supports will be terminated during the extended phase. And, no new license can be bought for VB6.

For a complete story, read here:
http://msdn.microsoft.com/vbasic/support/vb6.aspx

For anyone using VB6 to deploy applications for their customers, they must now follow the new rule from Microsoft, i.e. VB.NET Downgrade Policy. This policy is given by Microsoft only on a case-by-case basis (except if it's already included in your EULA). After receiving Microsoft's approval, we must then buy the license for VB.NET, before being able to use VB6 through the Downgrade Policy.

For a complete story, read here:
http://www.microsoft.com/permission/copyrgt/cop-soft.htm#Downgrades

Following is the list of supports for several Microsoft products. Listed are the end date of their mainstream support, prior entering the extended phase like what VB6 did last Friday:
.NET SDK 30-Jun-07
.NET Framework 1.1 30-Sep-08
VB.NET 2003 30-Sep-08
VB6 EE 31-Mar-05
VBA6 31-Dec-08
C#.NET 2003 SE 30-Sep-08
C++ 6.0 EE 30-Sep-05
VSS 6.0 SE 30-Jun-07
VS.NET 2003 EE 30-Sep-08
Web Services 2.0 30-Sep-08

For a complete list, read here:
http://support.microsoft.com/gp/lifedevtoolfam

Because of this EOL issues, several MVP from Microsoft signed an open petition to ask Microsoft to support unmanaged VB (VB6) and VBA. Currently there are already 4739 signatures, including 238 of them are from Microsoft MVP.

For a complete information on the petition, read here:
http://classicvb.org/petition/

Following are some technical issues regarding the migration from VB6 to VB.NET:
1. VB6 code cannot be recompiled using VB.NET compiler
2. Migration Wizard offered by Microsoft for this purpose cannot give a complete working solution, and instead leave a lot of TODO for the developer scattered throughout the migrated code.
3. Since 1975 up to 2001, all code from MS BASIC up to VB6 are still forward compatible (even though they're not necessarily backward compatible). But this history (tradition) has been broken by the migration from VB6 to VB.NET which is not compatible (both forward & backward)
4. The changes frmo VB6 to VB.NET are very fundamental since they change the language of VB itself.

Following are more links regarding the migration issues:
http://classicvb.org/petition/faq.asp
http://www.devx.com/vb/article/16822
http://vb.mvps.org/vfred/Trust.asp

Thursday, March 31, 2005

List Pagination (Continued)

To answer Josh's comment on why the implementation of list pagination is quite long, here's a brief background. This post may be beneficial to Java/OOP newbie, in terms of thinking in an OOP and working in a TDD (Test Driven Development) way.

It's all started with the modeling of what a Page object may look like. Well, it has to have a page number and the contents (list of records) itself. Then, we need to define what can we do with a Paginating List. We need to be able to get the first page, the last page, the previous & next page based on the current Page object. They're all described within the Paginating interface.

Then, I added two more information inside the Page class, which can be provided easily by any implementation class of Paginating interface, i.e. the total number of page and the total number of records shown per page. The addition of this two properties to the Page class proves to be a very beneficial to the PaginatingImpl class itself.

Now, before we dive into why the PaginatingImpl looks like it is, one thing needs to be restated here. The PaginatingImpl is just one example of how we can implement the Paginating interface. You can provide your own (which can be a very different) implementation. The only requirement for the implementation is only to support all of the methods declared in the Paginating interface.

When developing the PaginatingImpl, one thing I keep in mind is that I wanted this class to be immutable and thread-safe. By being an immutable class, the PaginatingImpl itself almost automatically becomes thread-safe and therefore sharable & cache-able. Hence I say that this solution can be used for a web application or a non-web application.

The implementation code within the PaginatingImpl itself is straightforward and is mainly driven by the test code. If you haven't download the test class, then I suggest you download them here first. This is the key driver which drives the PaginatingImpl & Page classes to evolve into what they are currently.

For example, to ease the assertion codes within the PaginatingTest class, I added the equals() and hashCode() methods in the Page class. Then, after performing several test cases, I remembered that the ArrayList implementation does not override the equals() and hashCode() method from Object class. Therefore, I may get an inconsistent (and invalid) result when comparing two List objects which contain exactly the same sequence of the same elements.

Hence I added two more methods, i.e. isListEqual() and listHashCode() which are merely my implementation of the equals() and hashCode() method for the List class. (Note: BTW, to all the Java newbies reading this post, if you don't know why equals() and hashCode() need to be overridden correctly at the same time, please leave me a comment, I'll try to post the explanation for them. Or you can Google them or read in your own javadoc API or search it in Javaranch's SCJP Forum.)

The toString() method was added in the Page class merely to support easy debugging which I used exactly only once before sharing this code to you all. So the method definitely has its uses.

If you read carefully enough within the PaginatingImpl, you may find strange arithmetic operation, e.g. minus 1, or minus 2, or plus 1. Well, those are small logic but they're crucial to prevent bugs occuring from this rather small implementation of List Pagination.
If you want to test yourself, just take three of my classes, Page, Paginating & PaginatingTest, then develop your own PaginatingImpl class. It's a good challenge, and you may just develop a better implementation than mine, and enrich the PaginatingTest with many more test cases.

I sure would love to hear if anyone would want to take the challenge. It's a good start to all three elements of today's software development, i.e. Java, OOP & TDD. You need to download JUnit libraries though, but it should be no big deal.

Designing just another solution maybe is an easy task, but designing a good, robust & extensible one maybe is not such an easy task.. :D

Anyway, let me know what you guys think.
I sure hope that this post is even more useful when compared to my previous one.

Further Reading:
Test Driven Development by Example
Head First Design Patterns
Design Patterns
JUnit in Action
JUnit Recipes