in progress...
[jalview.git] / forester / java / src / org / forester / io / parsers / tol / TolParser.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: https://sites.google.com/site/cmzmasek/home/software/forester
25
26 package org.forester.io.parsers.tol;
27
28 import java.io.File;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.Reader;
34 import java.io.StringReader;
35 import java.util.Enumeration;
36 import java.util.zip.ZipEntry;
37 import java.util.zip.ZipFile;
38 import java.util.zip.ZipInputStream;
39
40 import javax.xml.parsers.ParserConfigurationException;
41 import javax.xml.parsers.SAXParser;
42 import javax.xml.parsers.SAXParserFactory;
43
44 import org.forester.io.parsers.PhylogenyParser;
45 import org.forester.io.parsers.util.PhylogenyParserException;
46 import org.forester.phylogeny.Phylogeny;
47 import org.forester.util.ForesterUtil;
48 import org.xml.sax.InputSource;
49 import org.xml.sax.SAXException;
50 import org.xml.sax.SAXNotRecognizedException;
51 import org.xml.sax.SAXNotSupportedException;
52 import org.xml.sax.SAXParseException;
53 import org.xml.sax.XMLReader;
54 import org.xml.sax.helpers.DefaultHandler;
55
56 public class TolParser implements PhylogenyParser {
57
58     final public static String JAXP_SCHEMA_LANGUAGE                       = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
59     final public static String W3C_XML_SCHEMA                             = "http://www.w3.org/2001/XMLSchema";
60     final public static String JAXP_SCHEMA_SOURCE                         = "http://java.sun.com/xml/jaxp/properties/schemaSource";
61     final public static String SAX_FEATURES_VALIDATION                    = "http://xml.org/sax/features/validation";
62     final public static String APACHE_FEATURES_VALIDATION_SCHEMA          = "http://apache.org/xml/features/validation/schema";
63     final public static String APACHE_FEATURES_VALIDATION_SCHEMA_FULL     = "http://apache.org/xml/features/validation/schema-full-checking";
64     final public static String APACHE_PROPERTIES_SCHEMA_EXTERNAL_LOCATION = "http://apache.org/xml/properties/schema/external-schemaLocation";
65     private Object             _source;
66     private boolean            _valid;
67     private boolean            _zipped_inputstream;
68     private int                _error_count;
69     private int                _warning_count;
70     private String             _schema_location;
71     private StringBuffer       _error_messages;
72     private StringBuffer       _warning_messages;
73
74     public TolParser() {
75         init();
76         reset();
77     }
78
79     public int getErrorCount() {
80         return _error_count;
81     }
82
83     public StringBuffer getErrorMessages() {
84         return _error_messages;
85     }
86
87     private Reader getReaderFromZipFile() throws IOException {
88         Reader reader = null;
89         final ZipFile zip_file = new ZipFile( getSource().toString() );
90         final Enumeration<?> zip_file_entries = zip_file.entries();
91         while ( zip_file_entries.hasMoreElements() ) {
92             final ZipEntry zip_file_entry = ( ZipEntry ) zip_file_entries.nextElement();
93             if ( !zip_file_entry.isDirectory() && ( zip_file_entry.getSize() > 0 ) ) {
94                 final InputStream is = zip_file.getInputStream( zip_file_entry );
95                 reader = new InputStreamReader( is );
96                 break;
97             }
98         }
99         try {
100             zip_file.close();
101         }
102         catch ( final Exception e ) {
103             // Ignore.
104         }
105         return reader;
106     }
107
108     private String getSchemaLocation() {
109         return _schema_location;
110     }
111
112     private Object getSource() {
113         return _source;
114     }
115
116     public int getWarningCount() {
117         return _warning_count;
118     }
119
120     public StringBuffer getWarningMessages() {
121         return _warning_messages;
122     }
123
124     private void init() {
125         setZippedInputstream( false );
126     }
127
128     public boolean isValid() {
129         return _valid;
130     }
131
132     private boolean isZippedInputstream() {
133         return _zipped_inputstream;
134     }
135
136     @Override
137     public Phylogeny[] parse() throws IOException, PhylogenyParserException {
138         reset();
139         final TolXmlHandler handler = new TolXmlHandler();
140         final SAXParserFactory factory = SAXParserFactory.newInstance();
141         factory.setNamespaceAware( true );
142         try {
143             if ( !ForesterUtil.isEmpty( getSchemaLocation() ) ) {
144                 factory.setFeature( SAX_FEATURES_VALIDATION, true );
145                 factory.setFeature( APACHE_FEATURES_VALIDATION_SCHEMA, true );
146                 factory.setFeature( APACHE_FEATURES_VALIDATION_SCHEMA_FULL, true );
147             }
148         }
149         catch ( final SAXNotRecognizedException e ) {
150             e.printStackTrace();
151             throw new PhylogenyParserException( "sax not recognized exception: " + e.getMessage() );
152         }
153         catch ( final SAXNotSupportedException e ) {
154             e.printStackTrace();
155             throw new PhylogenyParserException( "sax not supported exception: " + e.getMessage() );
156         }
157         catch ( final ParserConfigurationException e ) {
158             e.printStackTrace();
159             throw new PhylogenyParserException( "parser _configuration exception: " + e.getMessage() );
160         }
161         catch ( final Exception e ) {
162             e.printStackTrace();
163             throw new PhylogenyParserException( "error while configuring sax parser: " + e.getMessage() );
164         }
165         try {
166             final SAXParser parser = factory.newSAXParser();
167             if ( !ForesterUtil.isEmpty( getSchemaLocation() ) ) {
168                 parser.setProperty( JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA );
169                 parser.setProperty( JAXP_SCHEMA_SOURCE, getSchemaLocation() );
170                 parser.setProperty( APACHE_PROPERTIES_SCHEMA_EXTERNAL_LOCATION, getSchemaLocation() );
171             }
172             final XMLReader xml_reader = parser.getXMLReader();
173             xml_reader.setContentHandler( handler );
174             xml_reader.setErrorHandler( new TolParserErrorHandler() );
175             if ( getSource() instanceof File ) {
176                 if ( !getSource().toString().toLowerCase().endsWith( ".zip" ) ) {
177                     xml_reader.parse( new InputSource( new FileReader( ( File ) getSource() ) ) );
178                 }
179                 else {
180                     final Reader reader = getReaderFromZipFile();
181                     if ( reader == null ) {
182                         throw new PhylogenyParserException( "Zip file \"" + getSource()
183                                                             + "\" appears not to contain any entries" );
184                     }
185                     xml_reader.parse( new InputSource( reader ) );
186                 }
187             }
188             else if ( getSource() instanceof InputSource ) {
189                 xml_reader.parse( ( InputSource ) getSource() );
190             }
191             else if ( getSource() instanceof InputStream ) {
192                 if ( !isZippedInputstream() ) {
193                     final InputStream is = ( InputStream ) getSource();
194                     final Reader reader = new InputStreamReader( is );
195                     xml_reader.parse( new InputSource( reader ) );
196                 }
197                 else {
198                     final ZipInputStream zip_is = new ZipInputStream( ( InputStream ) getSource() );
199                     zip_is.getNextEntry();
200                     final Reader reader = new InputStreamReader( zip_is );
201                     if ( reader == null ) {
202                         throw new PhylogenyParserException( "Zip input stream \"" + getSource()
203                                                             + "\" appears not to contain any data" );
204                     }
205                     xml_reader.parse( new InputSource( reader ) );
206                 }
207             }
208             else if ( getSource() instanceof String ) {
209                 final File file = new File( getSource().toString() );
210                 final Reader reader = new FileReader( file );
211                 xml_reader.parse( new InputSource( reader ) );
212             }
213             else if ( getSource() instanceof StringBuffer ) {
214                 final StringReader string_reader = new StringReader( getSource().toString() );
215                 xml_reader.parse( new InputSource( string_reader ) );
216             }
217             else {
218                 throw new PhylogenyParserException( "attempt to parse object of unsupported type: \""
219                         + getSource().getClass() + "\"" );
220             }
221         }
222         catch ( final SAXException sax_exception ) {
223             throw new PhylogenyParserException( "Failed to parse [" + getSource() + "]: " + sax_exception.getMessage() );
224         }
225         catch ( final ParserConfigurationException parser_config_exception ) {
226             throw new PhylogenyParserException( "Failed to parse [" + getSource()
227                                                 + "] Problem with xml parser _configuration: " + parser_config_exception.getMessage() );
228         }
229         catch ( final IOException e ) {
230             throw new PhylogenyParserException( "Problem with input source [" + getSource() + "]: \n" + e.getMessage() );
231         }
232         catch ( final Exception e ) {
233             e.printStackTrace();
234             throw new PhylogenyParserException( "Failed to parse [" + getSource() + "]: " + e.getMessage() );
235         }
236         catch ( final Error err ) {
237             err.printStackTrace();
238             throw new PhylogenyParserException( "Severe error: " + err.getMessage() );
239         }
240         final Phylogeny[] ps = new Phylogeny[ handler.getPhylogenies().size() ];
241         int i = 0;
242         for( final Phylogeny phylogeny : handler.getPhylogenies() ) {
243             ps[ i++ ] = phylogeny;
244         }
245         return ps;
246     }
247
248     private void reset() {
249         _valid = true;
250         _error_count = 0;
251         _warning_count = 0;
252         _error_messages = new StringBuffer();
253         _warning_messages = new StringBuffer();
254     }
255
256     @Override
257     public void setSource( final Object source ) {
258         _source = source;
259     }
260
261     public void setValidateAgainstSchema( final String schema_location ) {
262         _schema_location = schema_location;
263     }
264
265     public void setZippedInputstream( final boolean zipped_inputstream ) {
266         _zipped_inputstream = zipped_inputstream;
267     }
268
269     private class TolParserErrorHandler extends DefaultHandler {
270
271         @Override
272         public void error( final SAXParseException e ) {
273             ++_error_count;
274             _valid = false;
275             throw new RuntimeException( "XML error at line " + e.getLineNumber() + ": \n" + e.getMessage() );
276         }
277
278         @Override
279         public void fatalError( final SAXParseException e ) {
280             ++_error_count;
281             _valid = false;
282             throw new RuntimeException( "Fatal XML error at line " + e.getLineNumber() + ": \n" + e.getMessage() );
283         }
284
285         @Override
286         public void warning( final SAXParseException e ) {
287             ++_warning_count;
288             if ( _error_messages.length() > 1 ) {
289                 _error_messages.append( ForesterUtil.LINE_SEPARATOR );
290             }
291             _warning_messages.append( "[line: " + e.getLineNumber() + "] " + e.getMessage() );
292         }
293     }
294
295     @Override
296     public String getName() {
297         return "ToL Parser";
298     }
299 }