Internet Software Systems

Exercise 3

Printable version

Assessment: This exercise will count for 25% of the final mark.

The third exercise is to create an address book application that can read and manipulate data stored in a file. For this exercise, the data will be stored in XML format, and you will be using the JDOM package to access and manipulate it. Your application should be capable of adding new contacts, deleting existing contacts and modifying the details of existing contacts. The XML file format used in this AddressBook should include the same information and follow the basic format of the example below, but you are encouraged to add more interesting data and design your own interface for this project.

<contactlist nextid="2">
   <contact id="1">
	<title>my_title</title>
	<forename>my_forename</forename>
	<surname>my_surname</surname>
	<gender>Male/Female</gender>
	<address type="Home">
		<line1>address_line1</line1>
		<line2>address_line2</line2>
		<city>address_city</city>
		<postcode>postcode</postcode>
		<country>country</country>
	</address>
	<phone>
		<landline>my_landline</landline>
		<mobile>my_mobile</mobile>
	</phone>	
	<address type="Corresponding">
		<line1>address_line1</line1>
		<line2>address_line2</line2>
		<city>address_city</city>
		<postcode>postcode</postcode>
		<country>country</country>
	</address>
	<email>my_email</email>
	<photo>my_photo</photo>
	<webpage>my_personal_webpage</webpage>
   </contact>
</contactlist>

Note the attribute tags in the example code above (nextid and id). These have been added to provide unique identifiers for each of the contact elements (the id attribute is unique to each contact element, and when a new contact is added the value of its id attribute is set to the nextid value, which in turn is incremented by one). This is one way to easily identify elements which can then be modified or deleted. There are other approaches you can take, but you will need to implement some strategy for identifying elements uniquely.

For those of you who are not familiar with the Java swing classes, you can use the GUI template AddressBook.java). To use this you must write the XMLProcessor and Contact classes according to the Javadoc specification here: Contact.html, Address.html and XMLProcessor.html. Your XML document should have the same structure as the example above. For those of you who are comfortable using swing, you may find writing your own GUI a good way to refresh your skills.

Testing

To make sure all functionalities of this AddressBook work as you expected, a complete system testing plan is necessary. This should provide details on what is the purpose of each testing case, how you will carry out the testing and what is the criteria for success or failure. Testing plan and result should be submitted as an appendix.

Extension

If you are comfortable using JDOM and XPath, you may wish to make your addressbook more fully featured by implementing a search function that uses more advanced XPath expressions to select nodes that contain particular content.

Submission

The deadline for this exercise will be 12:00 noon on Friday 27/11/2009. Both a print copy and an electronic copy are necessary. Please submit a print copy of all your source code and testing report to the pigeon hole labelled with your tutors name. For the electronic copy, submit the jar file, the testing report and all the necessary source code without the imported library packages.



Hints

Importing the JDOM package

For the XML parsing to work, the JDom packages and its dependant libs (Jaxen package) will need to be added in your classpath. If you are are using Netbeans, on the project family tree, right click Library and select Add Jar..., then choose the jdom.jar and jaxen.jar files.

Binary packages for jdom and jaxen without source code could be download from here.

XPath

Although elements can be identified by manually traversing the DOM tree, there is a much more elegant solution - XPath. XPath is a technology that allows elements in an XML document to be selected according to certain criteria, and can save you a lot work. For example, the following XPath expression would select all of the contact nodes from a document with the same structure as the example above:

/addressbook/contact

For a good introduction to using XPath, see the tutorial at http://www.w3schools.com/xpath

Importing JAXEN package, which supports the XPath, is similar to JDOM package installation. Download Jaxen package from /bham/common/java/lib or SourceForge.net

Examples

To give you more of an idea about XML processing using JDOM, here are several examples. These examples are based on an XML representation of a simple filesystem, with a root element being the filesystem root and the children of that element being files and folders:

<?xml version="1.0" encoding="UTF-8"?>
<root>
 <file name="Exercise1.java" />
 <file name="Exercise2.java" />
 <file name="Exercise3.java" />
 <file name="Exercise4.java" />
 <folder name="images">
    <file name="Image1.gif" />
    <file name="Image2.gif" />
    <file name="Image3.gif" />
 </folder>
</root>


The first example simply shows how to build a DOM tree from an XML file using the JDOM library

import java.io.File;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;


public class Example1 {
    
    private Document doc;
    private File file;
    
    /** Creates a new instance of Example1 */
    public Example1() {
        file = new File("/bham/htdocs/www/resources/courses/java/MScISS/exercises/ex3/examples/examplexml.xml");
        open(file);
    }
    
    
    /**
     * Builds a DOM tree from the specified XML file and saves it to the global 'doc' variable
     * @param _file The file to be opened
     */  
     public void open(File _file){
        file = _file;
        try{
            SAXBuilder builder = new SAXBuilder();
            doc = builder.build(file);
        }catch(JDOMException jde){
            jde.printStackTrace();
        }
        catch(java.io.IOException ioe){
            ioe.printStackTrace();
        }
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Example1();
    }
    
}


The second example builds on the first, with the addition of a find method. The find method uses an XPath expression to select all of the nodes whose name contains a particular string. For example, if we take the xml code from above, and performed

 find("Exercise")
the nodes selected would be:
 <file name="Exercise1.java" />
 <file name="Exercise2.java" />
 <file name="Exercise3.java" />
 <file name="Exercise4.java" />

The code for the second example is shown below:

import java.util.List;
import java.util.Iterator;
import java.io.File;
import org.jdom.Content;
import org.jdom.Element;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;


public class Example2 {
    
    private Document doc;
    private File file;
    
    /** Creates a new instance of Example2 */
    public Example2() {
        file = new File("/bham/htdocs/www/resources/courses/java/MScISS/"+
				"exercises/ex3/examples/examplexml.xml");
        open(file);
        find("Exercise");
    }
    
    /**
     * Builds a DOM tree from the specified XML file and saves it to the global 'doc' variable
     * @param _file The file to be opened
     */
    public void open(File _file){
        file = _file;
        try{
            SAXBuilder builder = new SAXBuilder();
            doc = builder.build(file);
        }catch(JDOMException jde){
            jde.printStackTrace();
        }
        catch(java.io.IOException ioe){
            ioe.printStackTrace();
        }
    }
    
    /**
     * Retrieves all files/folders whose name contins the value of the variable name
     * @param name The String to match with the filenames of files/folders
     */
    public void find(String name){
        java.util.ArrayList contacts = new java.util.ArrayList();
        try{
            List nodes = XPath.selectNodes(doc.getRootElement(), 
			"//file[contains(attribute::name,'"+name+"')] | " +
            "//folder[contains(attribute::name,'"+name+"']");
            Iterator it = nodes.iterator();
            while (it.hasNext()) {
                Element e = (Element)it.next();
                System.out.println(e.getName() +": "+e.getAttribute("name").getValue());
            }
            
        }catch(JDOMException e){
            e.printStackTrace();
            System.exit(0);
        }
        catch(Throwable t){
            t.printStackTrace();
        }
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Example2();
    }
    
}


The third example builds further on the other two by adding several methods:

  • adding new elements to the DOM tree
  • remove elements from the DOM tree
  • saving changes to disk

import java.util.List;
import java.util.Iterator;
import java.io.File;
import java.io.FileOutputStream;
import org.jdom.Content;
import org.jdom.Element;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import org.jdom.output.XMLOutputter;

public class Example3 {
    
    private Document doc;
    private File file;
    
    /** Creates a new instance of Example2 */
    public Example3() {
        file = new File("/bham/htdocs/www/resources/courses/java/MScISS/"+
			"exercises/ex3/examples/examplexml.xml");
        open(file);
        find("Exercise");
        addFile(null,"New1");
        addFile(null,"New2");
        addFile(null,"New4");
        find("New");
        removeFile("New2");
        find("New");
    }
    
    /**
     * Builds a DOM tree from the specified XML file and saves it to the global 'doc' variable
     * @param _file The file to be opened
     */
    public void open(File _file){
        file = _file;
        try{
            SAXBuilder builder = new SAXBuilder();
            doc = builder.build(file);
        }catch(JDOMException jde){
            jde.printStackTrace();
        }
        catch(java.io.IOException ioe){
            ioe.printStackTrace();
        }
    }
    
    /**
     * Saves the current DOM tree to the file it was loaded from
     */
    public void save(){
        try{
            XMLOutputter xmlo = new XMLOutputter();
            xmlo.output(doc,new FileOutputStream(file));
        }
        catch(java.io.FileNotFoundException fnf){
            fnf.printStackTrace();
        }
        catch(java.io.IOException ioe){
            ioe.printStackTrace();
        }
    }
    
    /**
     * Retrieves all files/folders whose name contins the value of the variable name
     * @param name The String to match with the filenames of files/folders
     */
    public void find(String name){
        java.util.ArrayList contacts = new java.util.ArrayList();
        try{
            List nodes = XPath.selectNodes(doc.getRootElement(), 
			"//file[contains(attribute::name,'"+name+"')] " +
            "| //folder[contains(attribute::name,'"+name+"']");
            Iterator it = nodes.iterator();
            while (it.hasNext()) {
                Element e = (Element)it.next();
                System.out.println(e.getName() +": "+e.getAttribute("name").getValue());
            }
            
        }catch(JDOMException e){
            e.printStackTrace();
            System.exit(0);
        }
        catch(Throwable t){
            t.printStackTrace();
        }
    }
    
    /**
     * Adds a file object to the DOM tree. If a parent is specified, 
	 * the file is added as a child of that node, otherwise it is added
     * as a child of the root node
     * @param parent The parent of the new file element
     * @param filename The filename of the new file element
     */
    public void addFile(Element parent, String filename){
        Element file = new Element("file");
        file.setAttribute("name",filename);
        if(parent != null){
            parent.addContent(file);}
        else{
            doc.getRootElement().addContent(file);
        }
    }
    
    /**
     * Removes the file with the specified filename from the DOM tree
     * @param filename The filename of the file to be removed
     */
    public void removeFile(String filename){
        try{
            Element file = (Element)XPath.selectSingleNode(doc.getRootElement(), 
			"//file[attribute::name='"+filename+"']");
            doc.getRootElement().removeContent(file);
        }catch(JDOMException jde){
            jde.printStackTrace();
        }
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Example3();
    }
    
}