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));
}
}
|