Meeting the Visitor Pattern

   

The visitor pattern is a sort of mirror of the builder pattern. Again, our goal will be to separate the object structure from the writing of the XML document.

Figure 1.8 illustrates the generic visitor pattern. The various components are as follows :

  • The Element class and its descendants, which represent the object structure

  • The Structure class, which is the root of the structure

  • The Visitor and its descendant, which walk through the object structure, writing the XML document as they progress

  • The client, which is the class that sends the visitor on to the data structure

Warning

Don't confuse the class Element with an XML element. In the visitor pattern, Element stands for an element in the data structure.


Figure 1.8. Visitor pattern.

graphics/01fig08.gif

One of the remarkable aspects of this pattern is how a Visitor object recognizes a concrete element. It would have been possible to explicitly test the various options, such as in the following:

 if(element instanceof Catalog)       visitCatalog((Catalog)element);    else if(element instanceof VisualProduct)       visitVisualProduct((VisualProduct)element);    // and more 

However, this method is error prone. It is particularly easy to forget to update this list of tests when new classes are added to the structure.

Instead, Element and Visitor use a two-step protocol to recognize each other. Element implements the accept() method, which takes a Visitor as a parameter. When an Element accepts a Visitor , it calls the appropriate visitConcreteElement() method, passing a reference to itself, to the Visitor object.

Applying the Visitor Pattern

Figure 1.9 applies the visitor pattern to our object structure. It introduces two new interfaces and two new classes:

  • The CatalogElement interface from which the various classes in the data structure inherit. It declares the accept() method.

  • The CatalogVisitor interface declares various methods for visiting the object structure.

  • The XMLVisitor class is one visitor that writes the object structure in XML.

  • The CatalogViewer class implements the main() method for the application. It starts the visitor pattern.

Figure 1.9. Applying the visitor pattern.

graphics/01fig09.gif

The CatalogElement interface is shown in Listing 1.11. It declares only one method: accept() .

Listing 1.11 CatalogElement.java
 package com.psol.catalog; import java.io.IOException; public interface CatalogElement {    public void accept(CatalogVisitor visitor)       throws IOException; } 

The accept() method is implemented in CatalogElement 's descendants, such as the Catalog class (refer to Listing 1.1):

 public void accept(CatalogVisitor visitor)       throws IOException    {       visitor.visitCatalog(this);    } 

Listing 1.12 is the CatalogVisitor . It declares one method for each element in the object structure.

Listing 1.12 CatalogVisitor.jar
 package com.psol.catalog; import java.io.IOException; public interface CatalogVisitor {    public void visitCatalog(Catalog catalog)       throws IOException;    public void visitVisualProduct(VisualProduct product)       throws IOException;    public void visitTextualProduct(TextualProduct product)       throws IOException;    public void visitDescription(Description description)       throws IOException; } 

XMLVisitor , as seen in Listing 1.13, is one implementation of CatalogVisitor that writes the XML document.

For each object, it writes the corresponding XML code. If the object contains other objects, it calls their accept() method, which causes these objects to be written as well:

 public void visitTextualProduct(TextualProduct product)       throws IOException    {       pw.print("<Product");       printAttribute("id",product.getId());       Boolean bool = new Boolean(product.isChecked());       printAttribute("checked",bool.toString());       pw.println('>');       printElement("Text",product.getText());       pw.println("<Descriptions>");       for(int i = 0;i < product.getSize();i++)          product.descriptionAt(i).accept(this);       pw.println("</Descriptions>");       pw.println("</Product>");    } 
Listing 1.13 XMLVisitor.java
 package com.psol.catalog; import java.io.*; public class XMLVisitor    implements CatalogVisitor {    protected PrintWriter pw;    public XMLVisitor(PrintWriter pw)    {       this.pw = pw;    }    public void visitCatalog(Catalog catalog)       throws IOException    {       pw.println("<?xml version='1.0'?>");       pw.println("<Catalog>");       for(int i = 0;i < catalog.getSize();i++)          catalog.productAt(i).accept(this);       pw.print("</Catalog>");       pw.flush();    }    public void visitVisualProduct(VisualProduct product)       throws IOException    {       pw.print("<Product");       printAttribute("id",product.getId());       Boolean bool = new Boolean(product.isChecked());       printAttribute("checked",bool.toString());       pw.println('>');       printElement("Text",product.getText());       printElement("Image",product.getImage());       pw.println("</Product>");    }    public void visitTextualProduct(TextualProduct product)       throws IOException    {       pw.print("<Product");       printAttribute("id",product.getId());       Boolean bool = new Boolean(product.isChecked());       printAttribute("checked",bool.toString());       pw.println('>');       printElement("Text",product.getText());       pw.println("<Descriptions>");       for(int i = 0;i < product.getSize();i++)          product.descriptionAt(i).accept(this);       pw.println("</Descriptions>");       pw.println("</Product>");    }    public void visitDescription(Description description)       throws IOException    {       pw.print("<Text");       printAttribute("xml:lang",description.getLanguage());       pw.print('>');       printContent(description.getText());       pw.println("</Text>");    }    public void printElement(String tag,String content)       throws IOException    {       pw.print('<'); pw.print(tag); pw.print('>');       printContent(content);       pw.print("</"); pw.print(tag); pw.println('>');    }    public void printContent(String content)       throws IOException    {       // works with any Writer encoding but EBCDIC       for(int i = 0;i < content.length();i++)       {          char c = content.charAt(i);          if(c == '<')             pw.print("&lt;");          else if(c == '&')             pw.print("&amp;");          else if(c > '\ u007f')          {             pw.print("&#");             pw.print(Integer.toString(c));             pw.print(';');          }          else             pw.print(c);       }    }    public void printAttribute(String name,String value)       throws IOException    {       pw.print(''); pw.print(name); pw.print("='");       // works with any Writer encoding but EBCDIC       for(int i = 0;i < value.length();i++)       {          char c = value.charAt(i);          if(c == '\ '')             pw.print("&apos;");          else if(c == '&')             pw.print("&amp;");          else if(c > '\ u007f')          {             pw.print("&#");             pw.print(Integer.toString(c));             pw.print(';');          }          else             pw.print(c);       }       pw.print('\ '');    } } 

The application's main class is shown in Listing 1.14, CatalogViewer . CatalogViewer creates a frame on which it places an instance of CatalogPanel (refer to Listing 1.3). At startup, it uses the builder pattern to read the catalog.xml file.

When the window is closed, it uses the visitor pattern to overwrite catalog.xml . This saves any changes by the customer, such as selecting or deselecting a product:

 Writer writer = new FileWriter("catalog.xml");    CatalogVisitor visitor =       new XMLVisitor(new PrintWriter(writer));    catalog.accept(visitor); 

Tip

It would not be difficult to use JavaMail (the standard Java API for emailing) in conjunction with the visitor pattern to automatically email the product selection. This is left as an exercise for the reader.


Listing 1.14 CatalogViewer.java
 package com.psol.catalog; import java.io.*; import java.awt.*; import org.xml.sax.*; import java.awt.event.*; import org.xml.sax.helpers.XMLReaderFactory; public class CatalogViewer {    public static final String PARSER_NAME =       "org.apache.xerces.parsers.SAXParser";    protected static class SaveOnClose       extends WindowAdapter    {       protected Catalog catalog;       public SaveOnClose(Catalog catalog)       {          this.catalog = catalog;       }       public void windowClosing(WindowEvent evt)       {          try          {             Writer writer = new FileWriter("catalog.xml");             CatalogVisitor visitor =                new XMLVisitor(new PrintWriter(writer));             catalog.accept(visitor);          }          catch(IOException e)             { }          System.exit(0);       }    }    public static void main(String[] args)       throws Exception    {       XMLReader xmlReader =          XMLReaderFactory.createXMLReader(PARSER_NAME);       CatalogBuilder builder = new DefaultCatalogBuilder();       xmlReader.setContentHandler(new XMLDirector(builder));       xmlReader.parse("catalog.xml");       Catalog catalog = builder.getCatalog();       Panel panel = new CatalogPanel(catalog);       Frame frame = new Frame("Catalog Viewer");       frame.add(panel);       frame.setResizable(false);       frame.setSize(400,200);       frame.addWindowListener(new SaveOnClose(catalog));       frame.show();    } } 
   


Applied XML Solutions
Applied XML Solutions
ISBN: 0672320541
EAN: 2147483647
Year: 1999
Pages: 142

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net