/*
* The MIT License (MIT)
* Copyright (c) 2007-2015 by Institute for Computational Biomedicine,
* Weill Medical College of Cornell University.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.goby;
import edu.cornell.med.icb.goby.alignments.AlignmentReaderImpl;
import edu.cornell.med.icb.goby.alignments.Alignments;
import edu.cornell.med.icb.goby.exception.GobyRuntimeException;
import edu.cornell.med.icb.identifier.DoubleIndexedIdentifier;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.lang.MutableString;
import htsjdk.samtools.util.CloseableIterator;
import org.apache.log4j.Logger;
import org.broad.igv.sam.Alignment;
import java.io.IOException;
import java.util.NoSuchElementException;
/**
* An iterator over <a href="http://goby.campagnelab.org">Goby</a> alignment entries.
* IGV iterator return entries that fall within a window
* of genomic location. Implement this logic, leveraging sorted/indexed
* <a href="http://goby.campagnelab.org">Goby</a> alignments for speed.
* <p/>
* For further information about Goby, or to obtain sample alignment files, see http://goby.campagnelab.org
*
* @author Fabien Campagne
* Date: Jun 29, 2010
* Time: 12:01:40 PM
*/
public class GobyAlignmentIterator implements CloseableIterator<Alignment> {
private static final Logger LOG = Logger.getLogger(GobyAlignmentIterator.class);
private int targetIndex;
private int startReferencePosition;
private int endReferencePosition;
private final AlignmentReaderImpl reader;
protected final DoubleIndexedIdentifier indexToReferenceId;
private int previousPosition = Integer.MIN_VALUE;
private int previousReferenceIndex = -1;
private boolean useWindow;
// We need to record the sequence (chr), its redundant with the index but I'm not sure how to do the reverse lookup (JTR)
private String reference;
private int currentPosition;
/**
* Construct an iterator over the entire alignment file.
*
* @param reader Goby alignment reader.
* @param targetIdentifiers Bidirectional map from target index to target identifier/chromosome names.
*/
public GobyAlignmentIterator(AlignmentReaderImpl reader, DoubleIndexedIdentifier targetIdentifiers) {
this.reader = reader;
this.indexToReferenceId = targetIdentifiers;
startReferencePosition = 0;
endReferencePosition=Integer.MAX_VALUE;
previousPosition = Integer.MIN_VALUE;
useWindow = false;
}
Int2ObjectMap<ObjectArrayList<GobyAlignment>> spliceCache = new Int2ObjectOpenHashMap<ObjectArrayList<GobyAlignment>>();
/**
* Cache alignments that we will need to refer to later. This is useful to keep alignments that are linked
* across genomic positions by splice sites. The iterator only keeps alignments when configured with a window.
* This means that alignments are not cached when calling the iterator() method to get all entries.
*
* @param alignment The alignment to be cached.
* @return The list of gobyAlignments associated that have the same queryIndex as the cached alignment.
*/
public ObjectArrayList<GobyAlignment> cacheSpliceComponent(GobyAlignment alignment) {
if (useWindow) {
final int queryIndex = alignment.entry.getQueryIndex();
ObjectArrayList<GobyAlignment> list = spliceCache.get(queryIndex);
if (list == null) {
list = new ObjectArrayList<GobyAlignment>();
spliceCache.put(queryIndex, list);
}
list.add(alignment);
return list;
}
return null;
}
/**
* Construct an iterator over a window of the alignment file.
*
* @param reader Goby alignment reader.
* @param targetIdentifiers Bidirectional map from target index to target identifier/chromosome names.
* @param referenceIndex Index of the reference sequence/chromosome.
* @param start Minimum genomic location on the reference sequence that alignment entries must have to be returned.
* @param end Maximum genomic location on the reference sequence that alignment entries must have to be returned.
* @throws java.io.IOException If an error occurs reading the Goby alignment.
*/
public GobyAlignmentIterator(final AlignmentReaderImpl reader, final DoubleIndexedIdentifier targetIdentifiers,
int referenceIndex, String chr, int start, int end) throws IOException {
this(reader, targetIdentifiers);
this.useWindow = true;
if (referenceIndex != -1) {
this.targetIndex = referenceIndex;
this.reference = chr;
this.startReferencePosition = start;
this.endReferencePosition = end;
this.previousReferenceIndex = referenceIndex;
// LOG.debug(String.format("reposition %d %d", referenceIndex, start));
reader.reposition(referenceIndex, start);
}
}
/**
* A constructor useful only for testing. Do not use for production.
*
* @param targetIndex Index of the reference sequence over which the window is defined.
* @param start Start position
* @param end End position
* @throws IOException
*/
protected GobyAlignmentIterator(int targetIndex, int start, int end) throws IOException {
this.useWindow = true;
this.targetIndex = targetIndex;
this.startReferencePosition = start;
this.endReferencePosition = end;
this.reader = null;
this.indexToReferenceId = null;
}
/**
* Release resources used by this iterator. Do not close anything because we reused the Goby AlignmentReader (since Goby 1.9.6).
*/
public void close() {
// LOG.info("closing " + this);
}
/**
* Will hold the next entry, if it matches the window location criteria:
*/
private Alignments.AlignmentEntry nextEntry = null;
/**
* Determine if this iterator has more alignment entries in the given window.
*
* @return True if next() will return an alignment, False otherwise.
*/
public boolean hasNext() {
/* LOG.debug(String.format("previousPosition: %d endReferencePosition %d previousReferenceIndex %d targetIndex %d",
previousPosition, endReferencePosition, previousReferenceIndex, targetIndex)
); */
// Fetch the next entry with skipTo
if (nextEntry != null) return true;
try {
if (!useWindow) {
// all results are returned
if (!reader.hasNext()) return false;
nextEntry = reader.next();
} else {
// we return only within a window
nextEntry = reader.skipTo(targetIndex, startReferencePosition);
if (nextEntry == null ||
(nextEntry.getTargetIndex() != targetIndex ||
nextEntry.getPosition() < startReferencePosition ||
nextEntry.getPosition() > endReferencePosition)) {
// No next entry, on a different target sequence, or before the position of interest:
nextEntry = null;
}
}
} catch (IOException e) {
nextEntry = null;
LOG.error(e);
// throw new RuntimeException("IO error reading next Goby alignment entry", e);
return false;
} catch (GobyRuntimeException e) {
nextEntry = null;
LOG.error(e);
// throw new RuntimeException("IO error reading next Goby alignment entry", e);
return false;
}
final boolean result = nextEntry != null;
// LOG.debug("hasNext returning :" + result);
return result;
}
/**
* Return the next alignment.
*
* @return the next alignment within the window.
*/
public Alignment next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Alignments.AlignmentEntry entry;
entry = nextEntry;
nextEntry = null;
// LOG.debug(String.format("next targetIndex: %d position: %d", targetIndex, entry.getPosition()));
currentPosition = entry.getPosition();
return new GobyAlignment(this, entry);
}
/**
* This operation is not supported.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Return the reference sequence for this iterator
*
* @return The reference sequence
*/
String getReference() {
return reference;
}
public MutableString getId(int targetIndex) {
return indexToReferenceId.getId(targetIndex);
}
}