001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.bio.program.sax.blastxml;
022
023import java.io.IOException;
024
025import javax.xml.parsers.ParserConfigurationException;
026import javax.xml.parsers.SAXParserFactory;
027
028import org.biojava.bio.BioException;
029import org.biojava.utils.stax.SAX2StAXAdaptor;
030import org.xml.sax.ContentHandler;
031import org.xml.sax.DTDHandler;
032import org.xml.sax.EntityResolver;
033import org.xml.sax.ErrorHandler;
034import org.xml.sax.InputSource;
035import org.xml.sax.SAXException;
036import org.xml.sax.SAXNotRecognizedException;
037import org.xml.sax.SAXNotSupportedException;
038import org.xml.sax.XMLReader;
039import org.xml.sax.helpers.DefaultHandler;
040
041/**
042 * A facade class that wraps the NCBI Blast XML 
043 * parsing framework in a more user-friendly form.
044 * It is identical to BlastlikeSAXParser in use.
045 *
046 * @author David Huen
047 * @since 1.3
048 */
049public class BlastXMLParserFacade
050    implements XMLReader
051{
052    // these are defined for the handlers
053    // DOWNSTREAM of this one.
054    private ContentHandler contentHandler;
055
056    // these are internal handlers
057    final BlastXMLParser blasthandler = new BlastXMLParser();
058
059    // this is the SAX parser
060    XMLReader parser;
061
062    // this is a default base URI so SAX does not complain
063    // when user doesn't give an absolute URI.
064    private String baseURI;
065
066    private class Resolver
067        implements EntityResolver
068    {
069        public InputSource resolveEntity(String publicID, String systemID)
070            throws SAXException
071        {
072//            try {
073                // resolve the NCBI URN
074//                System.out.println("resolve " + publicID + ":" + systemID);
075
076                String resourceName = "org/biojava/bio/program/sax/blastxml/";
077
078                if (publicID.equals("-//NCBI//NCBI BlastOutput/EN")) {
079                    resourceName = resourceName + "NCBI_BlastOutput.dtd";
080                }
081                else if (publicID.equals("-//NCBI//NCBI Entity Module//EN")) {
082                    resourceName = resourceName + "NCBI_Entity.mod";
083                }
084                else if (publicID.equals("-//NCBI//NCBI BlastOutput Module//EN")) {
085                    resourceName = resourceName + "NCBI_BlastOutput.mod";
086                }
087                else
088                    return null;
089
090                InputSource is = new InputSource(this.getClass().getClassLoader().getResourceAsStream(resourceName));
091                is.setSystemId(baseURI);
092
093                return is;
094        }
095    }
096
097    public BlastXMLParserFacade()
098        throws BioException
099    {
100        // just initialise content handler
101        // to avoid fubar if undefined
102        DefaultHandler handler = new DefaultHandler();
103        contentHandler = handler;
104
105        try {
106            // create the parser
107            parser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
108
109            // assign the EntityResolver
110            parser.setEntityResolver(new Resolver());
111            parser.setContentHandler(new SAX2StAXAdaptor(blasthandler)); 
112
113            // assign default sane settings
114            // namespaces must be true if the SAX2StAX parser isn't to fubar.
115            parser.setFeature("http://xml.org/sax/features/namespaces", true);
116            parser.setFeature("http://xml.org/sax/features/validation", false);
117
118            // make a base URI just in case the user doesn't
119            baseURI = this.getClass().getClassLoader().getResource("org/biojava/bio/program/sax/blastxml/").toString();
120        }
121        catch (SAXException se) { throw new BioException (se); }
122        catch (ParserConfigurationException sce) { throw new BioException(sce); }
123    } 
124
125    /**
126     * correct this later
127     */
128    public ContentHandler getContentHandler()
129    {
130        return contentHandler;
131    }
132
133    public DTDHandler getDTDHandler()
134    {
135        return parser.getDTDHandler();
136    }
137
138    /**
139     * This class has an EntityResolver that
140     * resolves the public ID specifying the
141     * NCBI DTDs to resource files within the
142     * BioJava libraries.  This call will return
143     * that resolver.  It you should set your
144     * own resolver, ensure you resolve that
145     * URN yourself or the parser will blow up
146     * on you!.
147     */
148    public EntityResolver getEntityResolver()
149    {
150        return parser.getEntityResolver();
151    }
152
153    public ErrorHandler getErrorHandler()
154    {
155        return parser.getErrorHandler();
156    }
157
158    public boolean getFeature(String name)
159        throws SAXNotRecognizedException, SAXNotSupportedException
160    {
161        return parser.getFeature(name);
162    }
163
164    public Object getProperty(String name)
165        throws SAXNotRecognizedException, SAXNotSupportedException
166    {
167        return parser.getProperty(name);
168    }
169
170    public void parse(InputSource is)
171        throws IOException, SAXException
172    {
173        if (is.getSystemId() == null)
174            is.setSystemId(baseURI);
175        parser.parse(is);
176    }
177
178    public void parse(String systemId)
179        throws IOException, SAXException
180    {
181        parser.parse(systemId);
182    }
183/**
184 * this sets the ContentHandler that receives
185 * SAX events from the internal Blast XML parser which
186 * is the actual ContentHandler.  <b> It will not
187 * change the internal Blast XML parser. </b>
188 */
189    public void setContentHandler(ContentHandler handler)
190    {
191        contentHandler = handler;
192        blasthandler.setContentHandler(handler);
193    }
194
195    public void setDTDHandler(DTDHandler handler)
196    {
197        parser.setDTDHandler(handler);        
198    }
199
200    /**
201     * This class has an EntityResolver that
202     * resolves the public ID specifying the
203     * NCBI DTDs to resource files within the
204     * BioJava libraries.  This call will return
205     * that resolver.  It you should set your
206     * own resolver, ensure you resolve that
207     * URN yourself or the parser will blow up
208     * on you!.
209     */
210    public void setEntityResolver(EntityResolver resolver)
211    {
212        parser.setEntityResolver(resolver);
213    }
214
215    public void setErrorHandler(ErrorHandler handler)
216    {
217        parser.setErrorHandler(handler);
218    }
219
220    /**
221     * by default, we set the parser to non-validating.
222     * change it if you wish/dare!  The parser is 
223     * also set to be namespace aware. <b>DO NOT
224     * CHANGE THAT!!!</b>
225      */
226    public void setFeature(String key, boolean value)
227        throws SAXNotRecognizedException, SAXNotSupportedException
228    {
229        parser.setFeature(key, value);
230    }
231
232    public void setProperty(String key, Object value)
233        throws SAXNotRecognizedException, SAXNotSupportedException
234    {
235        parser.setProperty(key, value);
236    }
237}
238