Into Java - Part XVII

By Simon Grönlund

Into Java, Part 17
Last time we added the ability to save your paintings to a file. We used the very convenient object stream facility that Java provides. Simply letting a class implement the Serializable interface made the Java object output stream able to store that class in Java's own file format. Afterwards the object input stream instantiates every object back again as if nothing ever happened. We will return to these handy streams in a future column.

Today we will continue with our example application, the PaintBox, and add the ability to save our paintings to a file name we choose ourselves. Of course we will be able to open any saved file as well. Using a file chooser found in the Swing package this will be easy. Nevertheless, a few words on the way never hurt.

As you must have seen quite a few times accessing files through a file chooser, you might run into the problem of overwriting a file you did not intend to overwrite. Or you may have run into another error, a small one perhaps but still an error. In this case, small dialog boxes show up, how do we do that in Java? At first it looks tricky, but you will find yourself comfortably using JOptionPane within a few moments.

Since there will be quite a few things to add to the previous code I sincerely hope that you have read the former [IntoJava] column, written the code, got it compiled, and got it running. Remember that the last column included links to a couple of Java files, but you had to add the code explained in that column yourself. Further, this time I will divide the new code into smaller chunks and comment these parts. Please, locate the lines we discuss, I will include two lines from the old code (most of the time), one as the topmost line and one as the last line showed.

JFileChooser
The JFileChooser from Swing is pretty straightforward. Anyone familiar with the Windows user interface will recognize it immediately, whether good or bad. But as with everything done in Java, you are free to change the look and feel and of course most other details you might like to. But admittedly, this time I think that doing that will be tricky and way out of the scope of this column.

Nevertheless, you do not need to construct anything at all yourself, it comes with the box. But how do we use it? And how do we interact with it?

Let us start with the declaration of JFileChooser. It will be located in the PaintPanel class since it is there we chose to have our file opening and file saving methods. Since we have already imported javax.swing.* to the class we only need declare a JFileChooser. At the same time it seems convenient to declare a variable for the file we are currently working with, so we will use the class File that we have seen before. I chose to name them openedFile</tt> and fCho</tt>.



The JFileChooser</tt> is still not instantiated, and we do not do that in the constructor either. Why? It is a matter of using your own judgment. Sometimes we do not need a file chooser, why bother Java with creating such an instance then? This time we know we certainly will use one, but we can still wait a while, and thus save some application loading time. This is very polite programming. Do not instantiate things at once as you save time and the user finds your application quicker. On the other hand they will be waiting for these objects later on, but we will see that this is perhaps better. You decide.

openedFile
Since we introduced the variable openedFile</tt>, let us make a little detour to some changes we need to do with the method saveFile</tt> that we wrote in the last column. It still works, but let us use the currently used file name. Later we will be able to change that name. (For a while now, we will not be able to save anything with PaintBox since there is no file name given yet.)



We do a test to make sure that no file name is yet chosen by the user. Starting from scratch you must use the "Save as..." alternative rather than "Save", right? Naturally, from here we could have chosen to jump to the method that will take care of that, but we still aren't in trouble yet.

But if there is a currently used file and anyone picks the menu item "Save" we will continue as before, with only one difference, we instantiate a FileOutputStream</tt> with openedFile</tt> as argument. (The menu is located in the PaintBox class, where we have yet not added the "Save as..." menu item.)

Like saveFile</tt> we have to make the same change to readFile</tt>, but there is no need to check for a sane openedFile</tt> variable since we will never come to readFile</tt> unless there is one. We will soon see to that.



FileInputStream</tt> is now constructed with openedFile</tt> as argument, the remainder is still untouched. Expect more changes.

Back again
Consider you have started PaintBox, perhaps you want to open that artistic painting you made last time. You want to open a file, in other words. We had the menu item "Open" last time and it still works without changes. You may rename the menu item to "Open..." if you like.

Clicking "Open..." you would like to see the file chooser open before your eyes. Let us do the implementation.



Recall that we did not instantiate the file chooser from the start, but now we have to. Since it is null we are sent to initFChooser</tt>, another new method. Otherwise we would continue with the last line, but let us wait on that for a moment.



This method will only be called once, the first time anyone uses the file chooser. And this method will only be called from inside this class, hence it will be private</tt>. From now on the file chooser will always be present, though most of the time it will be invisible and only brought to visibility upon requests from the user. It is also used for both open and save actions, differing only in how you call it when you need it.

We simply instantiate a new JFileChooser</tt>. That will take a while for the Java Virtual Machine, though it is much quicker when you use Java 1.3. In the future, any call to it will be handled without delay. This time we set a current directory to the directory we are executing the application from, as we construct a <tt>File</tt> object with a single dot. But of course, you can use any directory of your choice, just create another path with the <tt>File</tt> object.

The last thing we do is to set a file filter. Since we are not interested in files not recognized by PaintBox, which is too simple to show GIF or JPEG and such stuff, we would like a filter. This is the way. But hey, how is <tt>PaintFileFilter</tt>, the filter object used?

PaintFileFilter
A file filter seems difficult to make. But it is not, at least not a basic one as ours is. <tt>PaintFileFilter</tt> is a class, but since we will only use it within the scope of <tt>PaintPanel</tt> we can simply add it to the <tt>PaintPanel.java</tt> file, as an add-on after the <tt>PaintPanel</tt> class.



This class extends an abstract class located deeper in the Swing package. The <tt>FileFilter</tt> class has two methods that we need to implement.

We start with the <tt>accept</tt> method that returns a <tt>boolean</tt>. It is simply two clauses that may return <tt>true</tt> or <tt>false</tt>. Both clauses make calls to methods of other Java classes.

The first clause asks the <tt>File</tt> object argument for its name, a <tt>String</tt> that is changed to lower case and finally tested against our own new file format "pbo" (PaintBox). If the file tried does end in ".pbo" the <tt>String</tt> class will return <tt>true</tt>, else <tt>false</tt>.

The second clause simply asks the <tt>File</tt> object if it is a directory, every directory should of course show up in any decent file chooser.

If any one of these two clauses gives a <tt>true</tt>, a <tt>true</tt> is returned.

The second method is used to set the description at the "Files of type" at the bottom of the file chooser. See the [file chooser image].

This is a most basic file filter class, but it serves its purpose, we have only one file type, and it instructs us how to do it. There are several pages on the Internet on how to make more complex filters.

Now, how is our filter used? Our file chooser, for each file and directory, simply asks this filter if the object at hand is of the correct type, otherwise it will not show up on the pane. And the file chooser sets the file type.

When this is done we have a file chooser set up that is ready to use.

Back again 2
With the file chooser instantiated we can ask for it to open itself. Let us do that, the first line of the image below asks our file chooser object to show an open file dialog. The argument to that method may be any kind of <tt>Component</tt> of the <tt>java.awt</tt> package. It is used to locate the file chooser near the place you called for it. We use <tt>this</tt> as parameter, that is a reference to the <tt>PaintPanel</tt> object we are using. Maybe there are other uses for it as well, I have not dug that deep into this topic.

The file chooser opens and we can highlight a file and click "Open". The file chooser turns invisible and we are ready to see the outcome, how do we do that?



We notice that we get an <tt>int</tt> back as a result. Now let us look at the Java API, at <tt>JFileChooser</tt>, and we will find quite a few constant variables of the <tt>int</tt> type. We are interested in the <tt>APPROVE_OPTION</tt>. If you clicked OK, or double clicked a file, you approved the file selection and this constant is returned. We are not interested in any other possibility in our application.

Next we test if the return value is okay and if so, then we continue. If anything else is returned we have no backup, we simply leave this method and have to start over. Our file chooser was turned invisible anyway. Please note that the <tt>if</tt>-block induces one extra closing brace at the end of this method, the <tt>readFile</tt> method.

Inside the <tt>if</tt>-block we get the selected file from the now invisible file chooser. Now we see how convenient it is using the <tt>openedFile</tt> variable of the <tt>File</tt> type; it will be preserved until the next time you open a file or save the file under a new name. It contains both the file name and in fact, the entire path if you searched for the new file in another directory, partition or even file system, and it is quietly used by <tt>saveFile</tt> any time you click "Save". Still <tt>File</tt> only holds a handle to the file and we have to make a file reader around it, as before.

Now we are done with the <tt>readFile</tt> method, as well as <tt>saveFile</tt>. So you may certainly compile here and at least open any file of the "pbo" type you find. I think you can rename the old <tt>paint.dat</tt> and open it to try all this.

saveAsFile
This method, also located in <tt>PaintPanel</tt> will be today's most-to-do implementation. And there will be some diversions from the plain file chooser topic on the tour. But let us start. As with <tt>readFile</tt>, the file chooser may not be instantiated. If you started to paint from scratch there is no file saved yet, thus a call is made to the <tt>initFChooser</tt> method.



After we are certain we really have a file chooser we declare a temporary <tt>File</tt> variable, why? Let us wait on the answer for a moment, but ponder over what will happen if you somewhat later cancel your actions. A temporary holder never hurts. The next variable, the <tt>boolean doSave</tt>, comes from similar reasoning. Initially no file is allowed to be saved, right? It will in a moment be much clearer how to use both these variables.

An observation I have made is that some programmers try really hard to nest their flow statements, the if-else blocks, to avoid extra variables. At the other extreme we find programmers that spread variables as seed, but they do not harvest anything good. Moderate use of variables, especially boolean variables, only makes the application easier to implement, to read, and to improve upon later on. Too heavily nested flow statements are hard to follow and amend when needed. Also such programming is more often prone to errors.

As with the open file dialog we call a similar method, <tt>showSaveDialog</tt>, that in most aspects works the same way and looks the same way. Also this method returns a result held in an <tt>int</tt>, a static constant of the <tt>JFileChooser</tt> class.



Continuing with that result, hopefully an <tt>APPROVE_OPTION</tt> constant, we ask the file chooser for the selected file. That may be a new file, if you entered a new file name, or a file you selected from the pane. Now check yourself; consider if you by mistake wrote a file name of an existing file you did not notice, wouldn't it be nice having a dialog pane warn you? Most people think so.

Using a file object of the <tt>File</tt> class, that is a simple task, merely ask the object if such a file <tt> exists</tt>, it will say <tt>true</tt> or <tt>false</tt>. If such a file already exists then we dive into a new <tt>if</tt>-block.



Here we will make use of a convenience class in the Swing package, <tt>JOptionPane</tt> is capable of dozens of different appearances. We are using the "confirm dialog", a dialog that most of the time has two or three buttons. I will set aside the next IntoJava to make a small app that can show the different styles of <tt>JOptionPane</tt> and also some other GUI tools. However, there are a zoo of options to use and you really have to make a journey with the <tt>javax.swing</tt> package and try to use the different things.

I understand that the next few lines are confusing and will explain them here: The other way to use dialog panes in Java is to use the JDialog class and make the entire layout, the complete implementation and so forth, yourself. You do not like that idea, do you? As easy-going as I am, the handy <tt>JOptionPane</tt> really looks nice. Simply send the proper parameters and there you are.
 * <tt>this</tt> is a reference to the parent, int, in this case a reference to the <tt>PaintPanel</tt> object. This reference can be any object of the <tt>Component</tt> type.
 * A <tt>String</tt> that contains the message. Notice that it is possible to use the new-line character <tt>\n</tt>. Please observe that the Java API asks for an <tt>Object</tt>, that means a <tt>String</tt>, an icon, a <tt>Component</tt> or an array of such objects.
 * The title, always a <tt>String</tt>.
 * The type of message, we chose a <tt>WARNING_MESSAGE</tt>, next month we will see four more.
 * The type of option, we chose a plain <tt> OK_CANCEL_OPTION</tt> that gives two buttons.
 * An icon of the <tt>Icon</tt> type, but we sent <tt>null</tt> as parameter.

When the user clicks one of the buttons, the <tt>int answer</tt> will encapsulate the result in a <tt>JOptionPane</tt> static constant. We are only interested in the <tt>OK_OPTION</tt> since any other answer says we do not want to overwrite the file. If we get an approval, we set the <tt>doSave</tt> variable to true.

Finally the outer <tt>if</tt>-block is finished, there was no file to overwrite and it seems to be okay to save the file. So far there are two ways to get an approval; there is no file to overwrite, but if there was one the user may approve the overwrite himself.

But there is still an obstacle to avoid, we have introduced our own file type and a filter for it. What will happen if we now try to save with another file type? Let us take care of that.



It is only when we have an approved <tt>doSave</tt> from the former part of the code that this test is necessary. If <tt>doSave</tt> is <tt>false</tt>, it does not matter what ending the file has, does it? So that is the first clause.

To dive into this <tt>if</tt>-block the next clause also has to indicate a possible erroneous file ending. Hence we ask the file for its name and again ask the <tt>String</tt> object that was returned from the file object if it ends with ".pbo". If so, everything is fine and we do not want to visit this block. Thus we switch a <tt>true</tt> to <tt>false</tt> with the exclamation mark. The result will be that the <tt>false</tt> that is returned from all other file endings is switched into a <tt>true</tt>, and both clauses are true. Voila!

Now we know we have another file ending, but that is still not an error, only a possible error. So, we must ask the user what his intention is. Another dialog pane is used, in every essential part it looks the same, only the message and title differ. Recall that <tt>doSave</tt> at this point is <tt>true</tt>, but any <tt>answer</tt> different from <tt>OK_OPTION</tt> will set <tt>doSave</tt> to <tt>false</tt>.

So far the outcome may be a <tt>doSave</tt> set to <tt>true</tt> or <tt>false</tt>, meaning to save the file, or not to save.



If the result so far is to save the file, then we simply will replace the old <tt>openedFile</tt> with the new file name. And now we clearly see why it is good to have such a temporary variable, if we any time change our mind and we do not want to erase the old <tt>openedFile</tt>. Now, why not let <tt>saveFile</tt> take care of the rest, avoiding duplicating that code?

On the other hand, what happens if <tt>doSave</tt> is set to <tt>false</tt>? Remember that we entered the outermost <tt>if</tt>-block because we approved a file in the file chooser. Probably that indicates that we would like to save a file anyway, but perhaps we made a mistake or did not notice a file we do not want to overwrite. The only natural thing to do is to give the user another chance with the file chooser, a call back to this very method will do. Note, the outermost <tt>if</tt>-block does not have an else-statement, a cancel in the file chooser cancels the entire method.

Such a thing as a call-back to the method we are in is somewhat dangerous. If you remember how recursion works, you know that the thread first saves away the context of this method. Then the thread takes another trip into the method, but is not aware of the former tour. And when finished the thread jumps back to the very spot it made the recursive call from, that is the <tt>saveAsFile</tt> statement in the code above. What will happen if there are more statements to execute in this first tour in the <tt>saveAsFile</tt> method?

The best way to avoid such dangers is to make sure there is nothing more to do after such a recursive call. That is, put these kind of recursive call as the last statement, then nothing more is done after the return from the call.

Now we are done with this new method. It contains the necessary calls to the file chooser and the dialogs helping the user around.

Finally, A New Menu for PaintBox
How can we get to the "Save as..." method? We have to add a new menu item to the "File" menu.



Immediately after the "Save" menu item we simply add this "Save as..." menu item. As with the other menu items, we include an action listener that calls a method, in this case the <tt>saveAsFile</tt> method. I have also changed the other item titles to "Save" and "Open..." though that isn't shown here.



Now we are finished with the entire PaintBox example application. Though it maybe is not the most fancy paint application, it serves as an example of how to build applications in Java. Naturally there are other ways to make exactly the same outcome, but this way was useful with its evolutionary development within the IntoJava instalments, spanning over a long period.

In Sweden we are facing the spring and hope to experience a lovely summer, so I give you this naive painting as the closing gift from the PaintBox era.

Summary
Over time this cute little application grew into something that is more capable than we first expected. Today we have learned how to use the <tt>JFileChooser</tt>, a handy tool that comes with the box. There are a few things I did not mention about it; it uses the language you have set your system for, it is capable of opening files in whatever file system your operating system supports, the file filters can be really complex and filter for almost anything, and you can of course create new folders from it.

We have also looked into the <tt>JOptionPane</tt> and used only one of its appearances. Multiplying every option we have we get 240 combinations, but not all of them are meaningful to us. Still, there are several combinations to pick from and we only need to give the class a different set of options to get them. I love this one, especially considering the amount of work needed to do the dialog myself from scratch.

Finally we briefly went through how to take care of different situations that may arise when we try to make an application more powerful. An estimate would say that every powerful feature you add will cause you twice as many situations to consider; what happens if this is so and that is that?, or if that is so?, and so on. Try to make it simple, your application will be less error prone and will work snappier.

Next month we will look into some more of the GUI stuff hidden in Swing. But do not think I will have the space to look into all of it, quite the contrary. The best way to get the hang of it is to play around and try and cry.

A little hint if you would like to extend PaintBox further, look into the <tt>Polygon</tt> class found in the <tt>java.awt</tt> package. With that class you may make a lot better paintings, clicking around and drawing free hand. It will be tricky, getting the mouse clicks, adding these to the polygon and getting it painted with the help of the <tt>drawPolygon</tt> and <tt>fillPolygon in java.awt.Graphics</tt>. How do you know when you are clicking around and when you are finished? What happens if you cross the lines, is that shape correctly filled later? Tricky, but in the end it will be worth the struggle.

Finally, do you have any ideas for a little app we can play with - I am soon going to discuss threads and networking with Java - please, let me know.

Have a nice month until we see each other again.