Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

SAX, the power API

In this preview from XML by Example, compare DOM and SAX and then put SAX to work

Return to article


Listing 4. BestDeal.java


/*
 * XML By Example, chapter 8: SAX
 */

package com.psol.xbe2;

import java.util.*;
import org.xml.sax.*;
import java.io.IOException;
import org.xml.sax.helpers.*;
import java.text.MessageFormat;

/**
 * This class receives events from the SAX2Internal adapter
 * and does the comparison required.
 * This class holds the "business logic."
 * SAX event handling is done in an inner class.
 */

public class BestDeal
{
   /**
    * SAX event handler to adapt from the SAX interface to
    * best deal data structure.
    */

   protected class SAX2BestDeal
      extends DefaultHandler
   {
      /**
       * constants
       */

      /**
       * state constants
       */
      final protected int START = 0,
                          PRICE_LIST = 1,
                          PRICE_LIST_NAME = 2,
                          VENDOR = 3,
                          VENDOR_NAME = 4,
                          VENDOR_PRICE_QUOTE = 5;

      /**
       * the current state
       */
      protected int state = START;

      /**
       * current leaf element and current vendor
       */
      protected String vendorName = null;
      protected StringBuffer buffer = null;
      protected int delivery = Integer.MAX_VALUE;

      /**
       * startElement event
       * @param uri namespace URI
       * @param name local name
       * @param qualifiedName qualified name (with prefix)
       * @param attributes attributes list
       */
      public void startElement(String uri,
                               String name,
                               String qualifiedName,
                               Attributes attributes)
         throws SAXException
      {
         if(!uri.equals(NAMESPACE_URI))
            return;
         // this accept many combinations of elements
         // it would work if new elements where being added, etc.
         // this ensures maximal flexibility: if the document
         // has to be validated, use a validating parser
         switch(state)
         {
            case START:
               if(name.equals("price-list"))
                  state = PRICE_LIST;
               break;
            case PRICE_LIST:
               if(name.equals("name"))
               {
                  state = PRICE_LIST_NAME;
                  buffer = new StringBuffer();
               }
               if(name.equals("vendor"))
                  state = VENDOR;
               break;
            case VENDOR:
               if(name.equals("name"))
               {
                  state = VENDOR_NAME;
                  buffer = new StringBuffer();
               }
               if(name.equals("price-quote"))
               {
                  state = VENDOR_PRICE_QUOTE;
                  String st = attributes.getValue("","delivery");
                  delivery = Integer.parseInt(st);
                  buffer = new StringBuffer();
               }
               break;
         }
      }

      /**
       * content of the element
       * @param chars documents characters
       * @param start first character in the content
       * @param length last character in the content
       */
      public void characters(char[] chars,int start,int length)
      {
         switch(state)
         {
            case PRICE_LIST_NAME:
            case VENDOR_NAME:
            case VENDOR_PRICE_QUOTE:
               buffer.append(chars,start,length);
               break;
         }
      }

      /**
       * endElement event
       * @param uri namespace URI
       * @param name local name
       * @param qualifiedName qualified name (with prefix)
       */
      public void endElement(String uri,
                             String name,
                             String qualifiedName)
      {
         if(!uri.equals(NAMESPACE_URI))
            return;
         switch(state)
         {
            case PRICE_LIST_NAME:
               if(name.equals("name"))
               {
                  state = PRICE_LIST;
                  setProductName(buffer.toString());
                  buffer = null;
               }
               break;
            case VENDOR_NAME:
               if(name.equals("name"))
               {
                  state = VENDOR;
                  vendorName = buffer.toString();
                  buffer = null;
               }
               break;
            case VENDOR_PRICE_QUOTE:
               if(name.equals("price-quote"))
               {
                  state = VENDOR;
                  double price = 0.0;
                  Double stringDouble =
                     Double.valueOf(buffer.toString());
                  if(null != stringDouble)
                     price = stringDouble.doubleValue();
                  compare(vendorName,price,delivery);
                  delivery = Integer.MAX_VALUE;
                  buffer = null;
               }
               break;
            case VENDOR:
               if(name.equals("vendor"))
               {
                  state = PRICE_LIST;
                  vendorName = null;
               }
               break;
            case PRICE_LIST:
               if(name.equals("price-list"))
                  state = START;
               break;
         }
      }
   }

   /**
    * constant
    */
   protected static final String
      MESSAGE =
         "The best deal is proposed by {0}. " +
         "A(n) {1} delivered in {2,number,integer} days for " +
         "{3,number,currency}",
      NAMESPACE_URI = "http://www.psol.com/xbe2/listing8.3",
      PARSER_NAME = "org.apache.xerces.parsers.SAXParser";

   /**
    * properties we are collecting: best price, delivery time,
    * product and vendor names
    */
   public double price = Double.MAX_VALUE;
   public int delivery = Integer.MAX_VALUE;
   public String product = null,
                 vendor = null;

   /**
    * target delivery value (refuse elements above this target)
    */
   protected int targetDelivery;

   /**
    * creates a BestDeal
    * @param td the target for delivery
    */
   public BestDeal(int td)
   {
      targetDelivery = td;
   }

   /**
    * called by SAX2Internal when it has found the product name
    * @param name the product name
    */
   public void setProductName(String name)
   {
      product = name;
   }

   /**
    * called by SAX2Internal when it has found a price
    * @param vendor vendor's name
    * @param price price proposal
    * @param delivery delivery time proposal
    */
   public void compare(String vendor,double price,int delivery)
   {
      if(delivery <= targetDelivery)
      {
         if(this.price > price)
         {
            this.price = price;
            this.vendor = vendor;
            this.delivery = delivery;
         }
      }
   }

   /**
    * return a ContentHandler that populates this object
    * @return the ContentHandler
    */
   public ContentHandler getContentHandler()
   {
      return new SAX2BestDeal();
   }

   /**
    * main() method
    * decodes command-line parameters and invoke the parser
    * @param args command-line argument
    * @throw Exception catch-all for underlying exceptions
    */
   public static void main(String[] args)
      throws IOException, SAXException
   {
      if(args.length < 2)
      {
         System.out.println(
            "java com.psol.xbe2.BestDeal file delivery");
         return;
      }

      BestDeal bestDeal = new BestDeal(Integer.parseInt(args[1]));

      XMLReader parser =
         XMLReaderFactory.createXMLReader(PARSER_NAME);
      parser.setContentHandler(bestDeal.getContentHandler());
      parser.parse(args[0]);

      Object[] objects = new Object[]
      {
          bestDeal.vendor,
          bestDeal.product,
          new Integer(bestDeal.delivery),
          new Double(bestDeal.price)
      };
      System.out.println(MessageFormat.format(MESSAGE,objects));
   }
}

Return to article