/* * Eoulsan development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public License version 2.1 or * later and CeCILL-C. This should be distributed with the code. * If you do not have a copy, see: * * http://www.gnu.org/licenses/lgpl-2.1.txt * http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.txt * * Copyright for this code is held jointly by the Genomic platform * of the Institut de Biologie de l'École normale supérieure and * the individual authors. These should be listed in @author doc * comments. * * For more information on the Eoulsan project and its aims, * or to join the Eoulsan Google group, visit the home page * at: * * http://outils.genomique.biologie.ens.fr/eoulsan * */ package fr.ens.biologie.genomique.eoulsan.bio.io; import static fr.ens.biologie.genomique.eoulsan.bio.io.BioCharsets.GFF_CHARSET; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import fr.ens.biologie.genomique.eoulsan.bio.BadBioEntryException; import fr.ens.biologie.genomique.eoulsan.bio.GFFEntry; import fr.ens.biologie.genomique.eoulsan.util.FileUtils; /** * This class defines a GFF3 reader. * @since 1.0 * @author Laurent Jourdren */ public class GFFReader implements Iterator<GFFEntry>, Iterable<GFFEntry>, Closeable { private final BufferedReader reader; private GFFEntry result = null; private int count; private boolean end; private boolean fastaSectionFound; private final Map<String, List<String>> metadata = new LinkedHashMap<>(); private boolean nextCallDone = true; protected IOException ioException; protected BadBioEntryException bbeException; private boolean gff3Format = true; @Override public Iterator<GFFEntry> iterator() { return this; } /** * Test if a fasta section was found. * @return true if a Fasta section was found */ public boolean isFastaSectionFound() { return this.fastaSectionFound; } @Override public boolean hasNext() { if (this.end) { return false; } String line = null; this.result = new GFFEntry(); try { while ((line = this.reader.readLine()) != null) { if (line.startsWith("###")) { continue; } if (line.startsWith("##FASTA")) { this.fastaSectionFound = true; this.end = true; return false; } if (line.startsWith("##")) { final int posTab = line.indexOf(' '); if (posTab == -1) { continue; } final String mdKey = line.substring(2, posTab).trim(); final String mdValue = line.substring(posTab + 1).trim(); final List<String> list; if (!this.metadata.containsKey(mdKey)) { list = new ArrayList<>(); this.metadata.put(mdKey, list); } else { list = this.metadata.get(mdKey); } list.add(mdValue); } else if (line.startsWith("#")) { continue; } else { if (this.gff3Format) { this.result.parseGFF3(line.trim()); } else { this.result.parseGTF(line.trim()); } this.result.setId(this.count++); // Add metadata if not reuse result object this.result.addMetaDataEntries(this.metadata); this.nextCallDone = false; return true; } } } catch (IOException e) { this.ioException = e; } catch (BadBioEntryException e) { this.bbeException = e; } this.end = true; return false; } @Override public GFFEntry next() { if (this.nextCallDone) { throw new NoSuchElementException(); } this.nextCallDone = true; return this.result; } @Override public void remove() { throw new UnsupportedOperationException("Unsupported operation"); } /** * Close the stream. * @throws IOException if an error occurs while closing the file */ @Override public void close() throws IOException { this.reader.close(); } /** * Throw an exception if an exception has been caught while last hasNext() * method call. * @throws IOException if an exception has been caught while last hasNext() * method call * @throws BadBioEntryException if the last entry is not valid */ public void throwException() throws IOException, BadBioEntryException { if (this.ioException != null) { throw this.ioException; } if (this.bbeException != null) { throw this.bbeException; } } // // Protected methods // /** * Get the format of the data to read. * @return true if the data to read is in GFF format */ protected boolean isGFF3Format() { return this.gff3Format; } /** * Set the format of the data to read. * @param gffFormat true if the data to read is in GFF3 format */ protected void setGFF3Format(final boolean gffFormat) { this.gff3Format = gffFormat; } // // Constructors // /** * Public constructor * @param is InputStream to use */ public GFFReader(final InputStream is) { if (is == null) { throw new NullPointerException("InputStream is null"); } this.reader = new BufferedReader(new InputStreamReader(is, GFF_CHARSET)); } /** * Public constructor * @param file File to use */ public GFFReader(final File file) throws FileNotFoundException { if (file == null) { throw new NullPointerException("File is null"); } this.reader = FileUtils.createBufferedReader(file, GFF_CHARSET); } /** * Public constructor. * @param filename File to use */ public GFFReader(final String filename) throws FileNotFoundException { this.reader = FileUtils.createBufferedReader(filename, GFF_CHARSET); } }