/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$ */ package org.eurocarbdb.application.glycanbuilder; import java.io.*; import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; import javax.xml.transform.sax.*; /** Utility class with functions to facilitate the parsing of XML files using the SAX architecture @author Alessio Ceroni (a.ceroni@imperial.ac.uk) */ public class SAXUtils { /** Extension of a SAX handler that parses an XML representation into an object tree. The nested elements represent members of the current object. This default implementation discard all the content of the element and should be extended by other classes. */ public static class ObjectTreeHandler extends DefaultHandler { // singletons protected Locator theLocator; protected LinkedList<ObjectTreeHandler> handlers_stack; // content protected Object object; protected StringBuilder text; protected HashMap<String,Vector<Object>> sub_objects; // construction /** Default constructor. */ public ObjectTreeHandler() { theLocator = null; handlers_stack = new LinkedList<ObjectTreeHandler>(); object = null; text = new StringBuilder(); sub_objects = new HashMap<String,Vector<Object>>(); } // methods /** Return the text parsed from the element. */ public String getText() { return text.toString(); } /** Return the object parsed from the XML stream */ public Object getObject() { return object; } /** Return <code>true</code> if this handler should be used to parse the current XML element. @see DefaultHandler#startElement */ protected boolean isElement(String namespaceURI, String localName, String qName) { return false; } /** Return the handler that should be used to parse a nested XML element. @see DefaultHandler#startElement */ protected ObjectTreeHandler getHandler(String namespaceURI, String localName, String qName) throws SAXException { return null; } /** Initialize the content of this handler @see DefaultHandler#startElement */ protected void initContent(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { object = null; text = new StringBuilder(); sub_objects = new HashMap<String,Vector<Object>>(); } /** Add a string to the text content of this handler @see DefaultHandler#characters */ protected void addText(char[] buf, int start, int length) throws SAXException { for( int i=0; i<length; i++ ) text.append(buf[start+i]); } /** Add a white space to the text content of this handler @see DefaultHandler#characters */ protected void addWhiteSpace() throws SAXException{ text.append(' '); } /** Add a sub object with a given name */ protected void addObject(String name, Object o) { if( o!=null ) { Vector<Object> v = sub_objects.get(name); if( v==null ) sub_objects.put(name,v = new Vector<Object>()); v.add(o); } } /** Return the sub object with the specified name. @param assert_non_null <code>true</code> if the method should throw an exception if the object is not found @return <code>null</code> if an object with that name is not found @throws Exception if assert_non_null is <code>true</code> and no object with the specified name is found */ protected Object getSubObject(String name, boolean assert_non_null) throws SAXException { Vector<Object> v = sub_objects.get(name); if( v==null ) { if( assert_non_null ) throw new SAXException(createMessage("Cannot find object with name " + name)); return null; } if( v.size()>1 ) throw new SAXException(createMessage("Multiple objects with name " + name)); return v.get(0); } /** Return the sub object with the specified name. @param default_value the value to be returned if an object with the specified name is not found */ protected Object getSubObject(String name, Object default_value) throws SAXException { Vector<Object> v = sub_objects.get(name); if( v==null ) return default_value; if( v.size()>1 ) throw new SAXException(createMessage("Multiple objects with name " + name)); return v.get(0); } /** Return the list of all objects parsed from the nested elements. */ protected Vector<Object> getSubObjects(String name) { Vector<Object> v = sub_objects.get(name); if( v==null ) return new Vector<Object>(); return v; } /** Finalize the content of this handler @see DefaultHandler#endElement */ protected Object finalizeContent(String namespaceURI, String localName, String qName) throws SAXException { return object; } // helpers protected String stringAttribute(Attributes atts, String name, String default_value) { if( atts==null || name==null ) return default_value; if( atts.getValue(name)==null ) return default_value; return atts.getValue(name); } protected String stringAttribute(Attributes atts, String name) { return stringAttribute(atts,name,null); } protected Double doubleAttribute(Attributes atts, String name, Double default_value) { if( atts==null || name==null ) return default_value; if( atts.getValue(name)==null ) return default_value; return Double.valueOf(atts.getValue(name)); } protected Double doubleAttribute(Attributes atts, String name) { return doubleAttribute(atts,name,null); } protected Integer integerAttribute(Attributes atts, String name, Integer default_value) { if( atts==null || name==null ) return default_value; if( atts.getValue(name)==null ) return default_value; return Integer.valueOf(atts.getValue(name)); } protected Integer integerAttribute(Attributes atts, String name) { return integerAttribute(atts,name,null); } protected Boolean booleanAttribute(Attributes atts, String name, Boolean default_value) { if( atts==null || name==null ) return default_value; if( atts.getValue(name)==null ) return default_value; return Boolean.valueOf(atts.getValue(name)); } protected Boolean booleanAttribute(Attributes atts, String name) { return booleanAttribute(atts,name,null); } protected String createMessage(String message) { if( theLocator==null ) return message; return (message + " at line " + theLocator.getLineNumber() + " column " + theLocator.getColumnNumber()); } protected String createMessage(Exception e) { if( e==null ) return createMessage("Exception"); return createMessage(e.getMessage()); } // events public void startDocument() throws SAXException { if( handlers_stack.size()!=0 ) throw new SAXException(createMessage("Unexpected start of document")); pushHandler(new DocumentHandler(this)); } public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if( handlers_stack.size()==0 ) new SAXException(createMessage("Unexpected start of element")); // get handler for this element ObjectTreeHandler handler = currentHandler().getHandler(namespaceURI,localName,qName); if( handler==null ) handler = new ObjectTreeHandler(); // init content handler.initContent(namespaceURI,localName,qName,atts); // push into stack pushHandler(handler); } public void characters(char[] ch, int start, int length) throws SAXException { if( handlers_stack.size()==0 ) new SAXException(createMessage("Unexpected character content")); currentHandler().addText(ch,start,length); } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { if( handlers_stack.size()==0 ) new SAXException(createMessage("Unexpected character content")); currentHandler().addWhiteSpace(); } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if( handlers_stack.size()==0 ) new SAXException(createMessage("Unexpected end of element")); // build object Object sub_object = currentHandler().finalizeContent(namespaceURI,localName,qName); // remove this handler from stack popHandler(); // add sub object the content of the parent if( sub_object!=null ) currentHandler().addObject(qName,sub_object); } public void endDocument() throws SAXException { if( handlers_stack.size()==0 ) new SAXException(createMessage("Unexpected end of document")); popHandler(); } public void setDocumentLocator(org.xml.sax.Locator locator) { theLocator = locator; } // internal methods private void pushHandler(ObjectTreeHandler handler) throws SAXException{ if( handler==null ) throw new SAXException(createMessage("Invalid null handler")); handlers_stack.addFirst(handler); } private void popHandler() throws SAXException { if( handlers_stack.size()==0 ) throw new SAXException(createMessage("Empty stack")); handlers_stack.removeFirst(); } private ObjectTreeHandler currentHandler() throws SAXException { if( handlers_stack.size()==0 ) throw new SAXException(createMessage("Empty stack")); return handlers_stack.getFirst(); } } private static class DocumentHandler extends ObjectTreeHandler { private ObjectTreeHandler root_handler = null; public DocumentHandler(ObjectTreeHandler _root) { root_handler = _root; } public ObjectTreeHandler getHandler(String namespaceURI, String localName, String qName) { if( root_handler!=null && root_handler.isElement(namespaceURI,localName,qName) ) return root_handler; return null; } } /** Interface that should be implemented by every object that can write on a SAX stream. */ public interface SAXWriter { /** Method that is called to write the information represented by this object into an XML document using the specified handler*/ public void write(TransformerHandler handler) throws SAXException; } private SAXUtils() {} /** Parse a stream in XML format using the specified handler @throws Exception on errors */ public static void read(InputStream is, DefaultHandler h) throws Exception { SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); parser.parse(is,h); } /** Write to a stream in XML format using the specified writer @throws Exception on errors */ public static void write(OutputStream os, SAXWriter w) throws Exception { // init handler StreamResult streamResult = new StreamResult(os); SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); TransformerHandler hd = tf.newTransformerHandler(); Transformer serializer = hd.getTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1"); serializer.setOutputProperty(OutputKeys.INDENT,"yes"); hd.setResult(streamResult); // write document hd.startDocument(); w.write(hd); hd.endDocument(); } }