|
Our Sketcher program can only be considered to be a practical application if we can save sketches in a file and retrieve them later – in other words we need to implement serialization for a SketchModel object and use that to make the File menu work. Ideally, we want to be able to write the model for a sketch to a file and be able to read it back at a later date and reconstruct exactly the same model object. This naturally leads us to choose serialization as the way to do this, because the primary purpose of serialization is the accurate storage and retrieval of objects.
We've seen how to serialize objects, back in Chapter 11. All we need to do to serialize a sketch document in our Sketcher program is to apply what we learned there. Of course, there are quite a few classes involved in a sketch document but it will be remarkably easy, considering the potential complexity of a sketch – I promise!
Of course, saving a sketch on disk and reading it back from a file will be significantly more work than implementing serialization for the document. The logic of opening and saving files so as not to lose anything accidentally can get rather convoluted. Before we get into that, there is a more fundamental point we should address – our sketch doesn't have a name. We should at least make provision for assigning a file name to a sketch, and maybe display the name in the title bar of the application window.
Since the sketch is going to have a name, because we intend to store it somewhere, let's define a default directory to hold sketches. Add the following lines to the end of the Constants interface (Constants.java):
File DEFAULT_DIRECTORY = new File("C:/Sketches"); String DEFAULT_FILENAME = "Sketch.ske";
If you want to store your sketches in a different directory you can set the definition of DEFAULT_DIRECTORY to suit your needs. The file extension, .ske, to identify sketches is also arbitrary. You can change this if you would prefer to use a different extension. Since we reference the File class here, we must add an import statement to the Constants source file to get at it:
import java.io.File;
We can now add the following data members to the SketchFrame class definition:
private String frameTitle; // Frame title private String filename = DEFAULT_FILENAME; // Current model file name private File modelFile; // File for the current sketch
The frameTitle member specifies the basic title for the Sketcher application window. We will append the file name for the sketch to it. The modelFile member will hold a reference to the File object identifying the file containing the current sketch, once the sketch has been saved. At this point you can update the import statements for the SketchFrame class to import all the classes from the java.io package as we will be using quite a few more of them in this chapter:
import java.io.*;
We can arrange for the frameTitle to be initialized and the default file name to be appended to the basic window title in the SketchFrame constructor. We can also make sure that DEFAULT_DIRECTORY exists and is valid. The following code will do this:
public SketchFrame(String title, Sketcher theApp) { //setTitle(title); // comment out this line // Code as before... frameTitle = title + ": "; setTitle(frameTitle + filename); if(!DEFAULT_DIRECTORY.exists()) if(!DEFAULT_DIRECTORY.mkdirs()) JOptionPane.showMessageDialog(this, "Error creating default directory", "Directory Creation Error", JOptionPane.ERROR_MESSAGE); }
Since we will be implementing the event handling for the File menu, you can remove or comment out the statements from the constructor that disable the actions for this:
// Disable actions //saveAction.setEnabled(false); //closeAction.setEnabled(false); //printAction.setEnabled(false);
If you recompile Sketcher and run it, you should now see the default file name for a sketch displayed in the title bar.
We now have a name assigned to the document, but there's another point to consider if we're preparing to store a sketch. When we close the application, we should have a means of checking whether the document needs to be saved. Otherwise it will be all too easy to close Sketcher and lose the brilliant sketch that we have just spent three hours crafting. Checking whether the sketch needs to be saved isn't difficult. We just need to record the fact that the document has changed.
To provide the means of recording whether a sketch has been changed or not we can add a boolean data member to the SketchFrame class that we will set to true when the SketchModel object changes, and false when it is unchanged – as is the case when it has just been loaded or saved in a file. Add the following data member definition to the class:
private boolean sketchChanged = false; // Model changed flag
This is sometimes referred to as the 'dirty' flag for the model, because it records when something has been done to sully the pristine state of the model data. The flag is false by default because the sketch is empty and therefore unchanged by definition. Any change that the user makes to the model should result in the flag being set to true, and when the model is written to a file, the flag should be reset to false. This will avoid unnecessary save operations while the sketch in memory remains unchanged.
We already have in place the means to signal changes to a sketch, since the SketchModel class has Observable as a base class. An Observable object can automatically notify any registered Observer objects when a change takes place. All we need to do is to make the SketchFrame class implement the Observer interface, and register the application window as an observer of the sketch object.
public class SketchFrame extends JFrame implements Constants, ActionListener, Observer { // Method called by SketchModel object when it changes public void update(Observable o, Object obj) { sketchChanged = true; } // Rest of the class as before... }
The Observer interface and the Observable class are defined in the java.util package, so we must import them into the SketchFrame.java file with the statements:
import java.util.Observer; import java.util.Observable;
We can register the application window as an observer for the SketchModel object by adding one statement to the init() method in the Sketcher class:
sketch.addObserver(window); // Register window as observer
The window field in the Sketcher object stores a reference to the application window. Whenever an element is added to the sketch, or deleted from it, the application window object will be notified. We can now press ahead with serializing the document.