Java Programming, Lecture Notes # 832, Revised 6/13/99.
Students in Prof. Baldwin's Advanced Java Programming classes at ACC will be responsible for knowing and understanding all of the material in this lesson beginning with the summer semester of 1999.
This lesson was originally written on June 13, 1999 and has been updated since then.
A previous lesson showed you how to use SAX to convert an XML file in a specific format to a set of Java objects stored in a Java Vector. The lesson also showed you how to convert the set of objects stored in the Vector to an XML file.
The earlier lesson pointed out that the objects in the vector could be modified with the net result being that the resulting XML file would be a modified version of the original XML file.
This lesson illustrates the modification of the objects in the Vector file prior to converting them back to XML format and writing them into an output XML file.
The sample program in this lesson parses an XML file and extracts the information contained in the file. The information extracted from the XML file is used to create a set of Java objects. The Java objects are used as the input initialization data to a GUI editor.
The edited objects are converted to XML format and written into an output file. The resulting XML file is an edited version of the original XML file.
The sample program in this lesson is designed for a very specific XML file format, as described in the following section.
An examination
The XML file used with this sample program represents the rudimentary aspects of a set of examination questions. The first listing below shows a schematic of the element structure of the XML file along with the attributes of the elements. The second listing shows the actual XML file that was used to test the program, including the content of each of the elements.
<?xml version="1.0"?> <exam> <problem problemNumber="1"> <question>...</question> <answer type="multiple" numberChoices="4" valid="0,1,3"> <item>...</item> <item>...</item> <item>...</item> <item>...</item> </answer> <explanation>...</explanation> </problem> <problem problemNumber="2"> ... </problem> </exam> |
XML file structure
The XML file uses the following types of elements as highlighted above:
The exam element
The XML document contains a single exam element.
The exam element contains one or more problem elements.
The problem elements
The problem elements have an attribute that specifies the problem number associated with each problem element.
Each problem element also contains
The question element
The question element is simply that. It asks the question for a particular problem.
The answer element
Each answer element contains one or more item elements and some attribute values. The attribute values explain how different parts of the answer elements should be used to comprise the answer to the question.
The explanation element
The explanation element is simply some text that explains the answer.
Example problem element
For example, the attributes of the answer element of the case shown above (and repeated below) represent a multiple-choice question having four available choices.
<answer type="multiple" numberChoices="4" valid="0,1,3"> |
In an online testing situation, the student would select one or more of the item elements as her answer to the question. In this particular case, the student would need to select items 0, 1, and 3 in order to provide a valid answer to the question.
Credit
The idea for using an XML file to contain information for administering online examinations came from an article published by Claude Duguay. He uses this concept to illustrate the use of XML and Java Servlets to provide an online testing capability. As of 6/10/99, the article is available online at http://www.devx.com/upload/free/features/javapro/1999/04apr99/cd0499/cd0499.asp.
The XML file
A listing of the actual XML file used to test the sample program in this lesson is shown below. Note that this file contains one exam element with two problem elements. The first problem has a multiple-choice question requiring the selection of three items as a valid answer as discussed above.
The second problem is a multiple-choice question requiring the selection of a single item as the valid answer.
<?xml version="1.0"?> <exam> <problem problemNumber="1"> <question> Which of the following are grown in a vegetable garden? </question> <answer type="multiple" numberChoices="4" valid="0,1,3"> <item>Carrots</item> <item>Cabbage</item> <item>Apples</item> <item>Lettuce</item> </answer> <explanation> Carrots, cabbage, and lettuce are grown in a vegetable garden. Apples are grown in an orchard. </explanation> </problem> <problem problemNumber="2"> <question> Which one of the following requires XML entities? </question> <answer type="single" numberChoices="3" valid="1"> <item>Tom</item> <item>"<Mary & Sue>"</item> <item>Dick</item> </answer> <explanation> Left and right angle brackets, ampersands, and quotation marks must be represented in XML by entities </explanation> </problem> </exam> |
The manner in which the sample program processes this XML file is described in the following sections.
Handling parser events and errors
This program uses the IBM parser (XML4J) along with the XML file from the previous section.
Purpose of the program
The driver portion of the program is contained in the source file named Sax03.java. It illustrates the implementation of a simple XML file editor. It works in conjunction with the following files:
Complete Listings of all five files are provided near the end of the lesson. Taken together, the five files illustrate
Converting XML to Java objects
This program reads an XML file named Sax03.xml with the specific format described earlier. It creates a Java object for each record in the XML file, (where each record represents an exam problem). It stores the objects in a Vector container. Classes in the file named Sax03B.java are used for this purpose.
The XML input file can be completely empty, but it must exist. Otherwise, the program will throw a FileNotFoundException. If it is not empty, the contents must match the required XML format.
Editing the data
After generating the Vector object containing the XML records, the program invokes a GUI object editor that provides the capability to
Thus, the program can be used for editing existing XML data or for generating new XML data or both.
Converting Java objects to XML
Then the program generates an XML file containing a record for each element in the vector, and writes it out to a disk file named junk.xml. Classes in the file named Sax03C.java are used for this purpose.
This conversion is very specific and fairly brute force. A more generalized approach using the Document Object Model (DOM) will be illustrated in a subsequent lesson.
The object class
A class definition in the file named Sax03D.java is used by all of the classes that make up this program as a common class for representing an XML record as an object.
Miscellaneous comments
No particular effort was expended to make this program robust. In particular, if it encounters an XML file in the wrong format, it may throw an exception, or it may continue to run, but it probably will not work properly.
The program was tested using JDK 1.2 under Win95. It also requires the IBM XML4J (or some suitable substitute) parser.
The program was tested with the XML file named Sax03.xml listed earlier. Note that line breaks were manually inserted in that listing to force the text to fit in this format).
Comments on the GUI
No particular effort was expended to provide a pleasing layout for the GUI. The GUI simply contains TextField and TextArea objects in a FlowLayout in a Panel in the center of a Frame. In addition, the GUI contains some navigation buttons.
Changing the size of the Frame will cause the normal behavior of FlowLayout.
GUI component labels
Except for the buttons used to navigate among the objects being edited, the GUI components are not labeled. The user must know what type of information needs to be entered into each component. However, when a GUI form is presented for a new object, the text fields are initialized with the names of the elements and attributes in the original XML file. This is intended to provide clues to the user as to the intended contents of each text field.
The two numeric fields provide no clues whatsoever. The first numeric field is intended to contain the problem number. The second numeric field is intended to contain the number of items to be displayed as possible answers when the XML file is presented in a GUI for administering the exam. For example, if the problem is a True/False problem, this value should be 2.
The GUI is not user friendly
In short, the GUI is not user-friendly insofar as data entry is concerned, but it does illustrate the concepts involved. Since this lesson is intended to illustrate the concept of editing an XML file, and is not intended to illustrate the layout and design of a GUI, it will be left as an exercise for the student to improve the GUI.
GUI buttons
The GUI provides the following buttons to navigate through the objects for editing and saving:
The behavior of each button is as the name implies. Whenever one of the buttons is selected, the current contents of the GUI fields are saved, possibly overwriting an existing element in the Vector object in which the objects are saved.
In other words, the user process is,
When another object is selected, or the Save button is selected, the edited version of the current object is saved into the Vector before moving on.
Creating new objects
A new object can be created and then edited by selecting the Next button when the last object is currently displayed in the editor. Another good exercise for the student would be to upgrade the program to support the insertion of new objects.
The Save button
This button causes the current edited object to be saved into the Vector.
It also causes the entire Vector object to be converted to a new XML file named junk.xml and written to the disk. (Actually, it simply terminates the Editor thread allowing he main thread to create the XML file.)
The entire program consists of a driver file named Sax03.java and several helper files as listed earlier. I'm going to begin with the file containing the class definition used to instantiate objects to contain the XML data.
Sax03D.java
The class definition in this file provides a common object format for storage of the data from the XML file. The class is designed to contain an instance variable for each item of data stored in a single exam problem in the XML file. This class has no methods. It is simply a container for the data extracted from the XML file.
This is a very specific class designed for a very specific XML format. It is essentially a copy of the file named Sax02D from an earlier lesson. The details of the file were explained in that lesson and won't be discussed further in this lesson. It is reproduced here simply for convenience.
class Sax03D{ int problemNumber;//an attribute of <problem> String question;//the content of <question> String type;//an attribute of <answer> int numberChoices;//an attribute of <answer> String valid;//an attribute of <answer> //Each populated element in the following array contains // the content of one <item> element in the XML file // with an arbitrary limit of five such elements. String[] item = new String[5]; String explanation;//the content of <explanation> }//end Sax03D |
Sax03.java
Next I will discuss the driver file named Sax03.java by breaking it up into fragments. A complete listing is provided near the end of the lesson.
The first fragment shows the beginning of the controlling class along with the declaration of three class variables.
Virtually everything in this lesson has been discussed in detail in one or more previous lessons. Consequently, the discussion in this lesson will be brief. The purpose of this lesson is to provide you with a high-level discussion to guide you through the entire process.
If you find material in this lesson that you don't understand, go back and review the previous lessons.
class Sax03 { static Vector theExam = new Vector();//store objects here static Thread mainThread = Thread.currentThread(); static String XMLfile = "Sax03.xml";//input file name |
main()
The next fragment shows the beginning of the main() method. In this method, I spawn a new thread of type Sax03B that will parse the incoming XML file, creating an object for each problem specification in the exam, and storing a reference to each of those objects in the Vector mentioned above and referred to by theExam.
start()
Then I invoke the start() method on the thread to start it running. If the XML file is a long one, some time will pass before theExam has been populated and is ready for use.
Producer/Consumer scenario
This is a typical producer/consumer scenario for which there are several control solutions. In this case, the Sax03B thread is the producer and the main thread is the consumer.
Go to sleep
Because of the simplicity of this particular situation, I chose simply to put the main thread to sleep and let it sleep until the thread that is parsing the XML file awakens it. That thread will interrupt the main thread when it finishes parsing the XML file, which will cause the main thread to wake up and process theExam.
Thus, the main thread will sleep until the parse is completed. It will wake up when interrupted by the parser thread and will then process the data in the Vector.
If parsing is not completed during the first 100000 milliseconds, it will wake up and then go back to sleep. (However, that would be an awfully long time to complete the parse so it might be better to throw an exception under those conditions.)
public static void main (String args[]){ try{ //Launch a thread that will parse the XML file Sax03B fetchItObj = new Sax03B(XMLfile,theExam,mainThread); fetchItObj.start();//start the thread running //Sleep until parse is completed. try{ while(true){//sleep and loop until interrupted Thread.currentThread().sleep(100000); }//end while }catch(InterruptedException e){ //Wake up and invoke the editor }//end catch |
Launch the GUI editor
The next fragment shows the only real difference between this program and the program named Sax02.java that was discussed in the earlier lesson.
This fragment launches a thread that provides the capability to edit the data in the Vector. Then, as in the previous fragment, the main thread goes to sleep until the edit is complete. It wakes up when interrupted by the editor thread and writes the edited data into a new XML file named junk.xml.
If editing is not completed during the 100000-millisecond period, it just goes back to sleep for an additional 100000 milliseconds.
Editor editor = new Editor(theExam,mainThread); editor.start();//start the Editor thread running |
XML file has been converted to objects and edited
At this point, each of the exam problems in the XML file has been converted into a Java object. References to those objects have been stored in a Vector object named theExam. The objects that comprise the contents of the Vector object have been edited. Editing involves both the modification of existing objects and the creation of new objects.
Convert objects to XML
The next fragment instantiates an object of type Sax03C and invokes the writeTheFile() method on that object to convert the data stored in the Vector object to XML format and write it into an output file named junk.xml.
Sax03C xmlWriter = new Sax03C(theExam,"junk.xml"); xmlWriter.writeTheFile(); }catch(Exception e){System.out.println(e);} System.exit(0);//Kill all threads and terminate the JVM }//end main }//end class Sax03 |
That completes the main() method and also completes the class definition for Sax03.
The Editor class
The Editor class is long and tedious, but it is straightforward. Therefore, I will edit large amounts of repetitive material out of the following fragments. The entire class can be viewed in the listings provided near the end of the lesson.
The next fragment shows the beginning of the class definition along with some typical declarations of reference variables.
class Editor extends Thread{ Vector theExam;//test problem objects are stored here Thread mainThread;//save a reference to main thread here //A series of references to GUI components on the Frame. // There is a component for each field in the object, // plus some Button objects for control. TextField problemNumber = new TextField("00"); TextArea question = new TextArea(2,40); //Several TextField and TextArea reference variables |
The constructor
The constructor is also straightforward. An abbreviated listing is shown in the nest fragment. After saving some incoming parameters, it constructs the GUI by
Then it registers a series of listener objects on the buttons. Many lines of similar code were omitted from the following listing for brevity.
Editor(Vector theExam, Thread mainThread){//constructor //save incoming parameters this.theExam = theExam; this.mainThread = mainThread; //construct and display the GUI object editor panel.add(problemNumber); |
The run() method
The run() method is very straightforward. If the Vector is empty, meaning that the XML file didn't contain any data, a blank data entry form is generated by invoking the initializeForm() method that will be discussed later.
Otherwise, the GUI is populated with the first object in the Vector by invoking the populateForm() method that will be discussed later..
public void run(){ if(theExam.isEmpty{ initializeForm(); }//end if(theExam.isEmpty()) else{//Vector is not empty theElement = (Sax03D)theExam.firstElement(); populateForm(); }//end else }//end run() |
The populateForm() method
This method populates the GUI form with the current element that has been extracted from the Vector.
The code is completely straightforward. The setText() method is invoked on all of the TextField and TextArea components in the GUI to set their values to values stored in the current object being edited.
void populateForm(){ problemNumber.setText("" + theElement.problemNumber); question.setText(theElement.question); //Several setText lines omitted for brevity }//end populateForm() |
The initializeForm()
methodThis method populates the current form with the names of the fields. This is considered to be a blank GUI as discussed earlier. It is intended to be used for a new data entry form. The names of the fields are provided as clues to the user as to the intended contents of each field in the form.
void initializeForm(){ problemNumber.setText("0"); question.setText("question"); |
The updateElement() method
This method is more substantive than the previous methods, so I will break it up and discuss it in fragments.
This method updates the working element with the data in the form. Then it saves the working element into the Vector, possibly replacing an element already there.
The method begins by getting a new empty object of type Sax03D. Then it uses the getText() method to get the String contents of each of the GUI text components and assigns those values to the corresponding instance variables in the object.
Note the requirement in some cases to convert the data from a String to an int before assigning it to an instance variable in the object.
void updateElement(){ theElement = new Sax03D(); theElement.problemNumber = Integer.parseInt(problemNumber.getText()); theElement.question = question.getText(); |
Save the element in the Vector
There are two possibilities here. In one case, the element is a new object that should be appended to the end of the Vector. This is accomplished using the addElement() method of the Vector object.
In the other case, the element should replace an element already in the Vector. This is accomplished using the setElementAt() method of the Vector object.
The determination between the two cases is made on the basis of the index of the element being edited.
if(elementNumber == theExam.size()){//new element theExam.addElement(theElement);//add to end }//end if else{//replace existing element theExam.setElementAt(theElement,elementNumber); }//end else }//end updateElement() |
The ActionListener classes
The GUI has five buttons labeled as
Action listener objects are registered on each of these buttons. The listener objects registered on the first four buttons
The Save button causes the data currently in the GUI to be saved in the Vector. It also terminates the Editor thread allowing the data in the Vector to be converted to XML format and written into an output XML file.
The listener classes for the first four buttons are very similar. In addition, everything contained in those classes is discussed elsewhere in this lesson, or in the many lessons that I have written on event handling in Java. Therefore, I am going to present the first listener class here and may discuss it briefly in class for students that may need such a discussion. However, I won't provide a written discussion.
The code for the next three listener classes can be viewed in the listing near the end of the lesson.
//This is an inner class used to instantiate an action // listener object for the First button. class FirstListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI before moving to // the first element in the Vector. updateElement(); elementNumber = 0;//set element number to first if(theExam.isEmpty()){ //This will be a new element so get a blank form. initializeForm(); }//end if else{ //This will be an edit of an existing element, so // get that element and use it to populate the // GUI form. theElement = (Sax03D)theExam.elementAt(elementNumber); populateForm(); }//end else }//end actionPerformed() }//end class NextListener |
The SaveListener class
The definition for this class is shown in the next fragment. This is an inner class used to instantiate a listener object for the Save button.
The listener object for the Save button saves the data in the current GUI editor screen just the same as the other buttons. However, it doesn't access another object. Rather, it invokes the interrupt() method on the reference to the mainThread to awaken the main thread, which should be sleeping.
The code in the main thread then causes the edited contents of the Vector object to be converted to XML format and written into an output file.
class SaveListener implements ActionListener{ public void actionPerformed(ActionEvent e){ updateElement(); mainThread.interrupt(); }//end actionPerformed() }//end class SaveListener |
That ends the discussion of the class definition for the Editor class.
Sax03B.java
The classes and methods in this file are essentially the same as the classes and methods in the file named Sax02B.java. I discussed those classes and methods in detail in an earlier lesson, and won't repeat that discussion here.
A complete listing of the file is available near the end of this lesson.
Sax03C.java
The classes and methods in this file are essentially the same as the classes and methods in the file named Sax02C.java. I discussed those classes and methods in detail in an earlier lesson, and won't repeat that discussion here.
A complete listing of this file is available in the next section.
A complete listing of the programs and the XML file is contained in this section.
<?xml version="1.0"?> <exam> <problem problemNumber="1"> <question>Which of the following are grown in a vegetable garden?</question> <answer type="multiple" numberChoices="4" valid="0,1,3"> <item>Carrots</item> <item>Cabbage</item> <item>Apples</item> <item>Lettuce</item> </answer> <explanation> Carrots, cabbage, and lettuce are grown in a vegetable garden. Apples are grown in an orchard. </explanation> </problem> <problem problemNumber="2"> <question>Which one of the following requires XML entities?</question> <answer type="single" numberChoices="3" valid="1"> <item>Tom</item> <item>"<Mary & Sue>"</item> <item>Dick</item> </answer> <explanation> Left and right angle brackets, ampersands, and quotation marks must be represented in XML by entities </explanation> </problem> <problem problemNumber="3"> <question>Snow is warm, true or false?</question> <answer type="single" numberChoices="2" valid="1"> <item>True</item> <item>False</item> </answer> <explanation>Snow is cold, not warm.</explanation> </problem> </exam> |
.
/*File Sax03.java Copyright 1999, R.G.Baldwin This program illustrates the implementation of a simple XML file editor. It works in conjunction with the following files: Sax03B.java - converts XML file to objects Sax03C.java - converts objects to XML files Sax03D.java - defines class for objects Sax03.xml - sample XML input file This program reads an XML file named Sax03.xml with a specific format, creates a Java object for each record in the XML file, (where each record represents a test problem) and stores the objects in a Vector. Classes in the file named Sax03B.java are used for this purpose. The XML input file can be completely empty, but it must exist. Otherwise, the program will throw a FileNotFoundException. If it is not empty, the contents must match the required XML format. After generating the Vector object containing the XML records, the program invokes a GUI object editor that provides the capability to edit the objects already in the Vector, and to create new objects and add them to the Vector. Thus, the program can be used both for editing existing XML data and generating new XML data. Then the program generates an XML file containing a record for each element in the vector, and writes it out to a disk file named junk.xml. Classes in the file named Sax03C.java are used for this purpose. A class definition in the file named Sax03D.java is used by all of the classes that make up this program as a common class for representing an XML record as an object. No particular effort was expended to make this program robust. In particular, if it encounters an XML file in the wrong format, it may throw an exception, or it may continue to run, but it may not work properly. Also, no particular effort was made relative to the cosmetics and layout of the GUI. The GUI simply contains TextField and TextArea objects in a FlowLayout in a Panel in the center of a Frame. Changing the size of the Frame will cause the normal behavior of FlowLayout. Except for the buttons used to navigate among the objects, the GUI components are not labeled. The user must know what type of information needs to be entered into each component. However, when a GUI form is presented for a new object, the text fields are initialized with the names of the elements and attributes in the original XML file. This is intended to provide clues to the user as to the intended contents of each text field. The two numeric fields provide no clues. The first numeric field is intended to contain the problem number. The second numeric field is intended to contain the number of items to be displayed as possible answers when the XML file is presented in a GUI for administering of a test. For example, if the problem is a True/False problem, this value should be 2. In short, the GUI is not very user-friendly insofar as data entry is concerned, but it does illustrate the concepts involved. It will be left as an exercise for the student to improve the GUI. The GUI provides the following buttons to navigate through the objects for editing: First, Prev, Next, and Last. The behavior is as the name implies. Whenever one of these buttons is selected, the current contents of the GUI fields are saved, possibly overwriting an existing element in the Vector object in which the objects are saved. In other words, the process is, select an object to be edited, modify the fields in the GUI, and then select another object, or select the Save button described below. When the next object is selected, or the Save button is selected, the edited version of the current object is saved into the Vector before moving on. A Save button is also provided. This button causes the current edited object to be saved into the Vector, and then causes the entire Vector object to be converted to a new XML file named junk.xml and written to the disk. The program was tested using JDK 1.2 under Win95. It also requires the IBM XML4Java (or some suitable substitute) parser. **********************************************************/ import java.util.*; import java.awt.*; import java.awt.event.*; class Sax03 { static Vector theExam = new Vector();//store objects here static Thread mainThread = Thread.currentThread(); static String XMLfile = "Sax03.xml";//input file name public static void main (String args[]){ try{ //Launch a thread that will parse the XML file, // creating an object for each problem specification // in the file, and storing the references to those // objects in the Vector referred to by theExam. Sax03B fetchItObj = new Sax03B(XMLfile,theExam,mainThread); fetchItObj.start();//start the thread running //Sleep until parse is completed. Then wake up when // interrupted by the parser and begin the editing // process If parsing is not completed during the // first 100000 milliseconds, just go back to sleep. // (However, that would be an awfully long time to // complete the parse.) try{ while(true){//sleep and loop until interrupted Thread.currentThread().sleep(100000); }//end while }catch(InterruptedException e){ //Wake up and invoke the editor }//end catch //Launch a thread that will edit the data in the // Vector. Editor editor = new Editor(theExam,mainThread); editor.start();//start the Editor thread running //Sleep until edit is complete. Then wake up when // interrupted by the editor and write the edited // data into a new XML file named junk.xml. If // editing is not completed during the 100000 // millisecond period, just go back to sleep for // an additional 100000 milliseconds. try{ while(true){//sleep and loop until interrupted Thread.currentThread().sleep(100000); }//end while }catch(InterruptedException e){ //Wake up and save the data in the Vector as // an XML file. }//end catch //Now convert each test "problem" object referenced // in the Vector to XML format and write the XML data // into a new file on the disk named junk.xml. Sax03C xmlWriter = new Sax03C(theExam,"junk.xml"); xmlWriter.writeTheFile(); }catch(Exception e){System.out.println(e);} System.exit(0);//Kill all threads and terminate the JVM }//end main }//end class Sax03 //=======================================================// class Editor extends Thread{ Vector theExam;//test problem objects are stored here Thread mainThread;//save a reference to main thread here //A series of references to GUI components on the Frame. // There is a component for each field in the object, // plus some Button objects for control. TextField problemNumber = new TextField("00"); TextArea question = new TextArea(2,40); TextField type = new TextField("multiple"); TextField numberChoices = new TextField("0"); TextField valid = new TextField("0,0,0,0,0"); TextArea item0 = new TextArea(2,40); TextArea item1 = new TextArea(2,40); TextArea item2 = new TextArea(2,40); TextArea item3 = new TextArea(2,40); TextArea item4 = new TextArea(2,40); TextArea explanation = new TextArea(2,40); Button firstButton = new Button("First"); Button prevButton = new Button("Prev"); Button nextButton = new Button("Next"); Button lastButton = new Button("Last"); Button saveButton = new Button("Save"); Frame frame;//top level container Panel panel = new Panel();//contains all GUI components int elementNumber = 0;//object currently being edited Sax03D theElement;//reference to object being edited Editor(Vector theExam, Thread mainThread){//constructor //save incoming parameters this.theExam = theExam; this.mainThread = mainThread; //construct and display the GUI object editor panel.add(problemNumber); panel.add(question); panel.add(type); panel.add(numberChoices); panel.add(valid); panel.add(item0); panel.add(item1); panel.add(item2); panel.add(item3); panel.add(item4); panel.add(explanation); panel.add(firstButton); panel.add(prevButton); panel.add(nextButton); panel.add(lastButton); panel.add(saveButton); frame = new Frame("Copyright 1999, R.G.Baldwin"); frame.add(panel); frame.setSize(310,500); //register listeners on the buttons firstButton.addActionListener(new FirstListener()); prevButton.addActionListener(new PrevListener()); nextButton.addActionListener(new NextListener()); lastButton.addActionListener(new LastListener()); saveButton.addActionListener(new SaveListener()); //make it all visible frame.setVisible(true); }//end constructor //-----------------------------------------------------// public void run(){ if(theExam.isEmpty()){//if Vector is empty //Create a blank GUI data entry form initializeForm(); }//end if(theExam.isEmpty()) else{//Vector is not empty //populate GUI with data from first Vector element theElement = (Sax03D)theExam.firstElement(); populateForm(); }//end else }//end run() //-----------------------------------------------------// //This method populates the GUI form with the current // element that has been extracted from the Vector void populateForm(){ problemNumber.setText("" + theElement.problemNumber); question.setText(theElement.question); type.setText(theElement.type); numberChoices.setText("" + theElement.numberChoices); valid.setText(theElement.valid); item0.setText(theElement.item[0]); item1.setText(theElement.item[1]); item2.setText(theElement.item[2]); item3.setText(theElement.item[3]); item4.setText(theElement.item[4]); explanation.setText(theElement.explanation); }//end populateForm() //-----------------------------------------------------// //This method populates the current form with the names // of the fields. This is considered to be a blank GUI // as discussed above and is intended to be used for // a new form. The names of the fields are provided as // clues to the user as to the intended contents of each // field in the form. void initializeForm(){ problemNumber.setText("0"); question.setText("question"); type.setText("type"); numberChoices.setText("3"); valid.setText("valid"); item0.setText("item"); item1.setText("item"); item2.setText("item"); item3.setText("item"); item4.setText("item"); explanation.setText("explanation"); }//end intializeForm() //-----------------------------------------------------// //This method updates the working element with the data // in the form. Then it saves the working element into // the Vector, possibly replacing an element already // there. void updateElement(){ theElement = new Sax03D();//get a clean one //Populate it from the data entry screen theElement.problemNumber = Integer.parseInt(problemNumber.getText()); theElement.question = question.getText(); theElement.type = type.getText(); theElement.numberChoices = Integer.parseInt(numberChoices.getText()); theElement.valid = valid.getText(); theElement.item[0] = item0.getText(); theElement.item[1] = item1.getText(); theElement.item[2] = item2.getText(); theElement.item[3] = item3.getText(); theElement.item[4] = item4.getText(); theElement.explanation = explanation.getText(); //Either add the populated working element to the end // of the Vector, or replace one of the elements // already in the Vector, depending on the index of // the element in the Vector being edited. if(elementNumber == theExam.size()){//new element theExam.addElement(theElement);//add to end }//end if else{//replace existing element theExam.setElementAt(theElement,elementNumber); }//end else }//end updateElement() //=====================================================// //This is an inner class used to instantiate an action // listener object for the First button. class FirstListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI before moving to // the first element in the Vector. updateElement(); elementNumber = 0;//set element number to first if(theExam.isEmpty()){ //This will be a new element so get a blank form. initializeForm(); }//end if else{ //This will be an edit of an existing element, so // get that element and use it to populate the // GUI form. theElement = (Sax03D)theExam.elementAt(elementNumber); populateForm(); }//end else }//end actionPerformed() }//end class NextListener //=====================================================// //This is an inner class used to instantiate an action // listener object for the Next button. class NextListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI before moving to // the next element in the Vector. updateElement(); elementNumber += 1;//increment element number if(elementNumber == theExam.size()){ //This will be a new element to be added to the // end of the Vector so get a blank GUI form. initializeForm(); }//end if else{ //This will be an edit of an existing element, so // get that element and use it to populate the // GUI form. theElement = (Sax03D)theExam.elementAt(elementNumber); populateForm(); }//end else }//end actionPerformed() }//end class NextListener //=====================================================// //This is an inner class used to instantiate an action // listener object for the Prev button class PrevListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI before moving to // the previous element in the Vector. updateElement(); //Don't allow the element number to be less than zero if(elementNumber > 0){ elementNumber -= 1;//decrement element number }//end if else{//beep and ignore the request Toolkit.getDefaultToolkit().beep(); }//end else //This will be an edit of an existing element, so // get that element and use it to populate the // GUI form. theElement = (Sax03D)theExam.elementAt(elementNumber); populateForm(); }//end actionPerformed() }//end class PrevListener //=====================================================// //This is an inner class used to instantiate an action // listener object for the Last button. class LastListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI before moving to // the last element in the Vector. updateElement(); //Set the element number to the last element in // the Vector. elementNumber = theExam.size()-1; if(theExam.isEmpty()){ //This will be a new element so get a blank form. initializeForm(); }//end if else{ //This will be an edit of an existing element, so // get that element and use it to populate the // GUI form. theElement = (Sax03D)theExam.elementAt(elementNumber); populateForm(); }//end else }//end actionPerformed() }//end class NextListener //=====================================================// //This is an inner class used to instantiate an action // listener object for the Save button. class SaveListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //Save data from current GUI into the Vector before // causing the Vector to be saved to an XML file. updateElement(); //The main thread should be asleep. Wake it up so // that it can save the Vector into an XML file // and then terminate the program. mainThread.interrupt(); }//end actionPerformed() }//end class SaveListener //=====================================================// }//end class Editor |
.
/*File Sax03B.java Copyright 1999, R.G.Baldwin This file is essentially a copy of the file named Sax02B. This program, when used in combination with the following files: Sax03.java Sax03C.java Sax03D.java illustrates the conversion of an XML file to a set of objects, and the conversion of those objects back to a new XML file. The classes in this particular file convert the XML file to a set of objects of type Sax03D stored in a Vector Note that this is a highly specialized class designed to accommodate a very specific XML format. See Sax03.java for a fuller description. The program was tested using JDK 1.2 under Win95. **********************************************************/ import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.ParserFactory; //The purpose of this class is to parse a specified XML // file, creating an object for each problem specification // in the file, and writing the references to those // objects in a Vector passed in as a parameter named // theExam. The main thread should go to sleep to await // completion of the parse. When the parse is complete // the constructor parameter named mainThread is used // to interrupt the sleeping thread and wake it up. class Sax03B extends Thread{ String XMLfile; Vector theExam; Thread mainThread; static final String parserClass = "com.ibm.xml.parsers.SAXParser"; //-----------------------------------------------------// //Constructor Sax03B(String XMLfile,Vector theExam,Thread mainThread){ this.XMLfile = XMLfile; this.theExam = theExam; this.mainThread = mainThread; }//end constructor //-----------------------------------------------------// public void run(){ try{ Parser parser = ParserFactory.makeParser(parserClass); //Instantiate an event and error handler DocumentHandler handler = new EventHandler(theExam,mainThread); //Register the event handler and the error handler parser.setDocumentHandler(handler); parser.setErrorHandler((ErrorHandler)handler); //Parse the document to create the events causing // the objects to be created and saved in the // Vector passed as a parameter to the constructor. parser.parse(XMLfile); }catch(Exception e){System.out.println(e);} }//end run }//end class Sax03B //=======================================================// //Methods of this class are listeners for document events // and error events. class EventHandler extends HandlerBase{ Vector theExam; //store objects in this Vector Thread mainThread; //wake this thread upon completion Sax03D theDataObj; //create objects of this type boolean inQuestionElement = false; boolean inItemElement = false; boolean inExplanationElement = false; int itemNumber; //-----------------------------------------------------// //Constructor EventHandler(Vector theExam,Thread mainThread){ this.theExam = theExam; this.mainThread = mainThread; }//end constructor //-----------------------------------------------------// //Handle event at beginning of document public void startDocument(){ //Not required. Nothing to do here. }//end startDocument() //-----------------------------------------------------// //Handle event at end of document. public void endDocument(){ mainThread.interrupt();//wake up the main thread }//end endDocument() //-----------------------------------------------------// //Handle event at beginning of element. This method // identifies the type of element and takes the // appropriate action for each type. Some // identifications result in no action being taken // because no action is required for that element type. // This code was included simply to illustrate how to // take a particular action for those element types if // needed. public void startElement(String elementName, AttributeList atts) throws SAXException{ if(elementName.equals("exam")){ //Not required, nothing to do here. }//end if(elementName.equals("exam")) else if(elementName.equals("problem")){ itemNumber = 0;//initialize the item counter //instantiate a new data object theDataObj = new Sax03D(); //begin populating the object with attribute value theDataObj.problemNumber = Integer.parseInt(atts.getValue("problemNumber")); }//end if(elementName.equals("problem")) else if(elementName.equals("question")){ //set flag that identifies the type of element inQuestionElement = true; }//end if(elementName.equals("question")) else if(elementName.equals("answer")){ //populate data object with attribute values theDataObj.type = atts.getValue("type"); theDataObj.numberChoices = Integer.parseInt(atts.getValue("numberChoices")); theDataObj.valid = atts.getValue("valid"); }//end if(elementName.equals("answer") else if(elementName.equals("item")){ //set flag that identifies the type of element inItemElement = true; }//end if(elementName.equals("item")) else if(elementName.equals("explanation")){ //set flag that identifies the type of element inExplanationElement = true; }//end if(elementName.equals("explanation")) //should never reach here else throw new SAXException( "Invalid element name: " + elementName); }//end start element //-----------------------------------------------------// //Handle event at end of element. This method identifies // the type of event and takes the appropriate action // for each type. Some identifications result in no // action being taken because no action is required for // that element type. This code was included simply to // illustrate how to take a particular action for those // element types if needed. public void endElement (String elementName) throws SAXException{ if(elementName.equals("exam")){ //Not required. Nothing to do here. }//end if(elementName.equals("exam")) else if(elementName.equals("problem")){ //Store the object that was created and populated // for this element in the Vector. theExam.addElement(theDataObj); }//end if(elementName.equals("problem")) else if(elementName.equals("question")){ //Set flag showing that an element of this type // is no longer being processed inQuestionElement = false; }//end if(elementName.equals("question")) else if(elementName.equals("answer")){ //Not required. Nothing to do here. }//end if(elementName.equals("answer")) else if(elementName.equals("item")){ //Set flag showing that an element of this type // is no longer being processed inItemElement = false; //Increment the item counter itemNumber += 1; }//end if(elementName.equals("item")) else if(elementName.equals("explanation")){ //Set flag showing that an element of this type // is no longer being processed inExplanationElement = false; }//end if(elementName.equals("explanation")) //should never reach here else throw new SAXException( "Invalid element name: " + elementName); }//end endElement() //-----------------------------------------------------// //Handle events caused by encountering character data // in the XML file. Note that the character data may // arrive altogether or may arrive in chunks. // Therefore it is necessary to concatenate the chunks. public void characters(char[] ch,int start,int length){ if(inQuestionElement){//if processing question element if(theDataObj.question == null){//if first chunk //save first chunk in the data object theDataObj.question = new String(ch, start, length); }//end if(theDataObj.question == null) else{ //Not first chunk. Concatenate this chunk with // previous data in the data object. theDataObj.question += new String(ch, start, length); }//end else }//end if(inQuestionElement) else if(inItemElement){//if processing item element if(itemNumber < 5){//hard code the limit for brevity if(theDataObj.item[itemNumber] == null){ //This is first chunk. Store it in data object theDataObj.item[itemNumber] = new String(ch, start, length); }//end if(theDataObj.item[itemNumber] == null) else{//Not first chunk. Concatenate it. theDataObj.item[itemNumber] += new String(ch, start, length); }//end else }//end if(itemNumber < 5) }//end if(inItemElement) else if(inExplanationElement){ if(theDataObj.explanation == null){ //This is first chunk. Store it in data object. theDataObj.explanation = new String(ch, start, length); }//end if(theDataObj.explanation == null) else{//Not first chunk. Concatenate it theDataObj.explanation += new String(ch, start, length); }//end else }//end if(inExplanationElement) }//end characters() //-----------------------------------------------------// //That is the end of all of the normal event handlers. //Begin error handlers here. These methods are declared // in the ErrorHandler interface that is implemented by // the HandlerBase class and extended by this class. //Handle a warning public void warning(SAXParseException ex){ System.out.println("[Warning] " + getLocationString(ex)+": "+ ex.getMessage()); }//end warning() //-----------------------------------------------------// //Handle an error public void error(SAXParseException ex) { System.out.println("[Error] "+ getLocationString(ex)+": "+ ex.getMessage()); }//end error() //-----------------------------------------------------// //Handle a fatal error public void fatalError(SAXParseException ex) throws SAXException{ System.out.println("[Fatal Error] "+ getLocationString(ex)+": "+ ex.getMessage()); System.out.println("Terminating"); System.exit(1); }//end fatalError() //-----------------------------------------------------// //Private method called by error handlers to return // information regarding the point in the document where // the error was detected by the parser. private String getLocationString(SAXParseException ex){ StringBuffer str = new StringBuffer(); //Get SystemId, display it, and use it to get the // name of the file being parsed String systemId = ex.getSystemId(); if(systemId != null){ System.out.println("systemID: " + systemId); //get file name from end of systemID int index = systemId.lastIndexOf('/'); if(index != -1){ systemId = systemId.substring(index + 1); }//end if(index.. str.append(systemId); }//end if(systemID... //now get and append location information str.append(':'); str.append(ex.getLineNumber()); str.append(':'); str.append(ex.getColumnNumber()); return str.toString(); }//end getLocationString() }//end class EventHandler //=======================================================// |
.
/*File Sax03C.java Copyright 1999, R.G.Baldwin This file is essentially a copy of the file named Sax02C. This program, when used in combination with the following files: Sax03.java Sax03B.java Sax03D.java illustrates the conversion of an XML file to a set of objects, and the conversion of those objects back to a new XML file. The classes in this particular file convert the objects of type Sax03D stored in a Vector into an XML file. See Sax03.java for a fuller description. The program was tested using JDK 1.2 under Win95. **********************************************************/ import java.util.*; import java.io.*; //The purpose of this class is to provide a utility // capability to convert the data stored in the objects // in a Vector to XML format and to write that data into // a new XML file. Note that this is a highly // specialized class designed to accommodate a very // specific XML format. class Sax03C{ Vector theExam;//contains references to the objects String XMLfile;//output file name Sax03C(Vector theExam,String XMLfile){//constuctor this.theExam = theExam; this.XMLfile = XMLfile; }//end constructor //-----------------------------------------------------// void writeTheFile() throws Exception{ //Get an output file writer. PrintWriter fileOut = new PrintWriter( new FileOutputStream(XMLfile)); //Write the preliminary material to the output file. fileOut.println("<?xml version=\"1.0\"?>"); fileOut.println("<exam>"); //Loop and convert each object referenced in the vector // to XML format and write it into the output file. Enumeration theEnum = theExam.elements(); while(theEnum.hasMoreElements()){ //Get next reference to object from the Vector Sax03D theDataObj = (Sax03D)theEnum.nextElement(); //Write the contents of the instance variables in the // object to the output file in XML format. Note // that it is necessary to use the strToXML() method // to ensure that no extraneous <, >, &, or " // characters are written into the XML file. //Write the <problem> element and its attribute to // the XML file. fileOut.println("<problem problemNumber=\"" + strToXML("" + theDataObj.problemNumber) + "\">"); //Write the <question> element and its content to the // XML file. fileOut.println("<question>" + strToXML(theDataObj.question) + "</question>"); //Write the various parts of the <answer> element to // the XML file. This includes tags, attributes, // and content. fileOut.print("<answer type=\"" + strToXML(theDataObj.type) + "\" "); fileOut.print("numberChoices=\"" + strToXML("" + theDataObj.numberChoices) + "\" "); fileOut.println("valid=\"" + strToXML(theDataObj.valid) + "\">"); //The instance variable named item is actually an // array containing up to five data values. Use a // for loop to extract the data from the array and // write them into the XML file. for(int cnt = 0; cnt < theDataObj.numberChoices; cnt++){ fileOut.println("<item>" + strToXML(theDataObj.item[cnt]) + "</item>"); }//end for loop //Close out the <answer> element. fileOut.println("</answer>"); //Write the <explanation> element and its content // to the XML file. fileOut.println("<explanation>" + strToXML(theDataObj.explanation) + "</explanation>"); //Close out the <problem> element. fileOut.println("</problem>"); }//end while //Close out the <exam> element fileOut.println("</exam>"); //Close the output XML file. fileOut.close(); }//end writeTheFile() //-----------------------------------------------------// //The purpose of this method is to modify and return a // String object replacing angle brackets, ampersands, // and quotation marks with XML entities. private String strToXML(String string) { StringBuffer str = new StringBuffer(); int len = (string != null) ? string.length() : 0; for (int cnt = 0; cnt < len; cnt++) { char ch = string.charAt(cnt); switch (ch) { case '<': { str.append("<"); break; }//end case '<' case '>': { str.append(">"); break; }//end case '>' case '&': { str.append("&"); break; }//end case '&' case '"': { str.append("""); break; }//end case '"': default: { str.append(ch); }//end default }//end switch }//end for loop return str.toString(); }//end strToXML() }//end class Sax03C //=======================================================// |
.
/*File Sax03D.java Copyright 1999, R.G.Baldwin This file is essentially a copy of the file named Sax02D. This program, when used in combination with the following files: Sax03.java Sax03B.java Sax03C.java illustrates the conversion of an XML file to a set of objects, and the conversion of those objects back to a new XML file. The class in this particular file provides a common object format for storage of the data from the XML file. See Sax03.java for a fuller description. The program was tested using JDK 1.2 under Win95. **********************************************************/ //This class is designed to contain an instance variable // for each piece of data stored in a single test "problem" // in the XML file. This class has no methods. It is // simply a container for the data extracted from the // XML file. Ths is a very specific class designed for a // very specific XML format. class Sax03D{ int problemNumber;//an attribute of <problem> String question;//the content of <question> String type;//an attribute of <answer> int numberChoices;//an attribute of <answer> String valid;//an attribute of <answer> //Each populated element in the following array contains // the content of one <item> element in the XML file // with an arbitrary limit of five such elements. String[] item = new String[5]; String explanation;//the content of <explanation> }//end Sax03D //=======================================================// |
-end-