Java dynamic proxy example code: SAX XML handler 

Joined:
03/07/2011
Posts:
57

June 03, 2011 09:41:03    Last update: June 03, 2011 09:41:03
Dynamic proxy can be used to eliminate the need to stub out unused interface methods. This is an example for a simple SAX content handler for XML parsing.
  1. The org.xml.sax.ContentHandler interface requires 11 methods be implemented but we only need three:
    import java.io.IOException;
    import java.util.Arrays;
    import org.xml.sax.helpers.XMLReaderFactory;
    import org.xml.sax.XMLReader;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.Attributes;
    import org.xml.sax.Locator;
    import org.xml.sax.SAXException;
    
    public class SaxParserDemo {
        public static void main(String[] args) throws SAXException, IOException {
    	XMLReader p = XMLReaderFactory.createXMLReader();
    	p.setContentHandler(new DemoContentHandler());
    	p.parse("demo.xml");
        }
    }
    
    class DemoContentHandler implements ContentHandler {
        public void startElement(String namespaceURI, 
    			     String localName, 
    			     String qName, 
    			     Attributes atts) 
    		throws SAXException {
    	System.out.printf("<%s>", qName);
        }
      
        public void characters(char[] text, 
    			   int start, 
    			   int length) 
    		throws SAXException {
    	System.out.print(Arrays.copyOfRange(text, start, start + length));
        }
      
        public void endElement(String namespaceURI, 
    			   String localName, 
    			   String qName) 
    		throws SAXException {
    	System.out.printf("</%s>", qName);
        }
    
        // do nothing methods  
        public void setDocumentLocator(Locator locator) {}
        public void startDocument() throws SAXException {}
        public void endDocument() throws SAXException {}
        public void startPrefixMapping(String prefix, String uri) throws SAXException {}
        public void endPrefixMapping(String prefix) throws SAXException {}
        public void skippedEntity(String name) throws SAXException {}  
        public void ignorableWhitespace(char[] text, int start, int length) throws SAXException {}
        public void processingInstruction(String target, String data) throws SAXException {}
    }
    

  2. With a dynamic proxy, we don't need the empty blocks for the unused methods:
    import java.io.IOException;
    import java.util.Arrays;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.InvocationHandler;
    import org.xml.sax.helpers.XMLReaderFactory;
    import org.xml.sax.XMLReader;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.Attributes;
    import org.xml.sax.Locator;
    import org.xml.sax.SAXException;
    
    public class SaxParserDemo2 {
        public static void main(String[] args) throws SAXException, IOException {
    	XMLReader p = XMLReaderFactory.createXMLReader();
    	p.setContentHandler((ContentHandler) Proxy.newProxyInstance(
    	    SaxParserDemo2.class.getClassLoader(),
    	    new Class[] { ContentHandler.class },
    	    new SaxDemoInvocationHandler()
    	));
    	p.parse("demo.xml");
        }
    }
    
    class SaxDemoInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
    	String methodName = method.getName();
    	if (methodName.equals("startElement")) {
    	    System.out.printf("<%s>", args[2]);
    	}
    	else if (methodName.equals("characters")) {
    	    char[] text = (char[]) args[0];
    	    int start = ((Integer) args[1]).intValue();
    	    int length = ((Integer) args[2]).intValue();
    	    System.out.print(Arrays.copyOfRange(text, start, start + length));
    	}
    	else if (methodName.equals("endElement")) {
    	    System.out.printf("</%s>", args[2]);
    	}
    	return null;
        }
    }
    

  3. Equivalently (with anonymous inner class):
    import java.io.IOException;
    import java.util.Arrays;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.InvocationHandler;
    import org.xml.sax.helpers.XMLReaderFactory;
    import org.xml.sax.XMLReader;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.Attributes;
    import org.xml.sax.Locator;
    import org.xml.sax.SAXException;
    
    public class SaxParserDemo2 {
        public static void main(String[] args) throws SAXException, IOException {
    	XMLReader p = XMLReaderFactory.createXMLReader();
    	p.setContentHandler((ContentHandler) Proxy.newProxyInstance(
    	    SaxParserDemo2.class.getClassLoader(),
    	    new Class[] { ContentHandler.class },
    	    new InvocationHandler() {
    		@Override
    		public Object invoke(Object proxy, Method method, Object[] args) {
    		    String methodName = method.getName();
    		    if (methodName.equals("startElement")) {
    			System.out.printf("<%s>", args[2]);
    		    }
    		    else if (methodName.equals("characters")) {
    			char[] text = (char[]) args[0];
    			int start = ((Integer) args[1]).intValue();
    			int length = ((Integer) args[2]).intValue();
    			System.out.print(Arrays.copyOfRange(text, start, start + length));
    		    }
    		    else if (methodName.equals("endElement")) {
    			System.out.printf("</%s>", args[2]);
    		    }
    		    return null;
    		}
    	    }
    	));
    	p.parse("demo.xml");
        }
    }
    

  4. demo.xml:
    <breakfast-menu>
        <food>
    	<name>Belgian Waffles</name>
    	<price>$5.95</price>
    	<description>
    	    two of our famous Belgian Waffles with plenty of real maple syrup
    	</description>
    	<calories>650</calories>
        </food>
    
        <food>
    	<name>Strawberry Belgian Waffles</name>
    	<price>$7.95</price>
    	<description>
    	light Belgian waffles covered with strawberries and whipped cream
    	</description>
    	<calories>900</calories>
        </food>
    
        <food>
    	<name>Berry-Berry Belgian Waffles</name>
    	<price>$8.95</price>
    	<description>
    	light Belgian waffles covered with an assortment of fresh berries and whipped cream
    	</description>
    	<calories>900</calories>
        </food>
    </breakfast-menu>
    

Share |
| Comment  | Tags