From ea80585ab3c50bd2ca453612d19e20bb1309c9de Mon Sep 17 00:00:00 2001 From: Len Trigg Date: Thu, 23 Jun 2016 14:08:42 +1200 Subject: [PATCH 001/137] Fix self-suppression problem with exception handling in AbstractAsyncWriter An exception thrown during synchronousWrite() by the underlying thread is typically picked up during write()'s invocation of checkAndRethrow(). Subsequent calling of close() (e.g. by try-with-resources) would result in an attempt to throw the same exception instance. This commit ensures that the underlying exception is only thrown once, by clearing the AtomicReference to the Throwable before throwing. Includes a unit test which demonstrates the problem. --- .../htsjdk/samtools/util/AbstractAsyncWriter.java | 7 +- .../java/htsjdk/samtools/util/AsyncWriterTest.java | 77 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/test/java/htsjdk/samtools/util/AsyncWriterTest.java diff --git a/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java b/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java index 86c18b9f9..ef1803bce 100644 --- a/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java +++ b/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java @@ -14,6 +14,10 @@ * NOTE: Objects of subclasses of this class are not intended to be shared between threads. * In particular there must be only one thread that calls {@link #write} and {@link #close}. * + * NOTE: Any exception thrown by the underlying Writer will be propagated back to the caller + * during the next available call to {@link #write} or {@link #close}. After the exception + * has been thrown to the caller, it is not safe to attempt further operations on the instance. + * * @author Tim Fennell */ public abstract class AbstractAsyncWriter implements Closeable { @@ -92,8 +96,9 @@ public void close() { * or RuntimeException as appropriate. */ private final void checkAndRethrow() { - final Throwable t = this.ex.get(); + final Throwable t = this.ex.getAndSet(null); if (t != null) { + this.isClosed.set(true); // Ensure no further attempts to write if (t instanceof Error) throw (Error) t; if (t instanceof RuntimeException) throw (RuntimeException) t; else throw new RuntimeException(t); diff --git a/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java b/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java new file mode 100644 index 000000000..c807ceffb --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java @@ -0,0 +1,77 @@ +/* + * The MIT License + * + * Copyright (c) 2016 Len Trigg + * + * 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 htsjdk.samtools.util; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class AsyncWriterTest { + private static class MyException extends RuntimeException { + final Integer item; + public MyException(Integer item) { + this.item = item; + } + } + private static class TestAsyncWriter extends AbstractAsyncWriter { + protected TestAsyncWriter() { + super(1); // Queue size of 1 to give us more control over the order of events + } + + @Override + protected String getThreadNamePrefix() { + return "TestAsyncWriter"; + } + + @Override + protected void synchronouslyWrite(Integer item) { + throw new MyException(item); + } + + @Override + protected void synchronouslyClose() { + // Nothing + } + } + @Test + public void testNoSelfSuppression() { + try (TestAsyncWriter t = new TestAsyncWriter()) { + try { + t.write(1); // Will trigger exception in writing thread + t.write(2); // Will block if the above write has not been executed, but may not trigger checkAndRethrow() + t.write(3); // Will trigger checkAndRethrow() if not already done by the above write + Assert.fail("Expected exception"); + } catch (MyException e) { + // Pre-bug fix, this was a "Self-suppression not permitted" exception from Java, rather than MyException + Assert.assertEquals(1, e.item.intValue()); + } + // Verify that attempts to write after exception will fail + try { + t.write(4); + Assert.fail("Expected exception"); + } catch (RuntimeIOException e) { + // Expected + } + } + } +} From c8720491595f80967cc6047b36017500f344dd46 Mon Sep 17 00:00:00 2001 From: droazen Date: Wed, 3 Aug 2016 17:15:42 -0400 Subject: [PATCH 002/137] Add mention of test.single to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 284fa9aa9..0e468d334 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Example gradle usage from the htsjdk root directory: ``` ./gradlew test + ./gradlew test -Dtest.single=TestClassName + ./gradlew test --tests htsjdk.variant.variantcontext.AlleleUnitTest ./gradlew test --tests "*AlleleUnitTest" From fba46371c71bf8ff7f3e2b56b97697de41a02f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Wed, 10 Aug 2016 15:05:36 +0200 Subject: [PATCH 003/137] FeatureCodec.getTabixFormat() to encapsulate tabix formatting (#669) * added getTabixFormat method to FeatureCodec interface to index files with tabix when the format is defined in implementors * changed as a default method in FeatureCodec * added javadoc param description * added tests and final getTabixFormat for binary codecs * addressed comments --- .../java/htsjdk/tribble/BinaryFeatureCodec.java | 9 +++++++ src/main/java/htsjdk/tribble/FeatureCodec.java | 14 ++++++++++ src/main/java/htsjdk/tribble/bed/BEDCodec.java | 5 ++++ .../java/htsjdk/tribble/index/IndexFactory.java | 31 +++++++++++++++++++--- .../java/htsjdk/variant/vcf/AbstractVCFCodec.java | 6 +++++ .../java/htsjdk/tribble/BinaryFeaturesTest.java | 5 ++++ src/test/java/htsjdk/tribble/bed/BEDCodecTest.java | 5 ++++ .../htsjdk/variant/vcf/AbstractVCFCodecTest.java | 7 +++++ 8 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/tribble/BinaryFeatureCodec.java b/src/main/java/htsjdk/tribble/BinaryFeatureCodec.java index dfe1c9174..dbd0afc47 100644 --- a/src/main/java/htsjdk/tribble/BinaryFeatureCodec.java +++ b/src/main/java/htsjdk/tribble/BinaryFeatureCodec.java @@ -3,6 +3,7 @@ import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.LocationAware; import htsjdk.samtools.util.RuntimeIOException; +import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.readers.PositionalBufferedStream; import java.io.IOException; @@ -40,4 +41,12 @@ public boolean isDone(final PositionalBufferedStream source) { throw new RuntimeIOException("Failure reading from stream.", e); } } + + /** + * Marked as final because binary features could not be tabix indexed + */ + @Override + public final TabixFormat getTabixFormat() { + throw new TribbleException("Binary codecs does not support tabix"); + } } diff --git a/src/main/java/htsjdk/tribble/FeatureCodec.java b/src/main/java/htsjdk/tribble/FeatureCodec.java index b45d8cf8c..f14191a67 100644 --- a/src/main/java/htsjdk/tribble/FeatureCodec.java +++ b/src/main/java/htsjdk/tribble/FeatureCodec.java @@ -19,6 +19,7 @@ package htsjdk.tribble; import htsjdk.samtools.util.LocationAware; +import htsjdk.tribble.index.tabix.TabixFormat; import java.io.IOException; import java.io.InputStream; @@ -119,4 +120,17 @@ * @return true if potentialInput can be parsed, false otherwise */ public boolean canDecode(final String path); + + /** + * Define the tabix format for the feature, used for indexing. Default implementation throws an exception. + * + * Note that only {@link AsciiFeatureCodec} could read tabix files as defined in + * {@link AbstractFeatureReader#getFeatureReader(String, String, FeatureCodec, boolean)} + * + * @return the format to use with tabix + * @throws TribbleException if the format is not defined + */ + default public TabixFormat getTabixFormat() { + throw new TribbleException(this.getClass().getSimpleName() + "does not have defined tabix format"); + } } diff --git a/src/main/java/htsjdk/tribble/bed/BEDCodec.java b/src/main/java/htsjdk/tribble/bed/BEDCodec.java index 0e9185025..62d202c19 100644 --- a/src/main/java/htsjdk/tribble/bed/BEDCodec.java +++ b/src/main/java/htsjdk/tribble/bed/BEDCodec.java @@ -25,6 +25,7 @@ import htsjdk.tribble.AsciiFeatureCodec; import htsjdk.tribble.annotation.Strand; +import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.readers.LineIterator; import htsjdk.tribble.util.ParsingUtils; @@ -224,4 +225,8 @@ public int value() { } } + @Override + public TabixFormat getTabixFormat() { + return TabixFormat.BED; + } } diff --git a/src/main/java/htsjdk/tribble/index/IndexFactory.java b/src/main/java/htsjdk/tribble/index/IndexFactory.java index 85fbd72c7..a588220dc 100644 --- a/src/main/java/htsjdk/tribble/index/IndexFactory.java +++ b/src/main/java/htsjdk/tribble/index/IndexFactory.java @@ -260,11 +260,25 @@ public static LinearIndex createLinearIndex(final File inputFile, final FeatureC public static Index createIndex(final File inputFile, final FeatureCodec codec, final IndexType type) { + return createIndex(inputFile, codec, type, null); + } + + /** + * Create an index of the specified type with default binning parameters + * + * @param inputFile the input file to load features from + * @param codec the codec to use for decoding records + * @param type the type of index to create + * @param sequenceDictionary May be null, but if present may reduce memory footprint for tabix index creation + */ + public static Index createIndex(final File inputFile, + final FeatureCodec codec, + final IndexType type, + final SAMSequenceDictionary sequenceDictionary) { switch (type) { case INTERVAL_TREE: return createIntervalIndex(inputFile, codec); case LINEAR: return createLinearIndex(inputFile, codec); - // Tabix index initialization requires additional information, so this construction method won't work. - case TABIX: throw new UnsupportedOperationException("Tabix indices cannot be created through a generic interface"); + case TABIX: return createTabixIndex(inputFile, codec, sequenceDictionary); } throw new IllegalArgumentException("Unrecognized IndexType " + type); } @@ -318,7 +332,18 @@ public static void writeIndex(final Index idx, final File idxFile) throws IOExce return (TabixIndex)createIndex(inputFile, new FeatureIterator(inputFile, codec), indexCreator); } - + /** + * @param inputFile The file to be indexed. + * @param codec the codec to use for decoding records + * @param sequenceDictionary May be null, but if present may reduce memory footprint for index creation. Features + * in inputFile must be in the order defined by sequenceDictionary, if it is present. + * + */ + public static TabixIndex createTabixIndex(final File inputFile, + final FeatureCodec codec, + final SAMSequenceDictionary sequenceDictionary) { + return createTabixIndex(inputFile, codec, codec.getTabixFormat(), sequenceDictionary); + } private static Index createIndex(final File inputFile, final FeatureIterator iterator, final IndexCreator creator) { Feature lastFeature = null; diff --git a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java index 7b157ca7c..16857b4e6 100644 --- a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java +++ b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java @@ -30,6 +30,7 @@ import htsjdk.tribble.Feature; import htsjdk.tribble.NameAwareCodec; import htsjdk.tribble.TribbleException; +import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.util.ParsingUtils; import htsjdk.variant.utils.GeneralUtils; import htsjdk.variant.variantcontext.Allele; @@ -782,4 +783,9 @@ protected void generateException(String message) { protected static void generateException(String message, int lineNo) { throw new TribbleException(String.format("The provided VCF file is malformed at approximately line number %d: %s", lineNo, message)); } + + @Override + public TabixFormat getTabixFormat() { + return TabixFormat.VCF; + } } diff --git a/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java b/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java index 946609725..eff8939d8 100644 --- a/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java +++ b/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java @@ -54,4 +54,9 @@ public void testBinaryCodec(final File source, final FeatureCodec Date: Wed, 10 Aug 2016 21:18:39 +0200 Subject: [PATCH 004/137] Fix #653 (#672) * added checking for AbstractFeatureReader.BLOCK_COMPRESSED_EXTENSIONS in TribbleIndexedFeatureReader * fixing WFIterator checking of compressed file * isGZIPPath deprecation * added test for new functionality * fixing URL encoding * added no-remote test file with spaces --- .../tribble/TribbleIndexedFeatureReader.java | 10 +++++++-- .../tribble/TribbleIndexFeatureReaderTest.java | 4 +++- .../resources/htsjdk/tribble/test with spaces.vcf | 24 +++++++++++++++++++++ src/test/resources/htsjdk/tribble/test.vcf.bgz | Bin 0 -> 849 bytes 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/test/resources/htsjdk/tribble/test with spaces.vcf create mode 100644 src/test/resources/htsjdk/tribble/test.vcf.bgz diff --git a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java index ae278f40a..514782d1e 100644 --- a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java +++ b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java @@ -33,10 +33,12 @@ import htsjdk.tribble.util.ParsingUtils; import java.io.BufferedInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -217,7 +219,7 @@ private void readHeader() throws IOException { PositionalBufferedStream pbs = null; try { is = ParsingUtils.openInputStream(path); - if (isGZIPPath(path)) { + if (hasBlockCompressedExtension(new URI(URLEncoder.encode(path, "UTF-8")))) { // TODO -- warning I don't think this can work, the buffered input stream screws up position is = new GZIPInputStream(new BufferedInputStream(is)); } @@ -273,7 +275,11 @@ private void readHeader() throws IOException { return new WFIterator(); } + /** + * @deprecated use {@link #hasBlockCompressedExtension(String)} instead + */ //Visible for testing + @Deprecated static boolean isGZIPPath(final String path) { if (path.toLowerCase().endsWith(".gz")) { return true; @@ -310,7 +316,7 @@ public WFIterator() throws IOException { final InputStream inputStream = ParsingUtils.openInputStream(path); final PositionalBufferedStream pbs; - if (isGZIPPath(path)) { + if (hasBlockCompressedExtension(path)) { // Gzipped -- we need to buffer the GZIPInputStream methods as this class makes read() calls, // and seekableStream does not support single byte reads final InputStream is = new GZIPInputStream(new BufferedInputStream(inputStream, 512000)); diff --git a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java index 76bd41068..afdd827e6 100644 --- a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java @@ -64,7 +64,9 @@ public void testGZExtension(final String testString, final boolean expected) thr public Object[][] createFeatureFileStrings() { return new Object[][]{ {TestUtils.DATA_DIR + "test.vcf", 5}, - {TestUtils.DATA_DIR + "test.vcf.gz", 5} + {TestUtils.DATA_DIR + "test.vcf.gz", 5}, + {TestUtils.DATA_DIR + "test.vcf.bgz", 5}, + {TestUtils.DATA_DIR + "test with spaces.vcf", 5} }; } diff --git a/src/test/resources/htsjdk/tribble/test with spaces.vcf b/src/test/resources/htsjdk/tribble/test with spaces.vcf new file mode 100644 index 000000000..27d45004c --- /dev/null +++ b/src/test/resources/htsjdk/tribble/test with spaces.vcf @@ -0,0 +1,24 @@ +##fileformat=VCFv4.1 +##fileDate=20090805 +##source=myImputationProgramV3.1 +##reference=file:///seq/references/1000GenomesPilot-NCBI36.fasta +##contig= +##phasing=partial +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##FILTER= +##FILTER= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NA00001 NA00002 NA00003 +20 14370 rs6054257 G A 29 PASS NS=3;DP=14;AF=0.5;DB;H2 GT:GQ:DP:HQ 0|0:48:1:51,51 1|0:48:8:51,51 1/1:43:5:.,. +20 17330 . T A 3 q10 NS=3;DP=11;AF=0.017 GT:GQ:DP:HQ 0|0:49:3:58,50 0|1:3:5:65,3 0/0:41:3 +20 1110696 rs6040355 A G,T 67 PASS NS=2;DP=10;AF=0.333,0.667;AA=T;DB GT:GQ:DP:HQ 1|2:21:6:23,27 2|1:2:0:18,2 2/2:35:4 +20 1230237 . T . 47 PASS NS=3;DP=13;AA=T GT:GQ:DP:HQ 0|0:54:7:56,60 0|0:48:4:51,51 0/0:61:2 +20 1234567 microsat1 GTC G,GTCT 50 PASS NS=3;DP=9;AA=G GT:GQ:DP 0/1:35:4 0/2:17:2 1/1:40:3 diff --git a/src/test/resources/htsjdk/tribble/test.vcf.bgz b/src/test/resources/htsjdk/tribble/test.vcf.bgz new file mode 100644 index 0000000000000000000000000000000000000000..44072dc94d11a640ed3c171ebe74ed1b43a75b55 GIT binary patch literal 849 zcmV-X1FrlZiwFb&00000{{{d;LjnLa1C5hiZ{j!X-=_xDMfsidrPY5ebx(GPckzudQ* zp)|@T2#wJQF}tVBQ@vAuexB#m$w-ry#j-9BwamZMR#dA*)vB;(8o3w_hg!W3-*)v7 zBZQ`^D08KkX;zxw7mp8f$_I(mMq0qGEKGXv|Coo?7iOwBn4`~$OWByaP-?C6ZFcq_ zD2Mr;`3WX`w@*frE41?x>W%jtkCKG*t=f{kgt}6@RB8WiR_3LnWtFNz-$5gPmbQqq z|MROgt&UQs#lf#+ZBm)po%teqX^U8F;Np~TRqbOKpDX3h3!@IIhN05CI<1-r{JZXm zQzp(v=2fP&^H*w)j*U;=eSMD$m!HAKrIDEvs>&R1@% zPd4*&j}Y_<*qGY3-a|B_GPSLtgY|lNxFXQC2-ySdx0}V%$yIYoAJYmt+f$nD9Zbfz ztQq-V)=XBNXMY`I^s;^q;ubNDKapc|(T)B1LA?6REx4+kOFMna=kC&4$^E;l^iDr} ztAhN8-o~GODC2Rqet8DVmkpSQVEruu!BY%Ye+EyWOBpl+2AvmJ1ol6V@a0J^Px^%* zfL-b#P-~8uOPB|yAOK_xmceEN78{>V!ljShNf7yHz$W3tWJbU=7SojomtwX8=ob?1 zNMOM*WEkLXIl3-~Sh!R$F@S^C&7%~70f?;&1@_kb>w!BDguRdav9OF0WXLXY^Da1p z6riE~X4fqlW5mb2?ZictG3zjeG2q@?FQoNBoe!lHq5B!CQwpiH=}E1`%`ukC06T(6fKZQ&pKVXp85E^L?C z^7C|8ms%QZIghp{`x^rr#f{atahkq4SrBfEv@b+pPmpdD$cFU~v{81FZ3X}UABzYC b000000RIL6LPG)o8vp|U0000000000n9!Tm literal 0 HcmV?d00001 From a781afa9597dcdbcde0020bfe464abee269b3b2e Mon Sep 17 00:00:00 2001 From: Chris Norman Date: Tue, 26 Jul 2016 17:43:31 -0400 Subject: [PATCH 005/137] Normalize CRAM reference bases and throw on MD5 validation failure. --- src/main/java/htsjdk/samtools/CRAMIterator.java | 12 +++++--- .../java/htsjdk/samtools/cram/build/Utils.java | 4 ++- .../samtools/cram/ref/CRAMReferenceSource.java | 4 +-- .../htsjdk/samtools/cram/ref/ReferenceSource.java | 22 +++++++++----- .../java/htsjdk/samtools/CRAMCRAIIndexerTest.java | 5 ++++ .../htsjdk/samtools/cram/structure/SliceTests.java | 34 ++++++++++++++++++++++ .../htsjdk/samtools/cram/auxf.alteredForMD5test.fa | 2 ++ .../samtools/cram/auxf.alteredForMD5test.fa.fai | 1 + 8 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa create mode 100644 src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa.fai diff --git a/src/main/java/htsjdk/samtools/CRAMIterator.java b/src/main/java/htsjdk/samtools/CRAMIterator.java index 4238677eb..f8179e689 100644 --- a/src/main/java/htsjdk/samtools/CRAMIterator.java +++ b/src/main/java/htsjdk/samtools/CRAMIterator.java @@ -177,10 +177,14 @@ void nextContainer() throws IOException, IllegalArgumentException, final Slice slice = container.slices[i]; if (slice.sequenceId < 0) continue; - if (validationStringency != ValidationStringency.SILENT && !slice.validateRefMD5(refs)) { - log.error(String - .format("Reference sequence MD5 mismatch for slice: seq id %d, start %d, span %d, expected MD5 %s", slice.sequenceId, - slice.alignmentStart, slice.alignmentSpan, String.format("%032x", new BigInteger(1, slice.refMD5)))); + if (!slice.validateRefMD5(refs)) { + final String msg = String.format( + "Reference sequence MD5 mismatch for slice: sequence id %d, start %d, span %d, expected MD5 %s", + slice.sequenceId, + slice.alignmentStart, + slice.alignmentSpan, + String.format("%032x", new BigInteger(1, slice.refMD5))); + throw new CRAMException(msg); } } diff --git a/src/main/java/htsjdk/samtools/cram/build/Utils.java b/src/main/java/htsjdk/samtools/cram/build/Utils.java index 60efc3b9d..7fdeebac8 100644 --- a/src/main/java/htsjdk/samtools/cram/build/Utils.java +++ b/src/main/java/htsjdk/samtools/cram/build/Utils.java @@ -17,7 +17,9 @@ */ package htsjdk.samtools.cram.build; -class Utils { +final public class Utils { + + private Utils() {}; /** * CRAM operates with upper case bases, so both read and ref bases should be diff --git a/src/main/java/htsjdk/samtools/cram/ref/CRAMReferenceSource.java b/src/main/java/htsjdk/samtools/cram/ref/CRAMReferenceSource.java index 35a3e7918..c77aaae0f 100644 --- a/src/main/java/htsjdk/samtools/cram/ref/CRAMReferenceSource.java +++ b/src/main/java/htsjdk/samtools/cram/ref/CRAMReferenceSource.java @@ -15,8 +15,8 @@ * against the reference by using common name variations, * such as adding or removing a leading "chr" prefix * from the requested name. if false, use exact match - * @return the bases representing the requested sequence. or null if the sequence - * cannot be found + * @return the upper cased, normalized (see {@link htsjdk.samtools.cram.build.Utils#normalizeBase}) + * bases representing the requested sequence, or null if the sequence cannot be found */ byte[] getReferenceBases(final SAMSequenceRecord sequenceRecord, final boolean tryNameVariants); } diff --git a/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java b/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java index da3d43f89..e73fb4155 100644 --- a/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java +++ b/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java @@ -20,6 +20,7 @@ import htsjdk.samtools.Defaults; import htsjdk.samtools.SAMException; import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.cram.build.Utils; import htsjdk.samtools.cram.io.InputStreamUtils; import htsjdk.samtools.reference.ReferenceSequence; import htsjdk.samtools.reference.ReferenceSequenceFile; @@ -123,13 +124,23 @@ public void clearCache() { return null; } + // Upper case and normalize (-> ACGTN) in-place, and add to the cache + private byte[] addToCache(final String sequenceName, final byte[] bases) { + for (int i = 0; i < bases.length; i++) { + bases[i] = Utils.normalizeBase(bases[i]); + } + cacheW.put(sequenceName, new WeakReference(bases)); + return bases; + } + public synchronized byte[] getReferenceBases(final SAMSequenceRecord record, final boolean tryNameVariants) { { // check cache by sequence name: final String name = record.getSequenceName(); final byte[] bases = findInCache(name); - if (bases != null) + if (bases != null) { return bases; + } } final String md5 = record.getAttribute(SAMSequenceRecord.MD5_TAG); @@ -152,10 +163,7 @@ public void clearCache() { { // try to fetch sequence by name: bases = findBasesByName(record.getSequenceName(), tryNameVariants); if (bases != null) { - SequenceUtil.upperCase(bases); - cacheW.put(record.getSequenceName(), new WeakReference( - bases)); - return bases; + return addToCache(record.getSequenceName(), bases); } } @@ -169,9 +177,7 @@ public void clearCache() { } } if (bases != null) { - SequenceUtil.upperCase(bases); - cacheW.put(md5, new WeakReference(bases)); - return bases; + return addToCache(md5, bases); } } } diff --git a/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java b/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java index 19284b202..11d2f3ce9 100644 --- a/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java +++ b/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java @@ -30,6 +30,11 @@ public void testCRAIIndexerFromContainer() throws IOException { refSource, ValidationStringency.STRICT); SAMFileHeader samHeader = reader.getFileHeader(); + Iterator it = reader.getIterator(); + while(it.hasNext()) { + SAMRecord samRec = it.next(); + } + reader.close(); FileInputStream fis = new FileInputStream(CRAMFile); diff --git a/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java b/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java index a5c16697a..c52dccba1 100644 --- a/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java +++ b/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java @@ -1,10 +1,19 @@ package htsjdk.samtools.cram.structure; +import htsjdk.samtools.CRAMFileReader; +import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.ValidationStringency; +import htsjdk.samtools.cram.CRAMException; +import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.util.SequenceUtil; import org.testng.Assert; import org.testng.annotations.Test; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + /** * Created by vadim on 07/12/2015. */ @@ -33,4 +42,29 @@ public void test_validateRef() { Assert.assertEquals(slice.refMD5, md5); Assert.assertTrue(slice.validateRefMD5(ref)); } + + @Test(expectedExceptions= CRAMException.class) + public void testFailsMD5Check() throws IOException { + // auxf.alteredForMD5test.fa has been altered slightly from the original reference + // to cause the CRAM md5 check to fail + final File CRAMFile = new File("src/test/resources/htsjdk/samtools/cram/auxf#values.3.0.cram"); + final File refFile = new File("src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa"); + ReferenceSource refSource = new ReferenceSource(refFile); + CRAMFileReader reader = null; + try { + reader = new CRAMFileReader( + CRAMFile, + null, + refSource, + ValidationStringency.STRICT); + Iterator it = reader.getIterator(); + while (it.hasNext()) { + it.next(); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } } diff --git a/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa b/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa new file mode 100644 index 000000000..108924022 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa @@ -0,0 +1,2 @@ +>Sheila +CTAGCTCAGAAAAAAAAAA diff --git a/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa.fai b/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa.fai new file mode 100644 index 000000000..570928880 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/cram/auxf.alteredForMD5test.fa.fai @@ -0,0 +1 @@ +Sheila 19 8 19 20 From 0e17e07eb8f3432764e480f55077bdc36151cee4 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Fri, 19 Aug 2016 15:16:04 -0400 Subject: [PATCH 006/137] Added a "RANDOM" duplicate scoring strategy. (#688) --- src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java index 9d0bed5cb..1abd51473 100644 --- a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java +++ b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java @@ -24,6 +24,8 @@ package htsjdk.samtools; +import htsjdk.samtools.util.Murmur3; + /** * This class helps us compute and compare duplicate scores, which are used for selecting the non-duplicate * during duplicate marking (see MarkDuplicates). @@ -33,9 +35,13 @@ public enum ScoringStrategy { SUM_OF_BASE_QUALITIES, - TOTAL_MAPPED_REFERENCE_LENGTH + TOTAL_MAPPED_REFERENCE_LENGTH, + RANDOM, } + /** Hash used for the RANDOM scoring strategy. */ + private static final Murmur3 hasher = new Murmur3(1); + /** An enum to use for storing temporary attributes on SAMRecords. */ private static enum Attr { DuplicateScore } @@ -80,6 +86,8 @@ public static short computeDuplicateScore(final SAMRecord record, final ScoringS score += SAMUtils.getMateCigar(record).getReferenceLength(); } break; + case RANDOM: + score += (short) (hasher.hashUnencodedChars(record.getReadName()) >> 16); } storedScore = score; From b81d0366a2da133f184aa329651c5bda0d06d2e2 Mon Sep 17 00:00:00 2001 From: Chris Norman Date: Fri, 19 Aug 2016 17:46:50 -0400 Subject: [PATCH 007/137] Remove unreferenced test files. (#687) removing unused test files --- src/test/resources/htsjdk/samtools/cram/auxf.fasta | 2 -- src/test/resources/htsjdk/samtools/cram/test.fasta | 2 -- src/test/resources/htsjdk/samtools/cram/test2.fasta | 2 -- 3 files changed, 6 deletions(-) delete mode 100644 src/test/resources/htsjdk/samtools/cram/auxf.fasta delete mode 100644 src/test/resources/htsjdk/samtools/cram/test.fasta delete mode 100644 src/test/resources/htsjdk/samtools/cram/test2.fasta diff --git a/src/test/resources/htsjdk/samtools/cram/auxf.fasta b/src/test/resources/htsjdk/samtools/cram/auxf.fasta deleted file mode 100644 index 11d25dda6..000000000 --- a/src/test/resources/htsjdk/samtools/cram/auxf.fasta +++ /dev/null @@ -1,2 +0,0 @@ ->Sheila -GCTAGCTCAGAAAAAAAAAA diff --git a/src/test/resources/htsjdk/samtools/cram/test.fasta b/src/test/resources/htsjdk/samtools/cram/test.fasta deleted file mode 100644 index 11d25dda6..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test.fasta +++ /dev/null @@ -1,2 +0,0 @@ ->Sheila -GCTAGCTCAGAAAAAAAAAA diff --git a/src/test/resources/htsjdk/samtools/cram/test2.fasta b/src/test/resources/htsjdk/samtools/cram/test2.fasta deleted file mode 100644 index 11d25dda6..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test2.fasta +++ /dev/null @@ -1,2 +0,0 @@ ->Sheila -GCTAGCTCAGAAAAAAAAAA From 0d0da8774eb86b1ec594cb485e2d300a9b362f4a Mon Sep 17 00:00:00 2001 From: Chris Norman Date: Tue, 23 Aug 2016 14:23:58 -0400 Subject: [PATCH 008/137] Throw on unrecognized CRAM encoding tag. (#593) --- .../java/htsjdk/samtools/cram/encoding/reader/DataReaderFactory.java | 4 +--- src/main/java/htsjdk/samtools/cram/structure/CompressionHeader.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/samtools/cram/encoding/reader/DataReaderFactory.java b/src/main/java/htsjdk/samtools/cram/encoding/reader/DataReaderFactory.java index 4e5c4ec02..253ab1521 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/reader/DataReaderFactory.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/reader/DataReaderFactory.java @@ -56,9 +56,7 @@ public AbstractReader buildReader(final AbstractReader reader, final DataSeries dataSeries = field.getAnnotation(DataSeries.class); final EncodingKey key = dataSeries.key(); final DataSeriesType type = dataSeries.type(); - if (header.encodingMap.get(key) == null) { - log.debug("Encoding not found for key: " + key); - } else { + if (header.encodingMap.get(key) != null) { try { field.set(reader, createReader(type, header.encodingMap.get(key), bitInputStream, inputMap)); diff --git a/src/main/java/htsjdk/samtools/cram/structure/CompressionHeader.java b/src/main/java/htsjdk/samtools/cram/structure/CompressionHeader.java index 323b3f690..2278bf17e 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/CompressionHeader.java +++ b/src/main/java/htsjdk/samtools/cram/structure/CompressionHeader.java @@ -17,6 +17,7 @@ */ package htsjdk.samtools.cram.structure; +import htsjdk.samtools.cram.CRAMException; import htsjdk.samtools.cram.encoding.ExternalCompressor; import htsjdk.samtools.cram.encoding.NullEncoding; import htsjdk.samtools.cram.io.ITF8; @@ -170,8 +171,7 @@ else if (TD_tagIdsDictionary.equals(key)) { final String key = new String(new byte[]{buffer.get(), buffer.get()}); final EncodingKey encodingKey = EncodingKey.byFirstTwoChars(key); if (encodingKey == null) { - log.debug("Unknown encoding key: " + key); - continue; + throw new CRAMException("Unknown encoding key: " + key); } final EncodingID id = EncodingID.values()[buffer.get()]; From 275cfe04841f15694633c72806742bc4fb9385a3 Mon Sep 17 00:00:00 2001 From: Chris Norman Date: Wed, 24 Aug 2016 13:31:20 -0400 Subject: [PATCH 009/137] Removed unused .bcf test files. (#691) remove 2 unreferenced and possibly invalid BCF files --- src/test/resources/htsjdk/variant/ex2.bgzf.bcf | Bin 1062 -> 0 bytes src/test/resources/htsjdk/variant/ex2.uncompressed.bcf | Bin 1892 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100755 src/test/resources/htsjdk/variant/ex2.bgzf.bcf delete mode 100755 src/test/resources/htsjdk/variant/ex2.uncompressed.bcf diff --git a/src/test/resources/htsjdk/variant/ex2.bgzf.bcf b/src/test/resources/htsjdk/variant/ex2.bgzf.bcf deleted file mode 100755 index eaa40afc68a5dbbb0f88f793bd965f6385bb8011..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1062 zcmV+>1lju^iwFb&00000{{{d;LjnK^1dWtUZ{tK5$6eb=*I5?fYO4q#t)`7uuxyh0 zC3e!K?#fA;7pYC!CM?|0*d8aA>^SwfUCIGcA?<}bd*P16jW2+>aDywKfip)=j9*d` zgV@@VXEgIXzyI^h{F7#@L(@OyD5_Ml9T(Yt;K5LP(dt~)s$!v3>hzDtN9Wo>zpb6> zqfxnyuo*b>(D7&5uI{>kWQ8ceU=A@x)^54q{!|kVNoIg-6rh=jv?MYTsp1=L}@=EDr>}^^hRP2&QIW6T45$2 zbsaQ386BN1Vb!%hqv-nKy1g$%VO{3z61q-!4GiS^FM%i|B8^NMRx)YF2xAaV;S4Ck zWa3|64B_x}v!-Op3>@Fr-LO|MK3KKK1yE*n8 zK`S+ECck%EjPyVG!x8oorc(TjLOQUQkmE$R0N}uCAECdgELEu=AbeJm!RSU^tn_eQSo?X z+HcA+R&uJ7FP76nsnt6lo^Yqb5!Y{X=SLk*KOS>uAM3|lVlpl=G?yF%H_*v{rXZ%H zl#X(NI-n@(eJXi&8Wgob#@~MZL(fq(#ZXl4b%0etsYyz`*e&W5zriy+&A-hv+a&Ng zze({qiZATY3`?T<`@A4LCHouqa{OC6?>-Xc#e#pZQ+T+z*|?EN#-mi~$5tY>K`p*w z6DfAHI439W9E?6SB$V7$WdY(it|_Ug3WK=Q}Z%qFyy~ zgc@<<7HWd5C`Fw#G3KK#Xz~_#=J8)&{&4v9&mTVLS+Wtg!fxhs`8-kDrb{F!Pq9R) z_}&&>c=%*R>APRHQl*0?LuP3Q{PKt%#KgGpoq g03VA81ONa4009360763o02=@U00000000000GF5KYC-@ut8CtiP~o8(Z|kuy;|^LyUsooC!^b!htg97UB% zj_YA32z?akFI%0Hhvc!RPxo|B`)46vZx{1wJMxt`|g=L95wUw5o$h zgaBX#v&fw!(z00g@N5!Ibxp1*>VZ}($8x^uUFl6lEBm&pJCdwfw$nfw7OlFY)DLV; zYB-Ljnb?$VR3>w5xtQpCy}%Cyg61xsk(Gk`gU&Ef8;r_>3*W?{E|teubFBAg5uV_1 z-F!Ml2pnOA{JDpT@P!*qg*J*%3YFq6+(6qW?*bhM5%PpKo=4LZBCkP=&Jtpzm36!T zMeB9JreF^Xoe-a2K>M#!jYbt+T?o@q1fxmk=Ryn8A{~uE5?cv1b_B zSztE|^=^uhZ#hP=nQ->O(nd8&@K>s74UU%m$@X%2D&x(Rl6;$%69s`szPfbrtCtYB z_$?(?<*HZ-Ev=#)R3@S=)h*L{GL4Amnw6D@YGvYBNEOWnwcDVmEg1j&<8LEJ(G){bxi=xvM71WX^ Date: Thu, 25 Aug 2016 16:30:12 +0200 Subject: [PATCH 010/137] added function to extract SA tag and return a list of supplementary alignments (#685) added a new function SAMUtils.getOtherCanonicalAlignments This function is used to extract the 'SA' tag of a SAMRecord as a Listof supplementary alignments. --- src/main/java/htsjdk/samtools/SAMUtils.java | 124 ++++++++++++++++++++++++ src/test/java/htsjdk/samtools/SAMUtilsTest.java | 77 ++++++++++++++- 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/SAMUtils.java b/src/main/java/htsjdk/samtools/SAMUtils.java index b31b7718c..c0432acc8 100644 --- a/src/main/java/htsjdk/samtools/SAMUtils.java +++ b/src/main/java/htsjdk/samtools/SAMUtils.java @@ -41,12 +41,18 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.regex.Pattern; /** * Utilty methods. */ public final class SAMUtils { + /** regex for semicolon, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} */ + private static final Pattern SEMICOLON_PAT = Pattern.compile("[;]"); + /** regex for comma, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} */ + private static final Pattern COMMA_PAT = Pattern.compile("[,]"); + // Representation of bases, one for when in low-order nybble, one for when in high-order nybble. private static final byte COMPRESSED_EQUAL_LOW = 0; private static final byte COMPRESSED_A_LOW = 1; @@ -1096,4 +1102,122 @@ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, fina public static boolean isValidUnsignedIntegerAttribute(long value) { return value >= 0 && value <= BinaryCodec.MAX_UINT; } + + /** + * Extract a List of 'other canonical alignments' from a SAM record. Those alignments are stored as a string in the 'SA' tag as defined + * in the SAM specification. + * The name, sequence and qualities, mate data are copied from the original record. + * @param record must be non null and must have a non-null associated header. + * @return a list of 'other canonical alignments' SAMRecords. The list is empty if the 'SA' attribute is missing. + */ + public static List getOtherCanonicalAlignments(final SAMRecord record) { + if( record == null ) throw new IllegalArgumentException("record is null"); + if( record.getHeader() == null ) throw new IllegalArgumentException("record.getHeader() is null"); + /* extract value of SA tag */ + final Object saValue = record.getAttribute( SAMTagUtil.getSingleton().SA ); + if( saValue == null ) return Collections.emptyList(); + if( ! (saValue instanceof String) ) throw new SAMException( + "Expected a String for attribute 'SA' but got " + saValue.getClass() ); + + final SAMRecordFactory samReaderFactory = new DefaultSAMRecordFactory(); + + /* the spec says: "Other canonical alignments in a chimeric alignment, formatted as a + * semicolon-delimited list: (rname,pos,strand,CIGAR,mapQ,NM;)+. + * Each element in the list represents a part of the chimeric alignment. + * Conventionally, at a supplementary line, the 1rst element points to the primary line. + */ + + /* break string using semicolon */ + final String semiColonStrs[] = SEMICOLON_PAT.split((String)saValue); + + /* the result list */ + final List alignments = new ArrayList<>( semiColonStrs.length ); + + /* base SAM flag */ + int record_flag = record.getFlags() ; + record_flag &= ~SAMFlag.PROPER_PAIR.flag; + record_flag &= ~SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; + record_flag &= ~SAMFlag.READ_REVERSE_STRAND.flag; + + + for(int i=0; i< semiColonStrs.length;++i ) { + final String semiColonStr = semiColonStrs[i]; + /* ignore empty string */ + if( semiColonStr.isEmpty() ) continue; + + /* break string using comma */ + final String commaStrs[] = COMMA_PAT.split(semiColonStr); + if( commaStrs.length != 6 ) throw new SAMException("Bad 'SA' attribute in " + semiColonStr); + + /* create the new record */ + final SAMRecord otherRec = samReaderFactory.createSAMRecord( record.getHeader() ); + + /* copy fields from the original record */ + otherRec.setReadName( record.getReadName() ); + otherRec.setReadBases( record.getReadBases() ); + otherRec.setBaseQualities( record.getBaseQualities() ); + if( record.getReadPairedFlag() && !record.getMateUnmappedFlag()) { + otherRec.setMateReferenceIndex( record.getMateReferenceIndex() ); + otherRec.setMateAlignmentStart( record.getMateAlignmentStart() ); + } + + + /* get reference sequence */ + final int tid = record.getHeader().getSequenceIndex( commaStrs[0] ); + if( tid == -1 ) throw new SAMException("Unknown contig in " + semiColonStr); + otherRec.setReferenceIndex( tid ); + + /* fill POS */ + final int alignStart; + try { + alignStart = Integer.parseInt(commaStrs[1]); + } catch( final NumberFormatException err ) { + throw new SAMException("bad POS in "+semiColonStr, err); + } + + otherRec.setAlignmentStart( alignStart ); + + /* set TLEN */ + if( record.getReadPairedFlag() && + !record.getMateUnmappedFlag() && + record.getMateReferenceIndex() == tid ) { + otherRec.setInferredInsertSize( record.getMateAlignmentStart() - alignStart ); + } + + /* set FLAG */ + int other_flag = record_flag; + other_flag |= (commaStrs[2].equals("+") ? 0 : SAMFlag.READ_REVERSE_STRAND.flag) ; + /* spec: Conventionally, at a supplementary line, the 1st element points to the primary line */ + if( !( record.getSupplementaryAlignmentFlag() && i==0 ) ) { + other_flag |= SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; + } + otherRec.setFlags(other_flag); + + /* set CIGAR */ + otherRec.setCigar( TextCigarCodec.decode( commaStrs[3] ) ); + + /* set MAPQ */ + try { + otherRec.setMappingQuality( Integer.parseInt(commaStrs[4]) ); + } catch (final NumberFormatException err) { + throw new SAMException("bad MAPQ in "+semiColonStr, err); + } + + /* fill NM */ + try { + otherRec.setAttribute( SAMTagUtil.getSingleton().NM , Integer.parseInt(commaStrs[5]) ); + } catch (final NumberFormatException err) { + throw new SAMException("bad NM in "+semiColonStr, err); + } + + /* if strand is not the same: reverse-complement */ + if( otherRec.getReadNegativeStrandFlag() != record.getReadNegativeStrandFlag() ) { + SAMRecordUtil.reverseComplement(otherRec); + } + + /* add the alignment */ + alignments.add( otherRec ); + } + return alignments; + } } diff --git a/src/test/java/htsjdk/samtools/SAMUtilsTest.java b/src/test/java/htsjdk/samtools/SAMUtilsTest.java index 48baf448d..0fa6b4a69 100644 --- a/src/test/java/htsjdk/samtools/SAMUtilsTest.java +++ b/src/test/java/htsjdk/samtools/SAMUtilsTest.java @@ -26,7 +26,7 @@ import org.testng.Assert; import org.testng.annotations.Test; -import java.util.Arrays; +import java.util.List; public class SAMUtilsTest { @Test @@ -173,4 +173,79 @@ public void testClippingOfRecordWithMateAtSamePosition() { record.setSecondOfPairFlag(true); Assert.assertEquals(SAMUtils.getNumOverlappingAlignedBasesToClip(record), 10); } + + @Test + public void testOtherCanonicalAlignments() { + // setup the record + final SAMFileHeader header = new SAMFileHeader(); + header.addSequence(new SAMSequenceRecord("1", 1000)); + header.addSequence(new SAMSequenceRecord("2", 1000)); + final SAMRecord record = new SAMRecord(header); + record.setReadPairedFlag(true); + record.setFirstOfPairFlag(true); + record.setCigar(TextCigarCodec.decode("10M")); + record.setReferenceIndex(0); + record.setAlignmentStart(1); + record.setMateReferenceIndex(0); + record.setMateAlignmentStart(1); + record.setReadPairedFlag(true); + record.setSupplementaryAlignmentFlag(true);//spec says first 'SA' record will be the primary record + + record.setMateReferenceIndex(0); + record.setMateAlignmentStart(100); + record.setInferredInsertSize(99); + + record.setReadBases("AAAAAAAAAA".getBytes()); + record.setBaseQualities("##########".getBytes()); + // check no alignments if no SA tag */ + Assert.assertEquals(SAMUtils.getOtherCanonicalAlignments(record).size(),0); + + + record.setAttribute(SAMTagUtil.getSingleton().SA, + "2,500,+,3S2=1X2=2S,60,1;" + + "1,191,-,8M2S,60,0;"); + + // extract suppl alignments + final List suppl = SAMUtils.getOtherCanonicalAlignments(record); + Assert.assertNotNull(suppl); + Assert.assertEquals(suppl.size(), 2); + + for(final SAMRecord other: suppl) { + Assert.assertFalse(other.getReadUnmappedFlag()); + Assert.assertTrue(other.getReadPairedFlag()); + Assert.assertFalse(other.getMateUnmappedFlag()); + Assert.assertEquals(other.getMateAlignmentStart(),record.getMateAlignmentStart()); + Assert.assertEquals(other.getMateReferenceName(),record.getMateReferenceName()); + + Assert.assertEquals(other.getReadName(),record.getReadName()); + if( other.getReadNegativeStrandFlag()==record.getReadNegativeStrandFlag()) { + Assert.assertEquals(other.getReadString(),record.getReadString()); + Assert.assertEquals(other.getBaseQualityString(),record.getBaseQualityString()); + } + } + + SAMRecord other = suppl.get(0); + Assert.assertFalse(other.getSupplementaryAlignmentFlag());//1st of suppl and 'record' is supplementary + Assert.assertEquals(other.getReferenceName(),"2"); + Assert.assertEquals(other.getAlignmentStart(),500); + Assert.assertFalse(other.getReadNegativeStrandFlag()); + Assert.assertEquals(other.getMappingQuality(), 60); + Assert.assertEquals(other.getAttribute(SAMTagUtil.getSingleton().NM),1); + Assert.assertEquals(other.getCigarString(),"3S2=1X2=2S"); + Assert.assertEquals(other.getInferredInsertSize(),0); + + + other = suppl.get(1); + Assert.assertTrue(other.getSupplementaryAlignmentFlag()); + Assert.assertEquals(other.getReferenceName(),"1"); + Assert.assertEquals(other.getAlignmentStart(),191); + Assert.assertTrue(other.getReadNegativeStrandFlag()); + Assert.assertEquals(other.getMappingQuality(), 60); + Assert.assertEquals(other.getAttribute(SAMTagUtil.getSingleton().NM),0); + Assert.assertEquals(other.getCigarString(),"8M2S"); + Assert.assertEquals(other.getInferredInsertSize(),-91);//100(mate) - 191(other) + + + } + } From dc2108539489422181c2638ad7d3677212eb8dba Mon Sep 17 00:00:00 2001 From: Pierre Lindenbaum Date: Mon, 5 Sep 2016 12:27:20 +0200 Subject: [PATCH 011/137] svtype --- .../variantcontext/StructuralVariantType.java | 47 ++++++++++++++++++++++ .../variant/variantcontext/VariantContext.java | 9 +++++ src/main/java/htsjdk/variant/vcf/VCFConstants.java | 5 ++- .../variantcontext/VariantContextUnitTest.java | 31 ++++++++++++++ .../htsjdk/variant/structuralvariants.vcf | 22 ++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/main/java/htsjdk/variant/variantcontext/StructuralVariantType.java create mode 100644 src/test/resources/htsjdk/variant/structuralvariants.vcf diff --git a/src/main/java/htsjdk/variant/variantcontext/StructuralVariantType.java b/src/main/java/htsjdk/variant/variantcontext/StructuralVariantType.java new file mode 100644 index 000000000..36b517a2a --- /dev/null +++ b/src/main/java/htsjdk/variant/variantcontext/StructuralVariantType.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright (c) 2016 Pierre Lindenbaum @yokofakun Institut du Thorax - Nantes - France + * + * 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 htsjdk.variant.variantcontext; + +/** + * Type of Structural Variant as defined in the VCF spec 4.2 + * + */ +public enum StructuralVariantType { + /** Deletion relative to the reference */ + DEL, + /** Insertion of novel sequence relative to the reference */ + INS, + /** Region of elevated copy number relative to the reference */ + DUP, + /** Inversion of reference sequence */ + INV, + /** Copy number variable region */ + CNV, + /** breakend structural variation. VCF Specification : An arbitrary rearrangement + * event can be summarized as a set of novel adjacencies. + * Each adjacency ties together two breakends. + */ + BND +} diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index f64b0ff8e..4d0eb7928 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -1732,4 +1732,13 @@ public int getAlleleIndex(final Allele allele) { if ( index == -1 ) throw new IllegalArgumentException("Allele " + targetAllele + " not in this VariantContex " + this); return GenotypeLikelihoods.getPLIndecesOfAlleles(0, index); } + + /** + * Search for the INFO=SVTYPE and return the type of Structural Variant + * @return the StructuralVariantType of null if there is no property SVTYPE + * */ + public StructuralVariantType geStructuralVariantType() { + final String svType = this.getAttributeAsString(VCFConstants.SVTYPE, null); + return svType == null ? null : StructuralVariantType.valueOf(svType); + } } diff --git a/src/main/java/htsjdk/variant/vcf/VCFConstants.java b/src/main/java/htsjdk/variant/vcf/VCFConstants.java index b05856d5e..6a52d1df0 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFConstants.java +++ b/src/main/java/htsjdk/variant/vcf/VCFConstants.java @@ -63,7 +63,10 @@ public static final String SOMATIC_KEY = "SOMATIC"; public static final String VALIDATED_KEY = "VALIDATED"; public static final String THOUSAND_GENOMES_KEY = "1000G"; - + + // reserved INFO for structural variants + /** INFO Type of structural variant */ + public static final String SVTYPE = "SVTYPE"; // separators public static final String FORMAT_FIELD_SEPARATOR = ":"; diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index be55f8f58..4706abdb9 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -26,6 +26,9 @@ package htsjdk.variant.variantcontext; +import htsjdk.samtools.util.CloseableIterator; +import htsjdk.samtools.util.CloserUtil; + // the imports for unit testing. import htsjdk.samtools.util.TestUtil; @@ -37,6 +40,8 @@ import htsjdk.tribble.TribbleException; import htsjdk.variant.VariantBaseTest; import htsjdk.variant.vcf.VCFConstants; +import htsjdk.variant.vcf.VCFFileReader; + import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; @@ -1447,4 +1452,30 @@ public void testExtraStrictValidationFailure(final VariantContext vc, final Alle // extraStrictValidation throws exceptions if it fails, so no Asserts here... vc.extraStrictValidation(reportedReference, observedReference, rsIDs); } + + + @DataProvider(name = "structuralVariationsTestData") + public Object[][] getStructuralVariationsTestData() { + return new Object[][] { + {new File("src/test/resources/htsjdk/variant/structuralvariants.vcf")} + }; + } + + @Test(dataProvider = "structuralVariationsTestData") + public void testExtractStructuralVariationsData(final File vcfFile) { + VCFFileReader reader = null; + CloseableIterator iter = null; + try { + reader = new VCFFileReader(vcfFile , false ); + iter = reader.iterator(); + while(iter.hasNext()) { + final VariantContext ctx = iter.next(); + final StructuralVariantType st = ctx.geStructuralVariantType(); + Assert.assertNotNull(st); + } + } finally { + CloserUtil.close(iter); + CloserUtil.close(reader); + } + } } diff --git a/src/test/resources/htsjdk/variant/structuralvariants.vcf b/src/test/resources/htsjdk/variant/structuralvariants.vcf new file mode 100644 index 000000000..5ffad2f94 --- /dev/null +++ b/src/test/resources/htsjdk/variant/structuralvariants.vcf @@ -0,0 +1,22 @@ +##fileformat=VCFv4.2 +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##ALT= +##ALT= +##ALT= +##ALT= +##FORMAT= +##FORMAT= +##contig= +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE1 +1 20 . N 1 . IMPRECISE;SVTYPE=DUP;END=4641652;END_CHR=1;STRAND_1=-;STRAND_2=+;SVLEN=4641632;METHOD=LUMPY;DP=90 GT:AO 1/1:90 +1 33 . N 1 . IMPRECISE;SVTYPE=DUP;END=2640388;END_CHR=1;STRAND_1=-;STRAND_2=+;SVLEN=2640355;METHOD=LUMPY;DP=3 GT:AO 1/1:3 +1 70 . N 1 . IMPRECISE;SVTYPE=DEL;END=4641583;END_CHR=1;STRAND_1=+;STRAND_2=-;SVLEN=-4641513;METHOD=LUMPY;DP=1 GT:AO 1/1:1 +1 101 . N 1 . IMPRECISE;SVTYPE=INV;END=1988714;END_CHR=1;STRAND_1=-;STRAND_2=-;SVLEN=1988613;METHOD=LUMPY;DP=2 GT:AO 1/1:2 From 01f913e935159b319dee8abcc2885c0978c239bb Mon Sep 17 00:00:00 2001 From: Pierre Lindenbaum Date: Mon, 5 Sep 2016 12:37:15 +0200 Subject: [PATCH 012/137] typo get --- src/main/java/htsjdk/variant/variantcontext/VariantContext.java | 2 +- src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 4d0eb7928..0baf0bd70 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -1737,7 +1737,7 @@ public int getAlleleIndex(final Allele allele) { * Search for the INFO=SVTYPE and return the type of Structural Variant * @return the StructuralVariantType of null if there is no property SVTYPE * */ - public StructuralVariantType geStructuralVariantType() { + public StructuralVariantType getStructuralVariantType() { final String svType = this.getAttributeAsString(VCFConstants.SVTYPE, null); return svType == null ? null : StructuralVariantType.valueOf(svType); } diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 4706abdb9..34854eaea 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -1470,7 +1470,7 @@ public void testExtractStructuralVariationsData(final File vcfFile) { iter = reader.iterator(); while(iter.hasNext()) { final VariantContext ctx = iter.next(); - final StructuralVariantType st = ctx.geStructuralVariantType(); + final StructuralVariantType st = ctx.getStructuralVariantType(); Assert.assertNotNull(st); } } finally { From fb1ba06168c4295f309f8b8aae6b6f822c835002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 13 Sep 2016 17:30:52 +0200 Subject: [PATCH 013/137] Fix BEDCodec.canDecode() to handle block-compressed extensions (#704) fix BEDCodec.canDecode to handle block-compressed extensions --- src/main/java/htsjdk/tribble/bed/BEDCodec.java | 12 +++++++++++- src/test/java/htsjdk/tribble/bed/BEDCodecTest.java | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/tribble/bed/BEDCodec.java b/src/main/java/htsjdk/tribble/bed/BEDCodec.java index 62d202c19..ea1e88989 100644 --- a/src/main/java/htsjdk/tribble/bed/BEDCodec.java +++ b/src/main/java/htsjdk/tribble/bed/BEDCodec.java @@ -23,6 +23,7 @@ */ package htsjdk.tribble.bed; +import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.AsciiFeatureCodec; import htsjdk.tribble.annotation.Strand; import htsjdk.tribble.index.tabix.TabixFormat; @@ -40,6 +41,9 @@ */ public class BEDCodec extends AsciiFeatureCodec { + /** Default extension for BED files. */ + public static final String BED_EXTENSION = ".bed"; + private static final Pattern SPLIT_PATTERN = Pattern.compile("\\t|( +)"); private final int startOffsetValue; @@ -197,7 +201,13 @@ private void createExons(int start, String[] tokens, FullBEDFeature gene, @Override public boolean canDecode(final String path) { - return path.toLowerCase().endsWith(".bed"); + final String toDecode; + if (AbstractFeatureReader.hasBlockCompressedExtension(path)) { + toDecode = path.substring(0, path.lastIndexOf(".")); + } else { + toDecode = path; + } + return toDecode.toLowerCase().endsWith(BED_EXTENSION); } public int getStartOffset() { diff --git a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java index c7b21931c..474a8a89b 100644 --- a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java +++ b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java @@ -226,4 +226,15 @@ private void createIndex(File testFile, File idxFile) throws IOException { public void testGetTabixFormat() { Assert.assertEquals(new BEDCodec().getTabixFormat(), TabixFormat.BED); } + + @Test + public void testCanDecode() { + final BEDCodec codec = new BEDCodec(); + final String pattern = "filename.%s%s"; + for(final String bcExt: AbstractFeatureReader.BLOCK_COMPRESSED_EXTENSIONS) { + Assert.assertTrue(codec.canDecode(String.format(pattern, "bed", bcExt))); + Assert.assertFalse(codec.canDecode(String.format(pattern, "vcf", bcExt))); + Assert.assertFalse(codec.canDecode(String.format(pattern, "bed.gzip", bcExt))); + } + } } From 21d8865b2e1f8165ef52d0d1ca7e8dff8e6010e3 Mon Sep 17 00:00:00 2001 From: Steve Huang Date: Tue, 13 Sep 2016 13:03:05 -0400 Subject: [PATCH 014/137] Fix JexlMap and GenotypeJEXLContext NullPtrException (#668) fixes NullPointerException in GenotypeJEXLContext.get() added tests for VariantJexlContext and GenotypeJexlContext --- .../variantcontext/GenotypeJEXLContext.java | 25 +- .../htsjdk/variant/variantcontext/JEXLMap.java | 174 ++++++------- .../variantcontext/VariantContextUtils.java | 54 ++-- .../variant/variantcontext/VariantJEXLContext.java | 16 +- .../variantcontext/VariantJEXLContextUnitTest.java | 276 ++++++++++++--------- 5 files changed, 305 insertions(+), 240 deletions(-) diff --git a/src/main/java/htsjdk/variant/variantcontext/GenotypeJEXLContext.java b/src/main/java/htsjdk/variant/variantcontext/GenotypeJEXLContext.java index cda97ab64..8d2cd10d5 100644 --- a/src/main/java/htsjdk/variant/variantcontext/GenotypeJEXLContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/GenotypeJEXLContext.java @@ -26,16 +26,16 @@ attributes.put("g", (Genotype g) -> g); attributes.put(VCFConstants.GENOTYPE_KEY, Genotype::getGenotypeString); - attributes.put("isHom", (Genotype g) -> g.isHom() ? "1" : "0"); - attributes.put("isHomRef", (Genotype g) -> g.isHomRef() ? "1" : "0"); - attributes.put("isHet", (Genotype g) -> g.isHet() ? "1" : "0"); - attributes.put("isHomVar", (Genotype g) -> g.isHomVar() ? "1" : "0"); - attributes.put("isCalled", (Genotype g) -> g.isCalled() ? "1" : "0"); - attributes.put("isNoCall", (Genotype g) -> g.isNoCall() ? "1" : "0"); - attributes.put("isMixed", (Genotype g) -> g.isMixed() ? "1" : "0"); - attributes.put("isAvailable", (Genotype g) -> g.isAvailable() ? "1" : "0"); - attributes.put("isPassFT", (Genotype g) -> g.isFiltered() ? "0" : "1"); - attributes.put(VCFConstants.GENOTYPE_FILTER_KEY, (Genotype g) -> g.isFiltered()? g.getFilters() : "PASS"); + attributes.put("isHom", (Genotype g) -> g.isHom() ? true_string : false_string); + attributes.put("isHomRef", (Genotype g) -> g.isHomRef() ? true_string : false_string); + attributes.put("isHet", (Genotype g) -> g.isHet() ? true_string : false_string); + attributes.put("isHomVar", (Genotype g) -> g.isHomVar() ? true_string : false_string); + attributes.put("isCalled", (Genotype g) -> g.isCalled() ? true_string : false_string); + attributes.put("isNoCall", (Genotype g) -> g.isNoCall() ? true_string : false_string); + attributes.put("isMixed", (Genotype g) -> g.isMixed() ? true_string : false_string); + attributes.put("isAvailable", (Genotype g) -> g.isAvailable() ? true_string : false_string); + attributes.put("isPassFT", (Genotype g) -> g.isFiltered() ? false_string : true_string); + attributes.put(VCFConstants.GENOTYPE_FILTER_KEY, (Genotype g) -> g.isFiltered()? g.getFilters() : VCFConstants.PASSES_FILTERS_v4); attributes.put(VCFConstants.GENOTYPE_QUALITY_KEY, Genotype::getGQ); } @@ -44,14 +44,15 @@ public GenotypeJEXLContext(VariantContext vc, Genotype g) { this.g = g; } + @Override public Object get(String name) { //should matching genotype attributes always supersede vc? if ( attributes.containsKey(name) ) { // dynamic resolution of name -> value via map return attributes.get(name).get(g); } else if ( g.hasAnyAttribute(name) ) { return g.getAnyAttribute(name); - } else if ( g.getFilters().contains(name) ) { - return "1"; + } else if ( g.getFilters() != null && g.getFilters().contains(name) ) { + return true_string; } else return super.get(name); } diff --git a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java index a7a871fc8..b8e13c75b 100644 --- a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java +++ b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java @@ -2,6 +2,7 @@ import htsjdk.variant.variantcontext.VariantContextUtils.JexlVCMatchExp; import org.apache.commons.jexl2.JexlContext; +import org.apache.commons.jexl2.JexlException; import org.apache.commons.jexl2.MapContext; import java.util.Collection; @@ -11,12 +12,8 @@ import java.util.Set; /** - * this is an implementation of a Map of JexlVCMatchExp to true or false values. It lazy initializes each value - * as requested to save as much processing time as possible. - * - * Compatible with JEXL 1.1 (this code will be easier if we move to 2.0, all of the functionality can go into the - * JexlContext's get() - * + * This is an implementation of a Map of {@link JexlVCMatchExp} to true or false values. + * It lazily initializes each value as requested to save as much processing time as possible. */ class JEXLMap implements Map { @@ -27,137 +24,148 @@ // our context private JexlContext jContext = null; - // our mapping from JEXLVCMatchExp to Booleans, which will be set to NULL for previously uncached JexlVCMatchExp + /** + * our mapping from {@link JexlVCMatchExp} to {@link Boolean}s, which will be set to {@code NULL} + * for previously un-cached {@link JexlVCMatchExp}. + */ private Map jexl; - - public JEXLMap(Collection jexlCollection, VariantContext vc, Genotype g) { + public JEXLMap(final Collection jexlCollection, final VariantContext vc, final Genotype g) { + initialize(jexlCollection); this.vc = vc; this.g = g; - initialize(jexlCollection); } - public JEXLMap(Collection jexlCollection, VariantContext vc) { + public JEXLMap(final Collection jexlCollection, final VariantContext vc) { this(jexlCollection, vc, null); } - private void initialize(Collection jexlCollection) { - jexl = new HashMap(); - for (JexlVCMatchExp exp: jexlCollection) { - jexl.put(exp, null); - } - } - /** - * create the internal JexlContext, only when required. This code is where new JEXL context variables - * should get added. + * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * + * @throws IllegalArgumentException when {@code o} is {@code null} or + * when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ - private void createContext() { - if ( vc == null ) { - jContext = new MapContext(Collections.emptyMap()); - } - else if (g == null) { - jContext = new VariantJEXLContext(vc); + public Boolean get(Object o) { + if (o == null) { + throw new IllegalArgumentException("Query key is null"); } - else { - jContext = new GenotypeJEXLContext(vc, g); + + // if we've already determined the value, return it + if (jexl.containsKey(o) && jexl.get(o) != null) { + return jexl.get(o); } - } - /** - * @return the size of the internal data structure - */ - public int size() { - return jexl.size(); + // otherwise cast the expression and try again + final JexlVCMatchExp e = (JexlVCMatchExp) o; + evaluateExpression(e); + return jexl.get(e); } /** - * @return true if we're empty - */ - public boolean isEmpty() { return this.jexl.isEmpty(); } - - /** * do we contain the specified key * @param o the key * @return true if we have a value for that key */ public boolean containsKey(Object o) { return jexl.containsKey(o); } - public Boolean get(Object o) { - // if we've already determined the value, return it - if (jexl.containsKey(o) && jexl.get(o) != null) return jexl.get(o); - - // try and cast the expression - JexlVCMatchExp e = (JexlVCMatchExp) o; - evaluateExpression(e); - return jexl.get(e); - } - - /** - * get the keyset of map - * @return a set of keys of type JexlVCMatchExp - */ public Set keySet() { return jexl.keySet(); } /** - * get all the values of the map. This is an expensive call, since it evaluates all keys that haven't - * been evaluated yet. This is fine if you truely want all the keys, but if you only want a portion, or know + * Get all the values of the map, i.e. the {@link Boolean} values. + * This is an expensive call, since it evaluates all keys that haven't been evaluated yet. + * This is fine if you truly want all the keys, but if you only want a portion, or know * the keys you want, you would be better off using get() to get them by name. + * + * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. + * * @return a collection of boolean values, representing the results of all the variants evaluated + * + * @throws IllegalArgumentException when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ public Collection values() { - // this is an expensive call - for (JexlVCMatchExp exp : jexl.keySet()) - if (jexl.get(exp) == null) + for (final JexlVCMatchExp exp : jexl.keySet()) { + if (jexl.get(exp) == null) { evaluateExpression(exp); + } + } return jexl.values(); } /** - * evaulate a JexlVCMatchExp's expression, given the current context (and setup the context if it's null) - * @param exp the JexlVCMatchExp to evaluate + * @return the number of keys, i.e. {@link JexlVCMatchExp}'s held by this mapping. + */ + public int size() { + return jexl.size(); + } + + public boolean isEmpty() { return this.jexl.isEmpty(); } + + public Boolean put(JexlVCMatchExp jexlVCMatchExp, Boolean aBoolean) { + return jexl.put(jexlVCMatchExp, aBoolean); + } + + public void putAll(Map map) { + jexl.putAll(map); + } + + /** + * Initializes all keys with null values indicating that they have not yet been evaluated. + * The actual value will be computed only when the key is requested via {@link #get(Object)} or {@link #values()}. */ - private void evaluateExpression(JexlVCMatchExp exp) { + private void initialize(Collection jexlCollection) { + jexl = new HashMap<>(); + for (final JexlVCMatchExp exp: jexlCollection) { + jexl.put(exp, null); + } + } + + /** + * Evaluates a {@link JexlVCMatchExp}'s expression, given the current context (and setup the context if it's {@code null}). + * + * @param exp the {@link JexlVCMatchExp} to evaluate + * + * @throws IllegalArgumentException when {@code exp} is {@code null}, or + * when the Jexl expression in {@code exp} fails to evaluate the JexlContext + * constructed with the input VC or genotype. + */ + private void evaluateExpression(final JexlVCMatchExp exp) { // if the context is null, we need to create it to evaluate the JEXL expression - if (this.jContext == null) createContext(); + if (this.jContext == null) { + createContext(); + } + try { final Boolean value = (Boolean) exp.exp.evaluate(jContext); // treat errors as no match jexl.put(exp, value == null ? false : value); - } catch (Exception e) { + } catch (final JexlException.Variable e) { // if exception happens because variable is undefined (i.e. field in expression is not present), evaluate to FALSE - // todo - might be safer if we explicitly checked for an exception type, but Apache's API doesn't seem to have that ability - if (e.getMessage() != null && e.getMessage().contains("undefined variable")) - jexl.put(exp,false); - else - throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s with message %s", exp.name, (e.getMessage() == null ? "no message" : e.getMessage()))); + jexl.put(exp,false); + } catch (final JexlException e) { + // todo - might be better if no exception is caught here but let's user decide how to deal with them; note this will propagate to get() and values() + throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s", exp.name), e); } } /** - * helper function: adds the list of attributes to the information map we're building - * @param infoMap the map - * @param attributes the attributes + * Create the internal JexlContext, only when required. + * This code is where new JEXL context variables should get added. */ - private static void addAttributesToMap(Map infoMap, Map attributes ) { - for (Entry e : attributes.entrySet()) { - infoMap.put(e.getKey(), String.valueOf(e.getValue())); + private void createContext() { + if (vc == null) { + jContext = new MapContext(Collections.emptyMap()); + } else if (g == null) { + jContext = new VariantJEXLContext(vc); + } else { + jContext = new GenotypeJEXLContext(vc, g); } } - public Boolean put(JexlVCMatchExp jexlVCMatchExp, Boolean aBoolean) { - return jexl.put(jexlVCMatchExp,aBoolean); - } - - public void putAll(Map map) { - jexl.putAll(map); - } - // ////////////////////////////////////////////////////////////////////////////////////// - // The Following are unsupported at the moment + // The Following are unsupported at the moment (date: 2016/08/18) // ////////////////////////////////////////////////////////////////////////////////////// // this doesn't make much sense to implement, boolean doesn't offer too much variety to deal diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java index ac4c43c70..96eaa64e3 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java @@ -202,7 +202,7 @@ public final static VCFCompoundHeaderLine getMetaDataForField(final VCFHeader he } /** - * A simple but common wrapper for matching VariantContext objects using JEXL expressions + * A simple but common wrapper for matching {@link VariantContext} objects using JEXL expressions */ public static class JexlVCMatchExp { public String name; @@ -212,8 +212,12 @@ public final static VCFCompoundHeaderLine getMetaDataForField(final VCFHeader he * Create a new matcher expression with name and JEXL expression exp * @param name name * @param exp expression + * @throws IllegalArgumentException if either argument is {@code null} */ public JexlVCMatchExp(String name, Expression exp) { + if (name == null) { throw new IllegalArgumentException("Cannot create JexlVCMatchExp with null name."); } + if (exp == null) { throw new IllegalArgumentException("Cannot create JexlVCMatchExp with null expression."); } + this.name = name; this.exp = exp; } @@ -258,7 +262,6 @@ public JexlVCMatchExp(String name, Expression exp) { return initializeMatchExps(names.toArray(nameArray), exps.toArray(expArray)); } - /** * Method for creating JexlVCMatchExp from input walker arguments mapping from names to exps. These two arrays contain * the name associated with each JEXL expression. initializeMatchExps will parse each expression and return @@ -288,51 +291,52 @@ public JexlVCMatchExp(String name, Expression exp) { } /** - * Returns true if exp match VC. See {@link #match(VariantContext, Collection)} for full docs. + * Returns true if {@code exp} match {@code vc}. + * See {@link #match(VariantContext, Collection)} for full docs. * @param vc variant context * @param exp expression - * @return true if there is a match + * @return true if there is a match */ public static boolean match(VariantContext vc, JexlVCMatchExp exp) { return match(vc, Collections.singletonList(exp)).get(exp); } /** - * Matches each JexlVCMatchExp exp against the data contained in vc, and returns a map from these - * expressions to true (if they matched) or false (if they didn't). This the best way to apply JEXL - * expressions to VariantContext records. Use initializeMatchExps() to create the list of JexlVCMatchExp - * expressions. + * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, + * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). + * This the best way to apply JEXL expressions to {@link VariantContext} records. + * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * - * @param vc variant context - * @param exps expressions - * @return true if there is a match + * @param vc variant context + * @param exps expressions + * @return true if there is a match */ public static Map match(VariantContext vc, Collection exps) { return new JEXLMap(exps,vc); - } /** - * Returns true if exp match VC/g. See {@link #match(VariantContext, Collection)} for full docs. - * @param vc variant context - * @param g genotype + * Returns true if {@code exp} match {@code vc}, {@code g}. + * See {@link #match(VariantContext, Genotype, Collection)} for full docs. + * @param vc variant context + * @param g genotype * @param exp expression - * @return true if there is a match + * @return true if there is a match */ public static boolean match(VariantContext vc, Genotype g, JexlVCMatchExp exp) { return match(vc,g, Collections.singletonList(exp)).get(exp); } /** - * Matches each JexlVCMatchExp exp against the data contained in vc/g, and returns a map from these - * expressions to true (if they matched) or false (if they didn't). This the best way to apply JEXL - * expressions to VariantContext records/genotypes. Use initializeMatchExps() to create the list of JexlVCMatchExp - * expressions. + * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, {@code g}, + * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). + * This the best way to apply JEXL expressions to {@link VariantContext} records. + * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * - * @param vc variant context - * @param g genotype - * @param exps expressions - * @return true if there is a match + * @param vc variant context + * @param g genotype + * @param exps expressions + * @return true if there is a match */ public static Map match(VariantContext vc, Genotype g, Collection exps) { return new JEXLMap(exps,vc,g); @@ -361,7 +365,6 @@ public static boolean match(VariantContext vc, Genotype g, JexlVCMatchExp exp) { * @throws IllegalArgumentException if vc is monomorphic, not a SNP or not bi-allelic. */ - static public boolean isTransition(final VariantContext vc) throws IllegalArgumentException { final byte refAllele = vc.getReference().getBases()[0]; final Collection altAlleles = vc.getAlternateAlleles(); @@ -386,7 +389,6 @@ static public boolean isTransition(final VariantContext vc) throws IllegalArgume || (refAllele == 'T' && altAllele == 'C'); } - /** * Returns a newly allocated VC that is the same as VC, but without genotypes * @param vc variant context diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java index ee232298a..493499e30 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java @@ -48,6 +48,9 @@ // our stored variant context private VariantContext vc; + static final String true_string = "1"; + static final String false_string = "0"; + private interface AttributeGetter { public Object get(VariantContext vc); } @@ -62,7 +65,7 @@ attributes.put("QUAL", (VariantContext vc) -> -10 * vc.getLog10PError()); attributes.put("ALLELES", VariantContext::getAlleles); attributes.put("N_ALLELES", VariantContext::getNAlleles); - attributes.put("FILTER", (VariantContext vc) -> vc.isFiltered() ? "1" : "0"); + attributes.put("FILTER", (VariantContext vc) -> vc.isFiltered() ? true_string : false_string); attributes.put("homRefCount", VariantContext::getHomRefCount); attributes.put("hetCount", VariantContext::getHetCount); @@ -80,11 +83,9 @@ public Object get(String name) { } else if ( vc.hasAttribute(name)) { result = vc.getAttribute(name); } else if ( vc.getFilters().contains(name) ) { - result = "1"; + result = true_string; } - //System.out.printf("dynamic lookup %s => %s%n", name, result); - return result; } @@ -92,11 +93,10 @@ public boolean has(String name) { return get(name) != null; } + /** + * @throws UnsupportedOperationException + */ public void set(String name, Object value) { throw new UnsupportedOperationException("remove() not supported on a VariantJEXLContext"); } } - - - - diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java index bd00b75af..bebd39384 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java @@ -25,16 +25,18 @@ package htsjdk.variant.variantcontext; -import htsjdk.samtools.util.Log; +import htsjdk.tribble.SimpleFeature; import htsjdk.variant.VariantBaseTest; import htsjdk.variant.variantcontext.VariantContextUtils.JexlVCMatchExp; +import htsjdk.variant.vcf.VCFConstants; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -43,178 +45,230 @@ * * @author aaron * @author bimber - * - * Class VariantJEXLContextUnitTest + * @author hyq + * * * Test out parts of the VariantJEXLContext and GenotypeJEXLContext */ public class VariantJEXLContextUnitTest extends VariantBaseTest { - private static String expression = "QUAL > 500.0"; - private static VariantContextUtils.JexlVCMatchExp exp; - - Allele A, Aref, T, Tref; - - Allele ATC, ATCref; - // A [ref] / T at 10 - - // - / ATC [ref] from 20-23 - - @BeforeClass - public void beforeClass() { - try { - exp = new VariantContextUtils.JexlVCMatchExp("name", VariantContextUtils.engine.get().createExpression(expression)); - } catch (Exception e) { - Assert.fail("Unable to create expression" + e.getMessage()); - } - } - - @BeforeMethod - public void before() { - A = Allele.create("A"); - Aref = Allele.create("A", true); - T = Allele.create("T"); - Tref = Allele.create("T", true); - - ATC = Allele.create("ATC"); - ATCref = Allele.create("ATC", true); - } - - + private static final VariantContextUtils.JexlVCMatchExp exp + = new VariantContextUtils.JexlVCMatchExp("name", VariantContextUtils.engine.get().createExpression("QUAL > 500.0")); + + // SNP alleles: A[ref]/T[alt] at chr1:10. One (crappy) sample, one (bare minimum) VC. + private static final SimpleFeature eventLoc = new SimpleFeature("chr1", 10, 10); + private static final Allele Aref = Allele.create("A", true); + private static final Allele Talt = Allele.create("T"); + private static final Genotype gt = new GenotypeBuilder("DummySample", Arrays.asList(Aref, Talt)) + .phased(false) + .DP(2) + .noGQ() + .noAD() + .noPL() + .filter("lowDP") + .attribute("WA", "whatEver") + .make(); + private static final VariantContext vc = new VariantContextBuilder("test", eventLoc.getContig(), eventLoc.getStart(), eventLoc.getEnd(), Arrays.asList(Aref, Talt)) + .genotypes(gt) + .noID() + .filter("q10") + .attribute("attr", "notEmpty") + .make(); + + //////////////////////// testing JEXLMap //////////////////////// @Test public void testGetValue() { - Map map = getVarContext(); + final Map jexlMap = getJEXLMap(); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 1); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 1); // eval our known expression - Assert.assertTrue(!map.get(exp)); + Assert.assertTrue(!jexlMap.get(exp)); } // Testing the new 'FT' and 'isPassFT' expressions in the JEXL map @Test public void testJEXLGenotypeFilters() { - JexlVCMatchExp passFlag = new VariantContextUtils.JexlVCMatchExp( + final JexlVCMatchExp passFlag = new VariantContextUtils.JexlVCMatchExp( "passFlag", VariantContextUtils.engine.get().createExpression("isPassFT==1")); - JexlVCMatchExp passFT = new VariantContextUtils.JexlVCMatchExp( + final JexlVCMatchExp passFT = new VariantContextUtils.JexlVCMatchExp( "FTPASS", VariantContextUtils.engine.get().createExpression("FT==\"PASS\"")); - JexlVCMatchExp failFT = new VariantContextUtils.JexlVCMatchExp( + final JexlVCMatchExp failFT = new VariantContextUtils.JexlVCMatchExp( "FTBadCall", VariantContextUtils.engine.get().createExpression("FT==\"BadCall\"")); - JexlVCMatchExp AD1 = new VariantContextUtils.JexlVCMatchExp( + final JexlVCMatchExp AD1 = new VariantContextUtils.JexlVCMatchExp( "AD1", VariantContextUtils.engine.get().createExpression("g.hasAD() && g.getAD().0==1")); - JexlVCMatchExp AD2 = new VariantContextUtils.JexlVCMatchExp( + final JexlVCMatchExp AD2 = new VariantContextUtils.JexlVCMatchExp( "AD2", VariantContextUtils.engine.get().createExpression("g.hasAD() && g.getAD().1==2")); - List jexlTests = Arrays.asList(passFlag, passFT, failFT, AD1, AD2); - Map map; - - List alleles = Arrays.asList(Aref, T); - VariantContextBuilder vcb = new VariantContextBuilder("test", "chr1", 10, 10, alleles); - VariantContext vcPass = vcb.filters("PASS").make(); - VariantContext vcFail = vcb.filters("BadVariant").make(); - GenotypeBuilder gb = new GenotypeBuilder("SAMPLE", alleles); + final List jexlTests = Arrays.asList(passFlag, passFT, failFT, AD1, AD2); + + final List alleles = Arrays.asList(Aref, Talt); + final VariantContextBuilder vcb = new VariantContextBuilder("test", "chr1", 10, 10, alleles); + final VariantContext vcPass = vcb.filters("PASS").make(); + final VariantContext vcFail = vcb.filters("BadVariant").make(); + final GenotypeBuilder gb = new GenotypeBuilder("SAMPLE", alleles); - Genotype genoNull = gb.make(); - Genotype genoPass = gb.filters("PASS").AD(new int[]{1,2}).DP(3).make(); - Genotype genoFail = gb.filters("BadCall").AD(null).DP(0).make(); + final Genotype genoNull = gb.make(); + final Genotype genoPass = gb.filters("PASS").AD(new int[]{1,2}).DP(3).make(); + final Genotype genoFail = gb.filters("BadCall").AD(null).DP(0).make(); + + Map jexlMap; // Create the JEXL Maps using the combinations above of vc* and geno* - map = new JEXLMap(jexlTests,vcPass, genoPass); + jexlMap = new JEXLMap(jexlTests, vcPass, genoPass); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertTrue(map.get(passFlag)); - Assert.assertTrue(map.get(passFT)); - Assert.assertFalse(map.get(failFT)); - Assert.assertTrue(map.get(AD1)); - Assert.assertTrue(map.get(AD2)); - - map = new JEXLMap(jexlTests, vcPass, genoFail); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertTrue(jexlMap.get(passFlag)); + Assert.assertTrue(jexlMap.get(passFT)); + Assert.assertFalse(jexlMap.get(failFT)); + Assert.assertTrue(jexlMap.get(AD1)); + Assert.assertTrue(jexlMap.get(AD2)); + + jexlMap = new JEXLMap(jexlTests, vcPass, genoFail); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertFalse(map.get(passFlag)); - Assert.assertFalse(map.get(passFT)); - Assert.assertTrue(map.get(failFT)); - Assert.assertFalse(map.get(AD1)); - Assert.assertFalse(map.get(AD2)); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertFalse(jexlMap.get(passFlag)); + Assert.assertFalse(jexlMap.get(passFT)); + Assert.assertTrue(jexlMap.get(failFT)); + Assert.assertFalse(jexlMap.get(AD1)); + Assert.assertFalse(jexlMap.get(AD2)); // Null genotype filter is equivalent to explicit "FT==PASS" - map = new JEXLMap(jexlTests, vcPass, genoNull); + jexlMap = new JEXLMap(jexlTests, vcPass, genoNull); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertTrue(map.get(passFlag)); - Assert.assertTrue(map.get(passFT)); - Assert.assertFalse(map.get(failFT)); - Assert.assertFalse(map.get(AD1)); - Assert.assertFalse(map.get(AD2)); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertTrue(jexlMap.get(passFlag)); + Assert.assertTrue(jexlMap.get(passFT)); + Assert.assertFalse(jexlMap.get(failFT)); + Assert.assertFalse(jexlMap.get(AD1)); + Assert.assertFalse(jexlMap.get(AD2)); // Variant-level filters should have no effect here - map = new JEXLMap(jexlTests,vcFail, genoPass); + jexlMap = new JEXLMap(jexlTests, vcFail, genoPass); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertTrue(map.get(passFlag)); - Assert.assertTrue(map.get(passFT)); - Assert.assertFalse(map.get(failFT)); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertTrue(jexlMap.get(passFlag)); + Assert.assertTrue(jexlMap.get(passFT)); + Assert.assertFalse(jexlMap.get(failFT)); - map = new JEXLMap(jexlTests,vcFail, genoFail); + jexlMap = new JEXLMap(jexlTests, vcFail, genoFail); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertFalse(map.get(passFlag)); - Assert.assertFalse(map.get(passFT)); - Assert.assertTrue(map.get(failFT)); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertFalse(jexlMap.get(passFlag)); + Assert.assertFalse(jexlMap.get(passFT)); + Assert.assertTrue(jexlMap.get(failFT)); - map = new JEXLMap(jexlTests,vcFail, genoNull); + jexlMap = new JEXLMap(jexlTests, vcFail, genoNull); // make sure the context has a value - Assert.assertTrue(!map.isEmpty()); - Assert.assertEquals(map.size(), 5); - Assert.assertTrue(map.get(passFlag)); - Assert.assertTrue(map.get(passFT)); - Assert.assertFalse(map.get(failFT)); + Assert.assertTrue(!jexlMap.isEmpty()); + Assert.assertEquals(jexlMap.size(), 5); + Assert.assertTrue(jexlMap.get(passFlag)); + Assert.assertTrue(jexlMap.get(passFT)); + Assert.assertFalse(jexlMap.get(failFT)); } @Test(expectedExceptions=UnsupportedOperationException.class) public void testContainsValue() { - Map map = getVarContext(); + final Map jexlMap = getJEXLMap(); - map.containsValue(exp); + jexlMap.containsValue(exp); } @Test(expectedExceptions=UnsupportedOperationException.class) public void testRemove() { - Map map = getVarContext(); + final Map jexlMap = getJEXLMap(); - map.remove(exp); + jexlMap.remove(exp); } @Test(expectedExceptions=UnsupportedOperationException.class) public void testEntrySet() { - Map map = getVarContext(); + final Map jexlMap = getJEXLMap(); - map.entrySet(); + jexlMap.entrySet(); } @Test(expectedExceptions=UnsupportedOperationException.class) public void testClear() { - Map map = getVarContext(); + final Map jexlMap = getJEXLMap(); - map.clear(); + jexlMap.clear(); } /** - * helper method - * @return a VariantJEXLContext + * @return a JEXLMap for use by actual tests */ - private JEXLMap getVarContext() { - List alleles = Arrays.asList(Aref, T); + private JEXLMap getJEXLMap() { + return new JEXLMap(Collections.singletonList(exp), vc); + } - VariantContext vc = new VariantContextBuilder("test", "chr1", 10, 10, alleles).make(); - return new JEXLMap(Arrays.asList(exp),vc); + //////////////////////// testing GenotypeJEXLContext and its base VariantJEXLContext //////////////////////// + + /** + * Test the various if-else cases in {@link GenotypeJEXLContext#get(String)} and {@link VariantJEXLContext#get(String)} + * {@link GenotypeJEXLContext#has(String)} is not tested because it simply checks if get() will return null. + */ + @Test + public void testVariantJEXLContextGetMethod() { + + final VariantJEXLContext jEXLContext = getJEXLContext(); + + // This is not tested because there's no simple test for equality for VariantContext, + // except exhaustive attributes testing, which is what happening below. +// Assert.assertEquals(jEXLContext.get("vc"), new VariantContextBuilder("test", "chr1", 10, 10, Arrays.asList(Aref, Talt)).make()); + + // GenotypeJEXLContext + Assert.assertTrue( ((Genotype) jEXLContext.get("g")).sameGenotype(gt, false)); + Assert.assertEquals(jEXLContext.get("isHom"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get("isHomRef"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get("isHomVar"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get("isHet"), VariantJEXLContext.true_string); + Assert.assertEquals(jEXLContext.get("isCalled"), VariantJEXLContext.true_string); + Assert.assertEquals(jEXLContext.get("isNoCall"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get("isMixed"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get("isAvailable"), VariantJEXLContext.true_string); + Assert.assertEquals(jEXLContext.get("isPassFT"), VariantJEXLContext.false_string); + Assert.assertEquals(jEXLContext.get(VCFConstants.GENOTYPE_KEY), gt.getGenotypeString()); + Assert.assertEquals(jEXLContext.get(VCFConstants.GENOTYPE_FILTER_KEY),"lowDP"); + Assert.assertEquals(jEXLContext.get(VCFConstants.GENOTYPE_QUALITY_KEY),Integer.valueOf(VCFConstants.MISSING_GENOTYPE_QUALITY_v3)); + Assert.assertEquals(jEXLContext.get("WA"),"whatEver"); // hasAnyAttribute->getAnyAttribute + Assert.assertEquals(jEXLContext.get("lowDP"),VariantJEXLContext.true_string); // getFilters()!=null + + // VariantJEXLContext + Assert.assertEquals(jEXLContext.get("CHROM"), eventLoc.getContig()); + Assert.assertEquals(jEXLContext.get("POS"), eventLoc.getStart()); + Assert.assertEquals(jEXLContext.get("TYPE"), VariantContext.Type.SNP.name()); + Assert.assertEquals(jEXLContext.get("QUAL"), -10.0); // because of noGQ() when building the genotype + Assert.assertEquals(jEXLContext.get("ALLELES"), vc.getAlleles()); + Assert.assertEquals(jEXLContext.get("N_ALLELES"), vc.getNAlleles()); + Assert.assertEquals(jEXLContext.get("FILTER"), VariantJEXLContext.true_string); + Assert.assertEquals(jEXLContext.get("homRefCount"), 0); + Assert.assertEquals(jEXLContext.get("homVarCount"), 0); + Assert.assertEquals(jEXLContext.get("hetCount"), 1); + Assert.assertEquals(jEXLContext.get("attr"), "notEmpty"); // hasAnyAttribute->getAnyAttribute + Assert.assertEquals(jEXLContext.get("q10"), VariantJEXLContext.true_string); // getFilters()!=null + + // all if-else fall through + Assert.assertNull(jEXLContext.get("mustBeNull")); + } + + @Test(expectedExceptions=UnsupportedOperationException.class) + public void testVariantJEXLContextSetMethodException(){ + getJEXLContext().set("noMatterWhat", "willBlowup"); + } + + /** + * @return a GenotypeJEXLContext for use by actual tests + */ + private VariantJEXLContext getJEXLContext(){ + return new GenotypeJEXLContext(vc, gt); } } From 78867688059bc03fdcd318227e9713d683b8b8d0 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Tue, 13 Sep 2016 16:06:29 -0400 Subject: [PATCH 015/137] Added a secondary constructor to IntervalList that takes a dict. (#703) --- src/main/java/htsjdk/samtools/SAMFileHeader.java | 6 ++++++ src/main/java/htsjdk/samtools/util/IntervalList.java | 11 +++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMFileHeader.java b/src/main/java/htsjdk/samtools/SAMFileHeader.java index 22d18a627..47543c2a6 100644 --- a/src/main/java/htsjdk/samtools/SAMFileHeader.java +++ b/src/main/java/htsjdk/samtools/SAMFileHeader.java @@ -120,6 +120,12 @@ public SAMFileHeader() { setAttribute(VERSION_TAG, CURRENT_VERSION); } + /** Constructor that initializes the sequence dictionary with the provided one. */ + public SAMFileHeader(final SAMSequenceDictionary dict) { + this(); + setSequenceDictionary(dict); + } + public String getVersion() { return (String) getAttribute("VN"); } diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index 8b46a1ceb..32b7176f5 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -71,12 +71,15 @@ /** Constructs a new interval list using the supplied header information. */ public IntervalList(final SAMFileHeader header) { - if (header == null) { - throw new IllegalArgumentException("SAMFileHeader must be supplied."); - } + if (header == null) throw new IllegalArgumentException("SAMFileHeader must be supplied."); this.header = header; } + /** Constructs a new interval list using the supplied header information. */ + public IntervalList(final SAMSequenceDictionary dict) { + this(new SAMFileHeader(dict)); + } + /** Gets the header (if there is one) for the interval list. */ public SAMFileHeader getHeader() { return header; } @@ -769,4 +772,4 @@ public int compare(final Interval lhs, final Interval rhs) { return retval; } -} \ No newline at end of file +} From 255e41a72dcd91fdd4f0c398b05c8e8a50894834 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Wed, 14 Sep 2016 15:55:31 -0400 Subject: [PATCH 016/137] annotating deprecated methods with @Deprecated (#698) some methods that were documented as deprecated were not also annotated with @Deprecated annotating them as deprecated allows tools to warn when these methods are accidentally used also updating documentation of deprecated methods to point to the replacement method with a valid {@link} --- .../java/htsjdk/samtools/AbstractBAMFileIndex.java | 3 ++- .../htsjdk/samtools/AbstractSAMHeaderRecord.java | 3 ++- .../htsjdk/samtools/MergingSamRecordIterator.java | 3 ++- src/main/java/htsjdk/samtools/SAMRecord.java | 6 +++-- .../java/htsjdk/samtools/SAMSequenceRecord.java | 3 ++- src/main/java/htsjdk/samtools/SAMUtils.java | 3 ++- .../java/htsjdk/samtools/SamFileHeaderMerger.java | 30 +++++++++++++--------- .../java/htsjdk/samtools/SamFileValidator.java | 3 ++- .../java/htsjdk/samtools/sra/SRAAccession.java | 3 ++- src/main/java/htsjdk/samtools/util/TestUtil.java | 3 ++- 10 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java b/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java index 4475e005c..6bf28ef29 100644 --- a/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java +++ b/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java @@ -387,8 +387,9 @@ protected BitSet regionToBins(final int startPos, final int endPos) { } /** - * @deprecated Invoke htsjdk.samtools.Chunk#optimizeChunkList(java.util.List, long) directly. + * @deprecated Invoke {@link Chunk#optimizeChunkList} directly. */ + @Deprecated protected List optimizeChunkList(final List chunks, final long minimumOffset) { return Chunk.optimizeChunkList(chunks, minimumOffset); } diff --git a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java index 42d09db17..769a7a735 100644 --- a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java +++ b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java @@ -50,8 +50,9 @@ public String getAttribute(final String key) { * Otherwise, the value will be converted to a String with toString. * @param key attribute name * @param value attribute value - * @deprecated Use the version that takes a String value instead + * @deprecated Use {@link #setAttribute(String, String) instead */ + @Deprecated public void setAttribute(final String key, final Object value) { setAttribute(key, value == null? null: value.toString()); } diff --git a/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java b/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java index 6b790fe00..a294752de 100644 --- a/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java +++ b/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java @@ -50,8 +50,9 @@ * * @param headerMerger The merged header and contents of readers. * @param forcePresorted True to ensure that the iterator checks the headers of the readers for appropriate sort order. - * @deprecated replaced by (SamFileHeaderMerger, Collection, boolean) + * @deprecated replaced by {@link #MergingSamRecordIterator(SamFileHeaderMerger, Collection, boolean)} */ + @Deprecated public MergingSamRecordIterator(final SamFileHeaderMerger headerMerger, final boolean forcePresorted) { this(headerMerger, headerMerger.getReaders(), forcePresorted); } diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index eb76c2841..dcd1a230b 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -985,9 +985,10 @@ public void setProperPairFlag(final boolean flag) { /** * the query sequence itself is unmapped. This method name is misspelled. - * Use setReadUnmappedFlag instead. + * Use {@link #setReadUnmappedFlag} instead. * @deprecated */ + @Deprecated public void setReadUmappedFlag(final boolean flag) { setReadUnmappedFlag(flag); } @@ -1648,8 +1649,9 @@ public int getAttributesBinarySize() { * * @return String representation of this. * @deprecated This method is not guaranteed to return a valid SAM text representation of the SAMRecord. - * To get standard SAM text representation, use htsjdk.samtools.SAMRecord#getSAMString(). + * To get standard SAM text representation, {@link SAMRecord#getSAMString}. */ + @Deprecated public String format() { final StringBuilder buffer = new StringBuilder(); addField(buffer, getReadName(), null, null); diff --git a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java index cbc2b7a2b..6bca979cc 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java @@ -82,9 +82,10 @@ private SAMSequenceRecord() { } /** - * @deprecated Use SAMSequenceRecord(final String name, final int sequenceLength) instead. + * @deprecated Use {@link #SAMSequenceRecord(String, int)} instead. * sequenceLength is required for the object to be considered valid. */ + @Deprecated public SAMSequenceRecord(final String name) { this(name, UNKNOWN_SEQUENCE_LENGTH); } diff --git a/src/main/java/htsjdk/samtools/SAMUtils.java b/src/main/java/htsjdk/samtools/SAMUtils.java index c0432acc8..685cb025f 100644 --- a/src/main/java/htsjdk/samtools/SAMUtils.java +++ b/src/main/java/htsjdk/samtools/SAMUtils.java @@ -421,8 +421,9 @@ public static int fastqToPhred(final char ch) { * * @param beg 0-based start of read (inclusive) * @param end 0-based end of read (exclusive) - * @deprecated Use GenomicIndexUtil.regionToBin + * @deprecated Use {@link GenomicIndexUtil#regionToBin} */ + @Deprecated static int reg2bin(final int beg, final int end) { return GenomicIndexUtil.regionToBin(beg, end); } diff --git a/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java b/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java index b162cb2e6..b3f588caa 100644 --- a/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java +++ b/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java @@ -122,8 +122,9 @@ public int compare(final AbstractSAMHeaderRecord o1, final AbstractSAMHeaderReco * * @param readers sam file readers to combine * @param sortOrder sort order new header should have - * @deprecated replaced by SamFileHeaderMerger(Collection, SAMFileHeader.SortOrder, boolean) + * @deprecated replaced by {@link #SamFileHeaderMerger(SAMFileHeader.SortOrder, Collection, boolean)} */ + @Deprecated public SamFileHeaderMerger(final Collection readers, final SAMFileHeader.SortOrder sortOrder) { this(readers, sortOrder, false); } @@ -135,8 +136,9 @@ public SamFileHeaderMerger(final Collection readers, final SAMFileHea * @param sortOrder sort order new header should have * @param mergeDictionaries If true, merge sequence dictionaries in new header. If false, require that * all input sequence dictionaries be identical. - * @deprecated replaced by SamFileHeaderMerger(Collection, SAMFileHeader.SortOrder, boolean) + * @deprecated replaced by {@link #SamFileHeaderMerger(SAMFileHeader.SortOrder, Collection, boolean)} */ + @Deprecated public SamFileHeaderMerger(final Collection readers, final SAMFileHeader.SortOrder sortOrder, final boolean mergeDictionaries) { this(sortOrder, getHeadersFromReaders(readers), mergeDictionaries); this.readers = readers; @@ -188,7 +190,7 @@ public SamFileHeaderMerger(final SAMFileHeader.SortOrder sortOrder, final Collec } } - // Utilility method to make use with old constructor + // Utility method to make use with old constructor private static List getHeadersFromReaders(final Collection readers) { final List headers = new ArrayList(readers.size()); for (final SamReader reader : readers) { @@ -585,7 +587,7 @@ private SAMSequenceDictionary mergeSequences(final SAMSequenceDictionary mergeIn // Since sequenceRecord already exists in resultingDict, don't need to add it. // Add in all the sequences prior to it that have been held in holder. resultingDict.addAll(loc, holder); - // Remember the index of sequenceRecord so can check for merge imcompatibility. + // Remember the index of sequenceRecord so can check for merge incompatibility. prevloc = loc + holder.size(); previouslyMerged = sequenceRecord; holder.clear(); @@ -622,12 +624,12 @@ private static int getIndexOfSequenceName(final List list, fi * @param masterDictionary the superset dictionary we've created. */ private void createSequenceMapping(final Collection headers, final SAMSequenceDictionary masterDictionary) { - final LinkedList resultingDictStr = new LinkedList(); + final LinkedList resultingDictStr = new LinkedList<>(); for (final SAMSequenceRecord r : masterDictionary.getSequences()) { resultingDictStr.add(r.getSequenceName()); } for (final SAMFileHeader header : headers) { - final Map seqMap = new HashMap(); + final Map seqMap = new HashMap<>(); final SAMSequenceDictionary dict = header.getSequenceDictionary(); for (final SAMSequenceRecord rec : dict.getSequences()) { seqMap.put(rec.getSequenceIndex(), resultingDictStr.indexOf(rec.getSequenceName())); @@ -640,8 +642,9 @@ private void createSequenceMapping(final Collection headers, fina /** * Returns the read group id that should be used for the input read and RG id. * - * @deprecated replaced by getReadGroupId(SAMFileHeader, String) + * @deprecated replaced by {@link #getReadGroupId(SAMFileHeader, String)} */ + @Deprecated public String getReadGroupId(final SamReader reader, final String originalReadGroupId) { return getReadGroupId(reader.getFileHeader(), originalReadGroupId); } @@ -655,8 +658,9 @@ public String getReadGroupId(final SAMFileHeader header, final String originalRe * @param reader one of the input files * @param originalProgramGroupId a program group ID from the above input file * @return new ID from the merged list of program groups in the output file - * @deprecated replaced by getProgramGroupId(SAMFileHeader, String) + * @deprecated replaced by {@link #getProgramGroupId(SAMFileHeader, String)} */ + @Deprecated public String getProgramGroupId(final SamReader reader, final String originalProgramGroupId) { return getProgramGroupId(reader.getFileHeader(), originalProgramGroupId); } @@ -693,8 +697,9 @@ public SAMFileHeader getMergedHeader() { /** * Returns the collection of readers that this header merger is working with. May return null. * - * @deprecated replaced by getHeaders() + * @deprecated replaced by {@link #getHeaders()} */ + @Deprecated public Collection getReaders() { return this.readers; } @@ -712,8 +717,9 @@ public SAMFileHeader getMergedHeader() { * @param reader the reader * @param oldReferenceSequenceIndex the old sequence (also called reference) index * @return the new index value - * @deprecated replaced by getMergedSequenceIndex(SAMFileHeader, Integer) + * @deprecated replaced by {@link #getMergedSequenceIndex(SAMFileHeader, Integer)} */ + @Deprecated public Integer getMergedSequenceIndex(final SamReader reader, final Integer oldReferenceSequenceIndex) { return this.getMergedSequenceIndex(reader.getFileHeader(), oldReferenceSequenceIndex); } @@ -745,7 +751,7 @@ public Integer getMergedSequenceIndex(final SAMFileHeader header, final Integer * Implementations of this interface are used by mergeHeaderRecords(..) to instantiate * specific subclasses of AbstractSAMHeaderRecord. */ - private static interface HeaderRecordFactory { + private interface HeaderRecordFactory { /** * Constructs a new instance of RecordType. @@ -753,7 +759,7 @@ public Integer getMergedSequenceIndex(final SAMFileHeader header, final Integer * @param id The id of the new record. * @param srcRecord Except for the id, the new record will be a copy of this source record. */ - public RecordType createRecord(final String id, RecordType srcRecord); + RecordType createRecord(final String id, RecordType srcRecord); } /** diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index 3a6deb028..fb559b0fb 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -588,8 +588,9 @@ public void setBisulfiteSequenced(boolean bisulfiteSequenced) { } /** - * @deprecated use setIndexValidationStringency instead + * @deprecated use {@link #setIndexValidationStringency} instead */ + @Deprecated public SamFileValidator setValidateIndex(final boolean validateIndex) { // The SAMFileReader must also have IndexCaching enabled to have the index validated, return this.setIndexValidationStringency(validateIndex ? IndexValidationStringency.EXHAUSTIVE : IndexValidationStringency.NONE); diff --git a/src/main/java/htsjdk/samtools/sra/SRAAccession.java b/src/main/java/htsjdk/samtools/sra/SRAAccession.java index 17180d771..9aeb10fe8 100644 --- a/src/main/java/htsjdk/samtools/sra/SRAAccession.java +++ b/src/main/java/htsjdk/samtools/sra/SRAAccession.java @@ -74,10 +74,11 @@ public static void setAppVersionString(String appVersionString) { } /** - * @deprecated * @return true if SRA successfully loaded native libraries and fully initialized, * false otherwise + * @deprecated use {@link #checkIfInitialized} instead */ + @Deprecated public static boolean isSupported() { return checkIfInitialized() == null; } diff --git a/src/main/java/htsjdk/samtools/util/TestUtil.java b/src/main/java/htsjdk/samtools/util/TestUtil.java index bbdf464d9..fd840d145 100644 --- a/src/main/java/htsjdk/samtools/util/TestUtil.java +++ b/src/main/java/htsjdk/samtools/util/TestUtil.java @@ -50,8 +50,9 @@ public static File getTempDirectory(final String prefix, final String suffix) { } /** - * @deprecated Use properly spelled method. + * @deprecated Use properly spelled method. {@link #getTempDirectory} */ + @Deprecated public static File getTempDirecory(final String prefix, final String suffix) { return getTempDirectory(prefix, suffix); } From 0e6edc78bf9859e9b3f91f1cee330f58a5b1ed61 Mon Sep 17 00:00:00 2001 From: jamesemery Date: Thu, 15 Sep 2016 11:13:12 -0500 Subject: [PATCH 017/137] Added support for an empty NM tag in SA tag fields using '*' character (#693) previously an empty NM tag would result in a 0 in the SA field, this was deceptive since having no mismatches is not the same as having an unknown number of mismatches. Now a null NM tag results in * to match other null fields. --- src/main/java/htsjdk/samtools/SAMUtils.java | 60 +++++++++++++------------ src/test/java/htsjdk/samtools/SAMUtilsTest.java | 27 ++++++----- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMUtils.java b/src/main/java/htsjdk/samtools/SAMUtils.java index 685cb025f..25b6799c7 100644 --- a/src/main/java/htsjdk/samtools/SAMUtils.java +++ b/src/main/java/htsjdk/samtools/SAMUtils.java @@ -1103,7 +1103,7 @@ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, fina public static boolean isValidUnsignedIntegerAttribute(long value) { return value >= 0 && value <= BinaryCodec.MAX_UINT; } - + /** * Extract a List of 'other canonical alignments' from a SAM record. Those alignments are stored as a string in the 'SA' tag as defined * in the SAM specification. @@ -1119,40 +1119,40 @@ public static boolean isValidUnsignedIntegerAttribute(long value) { if( saValue == null ) return Collections.emptyList(); if( ! (saValue instanceof String) ) throw new SAMException( "Expected a String for attribute 'SA' but got " + saValue.getClass() ); - + final SAMRecordFactory samReaderFactory = new DefaultSAMRecordFactory(); - - /* the spec says: "Other canonical alignments in a chimeric alignment, formatted as a - * semicolon-delimited list: (rname,pos,strand,CIGAR,mapQ,NM;)+. + + /* the spec says: "Other canonical alignments in a chimeric alignment, formatted as a + * semicolon-delimited list: (rname,pos,strand,CIGAR,mapQ,NM;)+. * Each element in the list represents a part of the chimeric alignment. * Conventionally, at a supplementary line, the 1rst element points to the primary line. */ - + /* break string using semicolon */ final String semiColonStrs[] = SEMICOLON_PAT.split((String)saValue); - + /* the result list */ final List alignments = new ArrayList<>( semiColonStrs.length ); - + /* base SAM flag */ int record_flag = record.getFlags() ; record_flag &= ~SAMFlag.PROPER_PAIR.flag; record_flag &= ~SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; record_flag &= ~SAMFlag.READ_REVERSE_STRAND.flag; - - + + for(int i=0; i< semiColonStrs.length;++i ) { final String semiColonStr = semiColonStrs[i]; /* ignore empty string */ if( semiColonStr.isEmpty() ) continue; - + /* break string using comma */ final String commaStrs[] = COMMA_PAT.split(semiColonStr); if( commaStrs.length != 6 ) throw new SAMException("Bad 'SA' attribute in " + semiColonStr); - + /* create the new record */ final SAMRecord otherRec = samReaderFactory.createSAMRecord( record.getHeader() ); - + /* copy fields from the original record */ otherRec.setReadName( record.getReadName() ); otherRec.setReadBases( record.getReadBases() ); @@ -1161,13 +1161,13 @@ public static boolean isValidUnsignedIntegerAttribute(long value) { otherRec.setMateReferenceIndex( record.getMateReferenceIndex() ); otherRec.setMateAlignmentStart( record.getMateAlignmentStart() ); } - - + + /* get reference sequence */ final int tid = record.getHeader().getSequenceIndex( commaStrs[0] ); if( tid == -1 ) throw new SAMException("Unknown contig in " + semiColonStr); otherRec.setReferenceIndex( tid ); - + /* fill POS */ final int alignStart; try { @@ -1175,47 +1175,49 @@ public static boolean isValidUnsignedIntegerAttribute(long value) { } catch( final NumberFormatException err ) { throw new SAMException("bad POS in "+semiColonStr, err); } - - otherRec.setAlignmentStart( alignStart ); - + + otherRec.setAlignmentStart( alignStart ); + /* set TLEN */ - if( record.getReadPairedFlag() && - !record.getMateUnmappedFlag() && + if( record.getReadPairedFlag() && + !record.getMateUnmappedFlag() && record.getMateReferenceIndex() == tid ) { otherRec.setInferredInsertSize( record.getMateAlignmentStart() - alignStart ); } - /* set FLAG */ + /* set FLAG */ int other_flag = record_flag; other_flag |= (commaStrs[2].equals("+") ? 0 : SAMFlag.READ_REVERSE_STRAND.flag) ; /* spec: Conventionally, at a supplementary line, the 1st element points to the primary line */ if( !( record.getSupplementaryAlignmentFlag() && i==0 ) ) { other_flag |= SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; - } + } otherRec.setFlags(other_flag); - + /* set CIGAR */ otherRec.setCigar( TextCigarCodec.decode( commaStrs[3] ) ); - + /* set MAPQ */ try { otherRec.setMappingQuality( Integer.parseInt(commaStrs[4]) ); } catch (final NumberFormatException err) { throw new SAMException("bad MAPQ in "+semiColonStr, err); } - + /* fill NM */ try { - otherRec.setAttribute( SAMTagUtil.getSingleton().NM , Integer.parseInt(commaStrs[5]) ); + if (!commaStrs[5].equals("*")) { + otherRec.setAttribute(SAMTagUtil.getSingleton().NM, Integer.parseInt(commaStrs[5])); + } } catch (final NumberFormatException err) { throw new SAMException("bad NM in "+semiColonStr, err); } - + /* if strand is not the same: reverse-complement */ if( otherRec.getReadNegativeStrandFlag() != record.getReadNegativeStrandFlag() ) { SAMRecordUtil.reverseComplement(otherRec); } - + /* add the alignment */ alignments.add( otherRec ); } diff --git a/src/test/java/htsjdk/samtools/SAMUtilsTest.java b/src/test/java/htsjdk/samtools/SAMUtilsTest.java index 0fa6b4a69..3be7e390c 100644 --- a/src/test/java/htsjdk/samtools/SAMUtilsTest.java +++ b/src/test/java/htsjdk/samtools/SAMUtilsTest.java @@ -173,7 +173,7 @@ public void testClippingOfRecordWithMateAtSamePosition() { record.setSecondOfPairFlag(true); Assert.assertEquals(SAMUtils.getNumOverlappingAlignedBasesToClip(record), 10); } - + @Test public void testOtherCanonicalAlignments() { // setup the record @@ -190,26 +190,26 @@ public void testOtherCanonicalAlignments() { record.setMateAlignmentStart(1); record.setReadPairedFlag(true); record.setSupplementaryAlignmentFlag(true);//spec says first 'SA' record will be the primary record - + record.setMateReferenceIndex(0); record.setMateAlignmentStart(100); record.setInferredInsertSize(99); - + record.setReadBases("AAAAAAAAAA".getBytes()); record.setBaseQualities("##########".getBytes()); // check no alignments if no SA tag */ Assert.assertEquals(SAMUtils.getOtherCanonicalAlignments(record).size(),0); - - + + record.setAttribute(SAMTagUtil.getSingleton().SA, - "2,500,+,3S2=1X2=2S,60,1;" + - "1,191,-,8M2S,60,0;"); - + "2,500,+,3S2=1X2=2S,60,1;" + + "1,191,-,8M2S,60,*;"); + // extract suppl alignments final List suppl = SAMUtils.getOtherCanonicalAlignments(record); Assert.assertNotNull(suppl); Assert.assertEquals(suppl.size(), 2); - + for(final SAMRecord other: suppl) { Assert.assertFalse(other.getReadUnmappedFlag()); Assert.assertTrue(other.getReadPairedFlag()); @@ -223,7 +223,7 @@ public void testOtherCanonicalAlignments() { Assert.assertEquals(other.getBaseQualityString(),record.getBaseQualityString()); } } - + SAMRecord other = suppl.get(0); Assert.assertFalse(other.getSupplementaryAlignmentFlag());//1st of suppl and 'record' is supplementary Assert.assertEquals(other.getReferenceName(),"2"); @@ -234,18 +234,17 @@ public void testOtherCanonicalAlignments() { Assert.assertEquals(other.getCigarString(),"3S2=1X2=2S"); Assert.assertEquals(other.getInferredInsertSize(),0); - + other = suppl.get(1); Assert.assertTrue(other.getSupplementaryAlignmentFlag()); Assert.assertEquals(other.getReferenceName(),"1"); Assert.assertEquals(other.getAlignmentStart(),191); Assert.assertTrue(other.getReadNegativeStrandFlag()); Assert.assertEquals(other.getMappingQuality(), 60); - Assert.assertEquals(other.getAttribute(SAMTagUtil.getSingleton().NM),0); + Assert.assertEquals(other.getAttribute(SAMTagUtil.getSingleton().NM),null); Assert.assertEquals(other.getCigarString(),"8M2S"); Assert.assertEquals(other.getInferredInsertSize(),-91);//100(mate) - 191(other) - } - + } From fbba5364e1809de071bc479f30e4e2c8b17f5bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 16 Sep 2016 20:15:55 +0200 Subject: [PATCH 018/137] adding additional write method to Index, fixes #430 (#683) Adding Index.write(file) to the index interface. This is a breaking change which will affect anyone who implements Index or calls TabixFile.write. This fixes an issue where IndexFactory.writeIndex was incorrectly failing to compress tabix index files. (fix #430 ) TabixIndex.write(file) now throws IOException instead of TribbleException. This makes it consistent with the write(stream) methods. Call sites must now catch or throw IOException. deprecated IndexFactory.writeIndex(index, file) because it is now redundant, replace with index.write(file) --- .../java/htsjdk/tribble/index/AbstractIndex.java | 13 +++--- src/main/java/htsjdk/tribble/index/Index.java | 8 ++++ .../java/htsjdk/tribble/index/IndexFactory.java | 16 ++------ .../htsjdk/tribble/index/tabix/TabixIndex.java | 9 ++--- .../java/htsjdk/tribble/FeatureReaderTest.java | 2 +- .../htsjdk/tribble/index/IndexFactoryTest.java | 5 +++ src/test/java/htsjdk/tribble/index/IndexTest.java | 47 ++++++++++++++++++++++ .../htsjdk/tribble/index/tabix/TabixIndexTest.java | 3 +- 8 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/main/java/htsjdk/tribble/index/AbstractIndex.java b/src/main/java/htsjdk/tribble/index/AbstractIndex.java index 42bce732d..47e31ccef 100644 --- a/src/main/java/htsjdk/tribble/index/AbstractIndex.java +++ b/src/main/java/htsjdk/tribble/index/AbstractIndex.java @@ -343,13 +343,16 @@ public void write(final LittleEndianOutputStream stream) throws IOException { } @Override + public void write(final File idxFile) throws IOException { + try(final LittleEndianOutputStream idxStream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)))) { + write(idxStream); + } + } + + @Override public void writeBasedOnFeatureFile(final File featureFile) throws IOException { if (!featureFile.isFile()) return; - final LittleEndianOutputStream idxStream = - new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(Tribble.indexFile(featureFile)))); - write(idxStream); - idxStream.close(); - + write(Tribble.indexFile(featureFile)); } public void read(final LittleEndianInputStream dis) throws IOException { diff --git a/src/main/java/htsjdk/tribble/index/Index.java b/src/main/java/htsjdk/tribble/index/Index.java index 252bc95e5..ca6cc60d3 100644 --- a/src/main/java/htsjdk/tribble/index/Index.java +++ b/src/main/java/htsjdk/tribble/index/Index.java @@ -70,6 +70,14 @@ public void write(LittleEndianOutputStream stream) throws IOException; /** + * Writes the index into a file. + * + * @param idxFile Where to write the index. + * @throws IOException if the index is unable to write to the specified file + */ + public void write(final File idxFile) throws IOException; + + /** * Write an appropriately named and located Index file based on the name and location of the featureFile. * If featureFile is not a normal file, the index will silently not be written. * @param featureFile diff --git a/src/main/java/htsjdk/tribble/index/IndexFactory.java b/src/main/java/htsjdk/tribble/index/IndexFactory.java index a588220dc..3cd1b7958 100644 --- a/src/main/java/htsjdk/tribble/index/IndexFactory.java +++ b/src/main/java/htsjdk/tribble/index/IndexFactory.java @@ -41,16 +41,13 @@ import htsjdk.tribble.index.tabix.TabixIndexCreator; import htsjdk.tribble.readers.PositionalBufferedStream; import htsjdk.tribble.util.LittleEndianInputStream; -import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.tribble.util.ParsingUtils; import htsjdk.tribble.util.TabixUtils; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -288,18 +285,11 @@ public static LinearIndex createLinearIndex(final File inputFile, final FeatureC * @param idx * @param idxFile * @throws IOException + * @deprecated use {@link Index#write(File)} instead */ + @Deprecated public static void writeIndex(final Index idx, final File idxFile) throws IOException { - LittleEndianOutputStream stream = null; - try { - stream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile))); - idx.write(stream); - } - finally { - if(stream != null) { - stream.close(); - } - } + idx.write(idxFile); } /** diff --git a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java index 9ab05d42e..044cefe61 100644 --- a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java +++ b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java @@ -201,13 +201,10 @@ public TabixFormat getFormatSpec() { * * @param tabixFile Where to write the index. */ - public void write(final File tabixFile) { - final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(tabixFile)); - try { + @Override + public void write(final File tabixFile) throws IOException { + try(final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(tabixFile))) { write(los); - los.close(); - } catch (final IOException e) { - throw new TribbleException("Exception writing " + tabixFile.getAbsolutePath(), e); } } diff --git a/src/test/java/htsjdk/tribble/FeatureReaderTest.java b/src/test/java/htsjdk/tribble/FeatureReaderTest.java index ac3405939..d62693c19 100644 --- a/src/test/java/htsjdk/tribble/FeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/FeatureReaderTest.java @@ -134,7 +134,7 @@ public void testBedNames(final File featureFile, final IndexFactory.IndexType in idxFile.delete(); } final Index idx = IndexFactory.createIndex(featureFile, codec, indexType); - IndexFactory.writeIndex(idx, idxFile); + idx.write(idxFile); idxFile.deleteOnExit(); } // else let's just hope the index exists, and if so use it diff --git a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java index ba64998d3..016049f32 100644 --- a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java @@ -25,11 +25,14 @@ import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.util.IOUtil; import htsjdk.tribble.TestUtils; import htsjdk.tribble.TribbleException; import htsjdk.tribble.bed.BEDCodec; +import htsjdk.tribble.index.linear.LinearIndex; import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.index.tabix.TabixIndex; +import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.variant.vcf.VCFCodec; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; @@ -37,6 +40,8 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; import java.util.List; /** diff --git a/src/test/java/htsjdk/tribble/index/IndexTest.java b/src/test/java/htsjdk/tribble/index/IndexTest.java index 8104a0803..aa179a9a2 100644 --- a/src/test/java/htsjdk/tribble/index/IndexTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexTest.java @@ -1,13 +1,23 @@ package htsjdk.tribble.index; +import htsjdk.samtools.util.IOUtil; +import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.TestUtils; +import htsjdk.tribble.Tribble; +import htsjdk.tribble.bed.BEDCodec; import htsjdk.tribble.index.linear.LinearIndex; +import htsjdk.tribble.index.tabix.TabixFormat; +import htsjdk.tribble.index.tabix.TabixIndex; +import htsjdk.tribble.util.LittleEndianOutputStream; +import htsjdk.tribble.util.TabixUtils; +import htsjdk.variant.vcf.VCFCodec; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -47,4 +57,41 @@ public void testMassiveQuery(final int start, final int mid, final int mid2, fin Assert.assertTrue(allSize >= Math.max(leftSize,rightSize), "Expected size of joint query " + allSize + " to be at least >= max of left " + leftSize + " and right queries " + rightSize); } + + + @DataProvider(name = "writeIndexData") + public Object[][] writeIndexData() { + return new Object[][]{ + {new File("src/test/resources/htsjdk/tribble/tabix/testTabixIndex.vcf"), IndexFactory.IndexType.LINEAR, new VCFCodec()}, + {new File("src/test/resources/htsjdk/tribble/tabix/testTabixIndex.vcf.gz"), IndexFactory.IndexType.TABIX, new VCFCodec()}, + {new File("src/test/resources/htsjdk/tribble/test.bed"), IndexFactory.IndexType.LINEAR, new BEDCodec()} + }; + } + + private final static OutputStream nullOutputStrem = new OutputStream() { + @Override + public void write(int b) throws IOException { } + }; + + @Test(dataProvider = "writeIndexData") + public void testWriteIndex(final File inputFile, final IndexFactory.IndexType type, final FeatureCodec codec) throws Exception { + // temp index file for this test + final File tempIndex = File.createTempFile("index", (type == IndexFactory.IndexType.TABIX) ? TabixUtils.STANDARD_INDEX_EXTENSION : Tribble.STANDARD_INDEX_EXTENSION); + tempIndex.delete(); + tempIndex.deleteOnExit(); + // create the index + final Index index = IndexFactory.createIndex(inputFile, codec, type); + Assert.assertFalse(tempIndex.exists()); + // write the index to a file + index.write(tempIndex); + Assert.assertTrue(tempIndex.exists()); + // load the generated index + final Index loadedIndex = IndexFactory.loadIndex(tempIndex.getAbsolutePath()); + // tess that the sequences and properties are the same + Assert.assertEquals(loadedIndex.getSequenceNames(), index.getSequenceNames()); + Assert.assertEquals(loadedIndex.getProperties(), index.getProperties()); + // test that write to a stream does not blows ip + index.write(new LittleEndianOutputStream(nullOutputStrem)); + } + } diff --git a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java index 557a3987d..6981b8751 100644 --- a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java +++ b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java @@ -37,6 +37,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; import java.util.Iterator; public class TabixIndexTest { @@ -71,7 +72,7 @@ public void readWriteTest(final File tabixFile) throws Exception { } @Test - public void testQueryProvidedItemsAmount() { + public void testQueryProvidedItemsAmount() throws IOException { final String VCF = "src/test/resources/htsjdk/tribble/tabix/YRI.trio.2010_07.indel.sites.vcf"; // Note that we store only compressed files final File plainTextVcfInputFile = new File(VCF); From b5fd3c02b3140b8bab4ec7436c1e21e4604a63f4 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Mon, 19 Sep 2016 13:44:43 -0400 Subject: [PATCH 019/137] fixed the text codec to enable empty strings TA:Z:"" as per latest hts-spec * Added a test to show that htsJDK can handle empty strings as tag values. * - fixed the text codec to enable empty strings - testing that we can code and decode into various formats - Enables the reading and writing of sam/bam/cram records with empty string tags as required by samtools/hts-specs#135 --- src/main/java/htsjdk/samtools/SAMRecord.java | 2 +- src/main/java/htsjdk/samtools/TextTagCodec.java | 5 ++- .../java/htsjdk/samtools/SAMIntegerTagTest.java | 46 ++++++++++++++++++++++ .../java/htsjdk/samtools/SAMRecordUnitTest.java | 13 ++++++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index dcd1a230b..a22ded540 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -1397,7 +1397,7 @@ protected void setAttribute(final short tag, final Object value) { * @deprecated * The attribute type and value checks have been moved directly into - * {@code SAMBinaryTagAndValue}. + * {@link SAMBinaryTagAndValue}. */ @Deprecated protected static boolean isAllowedAttributeValue(final Object value) { diff --git a/src/main/java/htsjdk/samtools/TextTagCodec.java b/src/main/java/htsjdk/samtools/TextTagCodec.java index 0fae20269..60363e160 100644 --- a/src/main/java/htsjdk/samtools/TextTagCodec.java +++ b/src/main/java/htsjdk/samtools/TextTagCodec.java @@ -41,6 +41,7 @@ * instance is used in multiple threads. */ public class TextTagCodec { + // 3 fields for non-empty strings 2 fields if the string is empty. private static final int NUM_TAG_FIELDS = 3; /** @@ -149,12 +150,12 @@ public String encodeUntypedTag(final String tagName, final Object value) { */ public Map.Entry decode(final String tag) { final int numFields = StringUtil.splitConcatenateExcessTokens(tag, fields, ':'); - if (numFields != TextTagCodec.NUM_TAG_FIELDS) { + if (numFields != TextTagCodec.NUM_TAG_FIELDS && numFields != TextTagCodec.NUM_TAG_FIELDS - 1) { throw new SAMFormatException("Not enough fields in tag '" + tag + "'"); } final String key = fields[0]; final String type = fields[1]; - final String stringVal = fields[2]; + final String stringVal = numFields == TextTagCodec.NUM_TAG_FIELDS ? fields[2] : ""; final Object val = convertStringToObject(type, stringVal); return new Map.Entry() { public String getKey() { diff --git a/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java b/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java index 2d78a78dc..133062a15 100644 --- a/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java +++ b/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java @@ -98,6 +98,52 @@ public void testGetTypedAttributeMethods() throws Exception { Assert.assertEquals(rec.getIntegerAttribute(INTEGER_TAG).intValue(), 1); } + + @DataProvider + public Object[][] formatsAndValues(){ + return new Object[][]{ + new Object[]{"sam","Hello World!"}, + new Object[]{"bam","Hello World!"}, + new Object[]{"cram","Hello World!"}, + new Object[]{"cram",""}, + new Object[]{"bam",""}, + new Object[]{"sam",""}, + }; + } + /** + * Should be able to write empty and non-empty strings + */ + @Test(dataProvider = "formatsAndValues") + public void testWriteAndReadStrings(final String format,final String value) throws Exception { + final SAMRecord rec = createSamRecord(); + rec.setAttribute(STRING_TAG, value); + writeAndReadSamRecord(format, rec); + Assert.assertEquals(rec.getStringAttribute(STRING_TAG),value); + } + + + @DataProvider + public Object[][] formatsAndValues2(){ + return new Object[][]{ + new Object[]{"sam",'a'}, + new Object[]{"bam",'a'}, + new Object[]{"cram",'a'}, + new Object[]{"cram",null}, + new Object[]{"bam",null}, + new Object[]{"sam",null}, + }; + } + /** + * Should be able to write empty and non-empty strings + */ + @Test(dataProvider = "formatsAndValues2") + public void testWriteAndReadCharacters(final String format,final Character value) throws Exception { + final SAMRecord rec = createSamRecord(); + rec.setAttribute(STRING_TAG, value); + writeAndReadSamRecord(format, rec); + Assert.assertEquals(rec.getCharacterAttribute(STRING_TAG),value); + } + /** * Should be an exception if a typed attribute call is made for the wrong type. */ diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index a8f06e45a..15b732607 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -417,6 +417,19 @@ public void test_isAllowedAttributeDataType() { Assert.assertFalse(SAMRecord.isAllowedAttributeValue(new Long(Integer.MIN_VALUE - 1L))); } + @Test() + public void test_setAttribute_empty_string() { + final SAMFileHeader header = new SAMFileHeader(); + final SAMRecord record = new SAMRecord(header); + Assert.assertNull(record.getStringAttribute(SAMTag.MD.name())); + record.setAttribute(SAMTag.MD.name(), ""); + Assert.assertNotNull(record.getStringAttribute(SAMTag.MD.name())); + Assert.assertEquals(record.getStringAttribute(SAMTag.MD.name()),""); + record.setAttribute(SAMTag.MD.name(), null); + Assert.assertNull(record.getStringAttribute(SAMTag.MD.name())); + } + + @Test(expectedExceptions = IllegalArgumentException.class) public void test_setAttribute_unsigned_int_negative() { SAMFileHeader header = new SAMFileHeader(); From 224cfc1f68f49d63c8d9e72dfc677703379ecd15 Mon Sep 17 00:00:00 2001 From: Mark Fleharty Date: Tue, 20 Sep 2016 10:20:12 -0400 Subject: [PATCH 020/137] adding Hamming Distance functions to StringUtil (#690) adding hammingDistance and isWithinHammingDistance functions to StringUtils hamming distance is the number of mismatches between equal length strings --- src/main/java/htsjdk/samtools/util/StringUtil.java | 55 ++++++++++++++++++++++ .../java/htsjdk/samtools/util/StringUtilTest.java | 52 ++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/main/java/htsjdk/samtools/util/StringUtil.java b/src/main/java/htsjdk/samtools/util/StringUtil.java index ecb1b3f49..90492533e 100644 --- a/src/main/java/htsjdk/samtools/util/StringUtil.java +++ b/src/main/java/htsjdk/samtools/util/StringUtil.java @@ -545,4 +545,59 @@ public static int levenshteinDistance(final String string1, final String string2 return i; } + + /** + * Calculates the Hamming distance (number of character mismatches) between two strings s1 and s2. + * Since Hamming distance is not defined for strings of differing lengths, we throw an exception if + * the two strings are of different lengths. Hamming distance is case sensitive and does not have + * any special treatment for DNA. + * + * @param s1 The first string to compare + * @param s2 The second string to compare, note that if s1 and s2 are swapped the value returned will be identical. + * @return Hamming distance between s1 and s2. + * @throws IllegalArgumentException If the two strings have differing lengths. + */ + public static int hammingDistance(final String s1, final String s2) { + if (s1.length() != s2.length()) { + throw new IllegalArgumentException("Attempted to determine Hamming distance of strings with differing lengths. " + + "The first string has length " + s1.length() + " and the second string has length " + s2.length() + "."); + } + int measuredDistance = 0; + for (int i = 0;i < s1.length();i++) { + if (s1.charAt(i) != s2.charAt(i)) { + measuredDistance++; + } + } + return measuredDistance; + } + + /** + * Determines if two strings s1 and s2 are within maxHammingDistance of each other using the Hamming distance metric. + * Since Hamming distance is not defined for strings of differing lengths, we throw an exception if + * the two strings are of different lengths. Hamming distance is case sensitive and does not have any + * special treatment for DNA. + * + * @param s1 The first string to compare + * @param s2 The second string to compare, note that if s1 and s2 are swapped the value returned will be identical. + * @param maxHammingDistance The largest Hamming distance the strings can have for this function to return true. + * @return true if the two strings are within maxHammingDistance of each other, false otherwise. + * @throws IllegalArgumentException If the two strings have differing lengths. + */ + public static boolean isWithinHammingDistance(final String s1, final String s2, final int maxHammingDistance) { + if (s1.length() != s2.length()) { + throw new IllegalArgumentException("Attempted to determine if two strings of different length were within a specified edit distance."); + } + int measuredDistance = 0; + for (int i = 0;i < s1.length();i++) { + if (s1.charAt(i) != s2.charAt(i)) { + measuredDistance++; + // If the measuredDistance is larger than the maxHammingDistance we can short circuit and return + // false, there is no need to continue evaluating the distance. + if (measuredDistance > maxHammingDistance) { + return false; + } + } + } + return true; + } } diff --git a/src/test/java/htsjdk/samtools/util/StringUtilTest.java b/src/test/java/htsjdk/samtools/util/StringUtilTest.java index 91e8792f4..dbb2a0709 100644 --- a/src/test/java/htsjdk/samtools/util/StringUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/StringUtilTest.java @@ -67,4 +67,56 @@ public void testSplit(final String input, final String[] expectedResult, final b {"A:BB:C:", new String[]{"A", "BB", "C:"}, true}, }; } + + @DataProvider(name="withinHammingDistanceProvider") + public Object[][] isWithinHammingDistanceProvider() { + return new Object[][] { + {"ATAC", "GCAT", 3, true}, + {"ATAC", "GCAT", 2, false}, + {"ATAC", "GCAT", 1, false}, + {"ATAC", "GCAT", 0, false} + }; + } + + @Test(dataProvider = "withinHammingDistanceProvider") + public void testIsWithinHammingDistance(final String s1, final String s2, final int maxHammingDistance, final boolean expectedResult) { + Assert.assertEquals(StringUtil.isWithinHammingDistance(s1, s2, maxHammingDistance), expectedResult); + } + + @DataProvider(name="withinHammingDistanceExceptionProvider") + public Object[][] isWithinHammingDistanceException() { + return new Object[][] { + {"ATAC", "GCT" , 3}, + {"ATAC", "AT" , 2}, + {"ATAC", "T" , 1}, + {"" , "GCAT", 0} + }; + } + + @Test(dataProvider = "withinHammingDistanceExceptionProvider", expectedExceptions = IllegalArgumentException.class) + public void testIsWithinHammingDistanceExceptions(final String s1, final String s2, final int maxHammingDistance) { + StringUtil.isWithinHammingDistance(s1, s2, maxHammingDistance); + } + + @Test(dataProvider = "withinHammingDistanceExceptionProvider", expectedExceptions = IllegalArgumentException.class) + public void testHammingDistanceExceptions(final String s1, final String s2, final int maxHammingDistance) { + StringUtil.hammingDistance(s1, s2); + } + + @DataProvider(name="hammingDistanceProvider") + public Object[][] hammingDistance() { + return new Object[][] { + {"ATAC" , "GCAT" , 3}, + {"ATAGC", "ATAGC", 0}, + {"ATAC" , "atac" , 4}, // Hamming distance is case sensitive. + {"" , "" , 0}, // Two empty strings should have Hamming distance of 0. + {"nAGTN", "nAGTN", 0} // Ensure that matching Ns are not counted as mismatches. + }; + } + + @Test(dataProvider = "hammingDistanceProvider") + public void testHammingDistance(final String s1, final String s2, final int expectedResult) { + Assert.assertEquals(StringUtil.hammingDistance(s1, s2), expectedResult); + } + } From 88b671963a829ea5bba9a63cdaa6476cb8e875dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Wed, 21 Sep 2016 00:18:28 +0200 Subject: [PATCH 021/137] IndexingVariantContextWriter cleanup (#706) * extract PositionalOutputStream * extract setIndexSequenceDictionary to IndexCreator with no-op default and TribbleIndexCreator implementation --- .../samtools/util/PositionalOutputStream.java | 65 ++++++++++++++++++++++ .../java/htsjdk/tribble/index/IndexCreator.java | 7 +++ .../htsjdk/tribble/index/TribbleIndexCreator.java | 16 ++++++ .../writer/IndexingVariantContextWriter.java | 54 +----------------- .../samtools/util/PositionalOutputStreamTest.java | 62 +++++++++++++++++++++ 5 files changed, 152 insertions(+), 52 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/util/PositionalOutputStream.java create mode 100644 src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java diff --git a/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java b/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java new file mode 100644 index 000000000..ef28be610 --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2012 The Broad Institute +* +* 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 htsjdk.samtools.util; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Wraps output stream in a manner which keeps track of the position within the file and allowing writes + * at arbitrary points + */ +public final class PositionalOutputStream extends OutputStream implements LocationAware +{ + private final OutputStream out; + private long position = 0; + + public PositionalOutputStream(final OutputStream out) { + this.out = out; + } + + public final void write(final byte[] bytes) throws IOException { + write(bytes, 0, bytes.length); + } + + public final void write(final byte[] bytes, final int startIndex, final int numBytes) throws IOException { + position += numBytes; + out.write(bytes, startIndex, numBytes); + } + + public final void write(final int c) throws IOException { + position++; + out.write(c); + } + + public final long getPosition() { return position; } + + @Override + public void close() throws IOException { + super.close(); + out.close(); + } +} diff --git a/src/main/java/htsjdk/tribble/index/IndexCreator.java b/src/main/java/htsjdk/tribble/index/IndexCreator.java index 9b03d4489..c90ec9f34 100644 --- a/src/main/java/htsjdk/tribble/index/IndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/IndexCreator.java @@ -23,6 +23,7 @@ */ package htsjdk.tribble.index; +import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.tribble.Feature; /** @@ -45,6 +46,12 @@ * @return an index object */ public Index finalizeIndex(long finalFilePosition); + + /** + * Set the sequence dictionary for the index. Default implementation does nothing. + * @param dict the dictionary to add to the index. + */ + public default void setIndexSequenceDictionary(final SAMSequenceDictionary dict) { } } diff --git a/src/main/java/htsjdk/tribble/index/TribbleIndexCreator.java b/src/main/java/htsjdk/tribble/index/TribbleIndexCreator.java index fc42818fa..f7385e828 100644 --- a/src/main/java/htsjdk/tribble/index/TribbleIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/TribbleIndexCreator.java @@ -23,15 +23,31 @@ */ package htsjdk.tribble.index; +import htsjdk.samtools.SAMSequenceDictionary; +import htsjdk.samtools.SAMSequenceRecord; + import java.util.LinkedHashMap; /** * Base class for Tribble-specific index creators. */ public abstract class TribbleIndexCreator implements IndexCreator { + // a constant we use for marking sequence dictionary entries in the Tribble index property list + private static final String SEQUENCE_DICTIONARY_PROPERTY_PREDICATE = "DICT:"; + protected LinkedHashMap properties = new LinkedHashMap(); public void addProperty(final String key, final String value) { properties.put(key, value); } + + /** Set the sequence dictionary entries for the index property list. */ + @Override + public void setIndexSequenceDictionary(final SAMSequenceDictionary dict) { + for (final SAMSequenceRecord seq : dict.getSequences()) { + final String contig = SEQUENCE_DICTIONARY_PROPERTY_PREDICATE + seq.getSequenceName(); + final String length = String.valueOf(seq.getSequenceLength()); + addProperty(contig,length); + } + } } diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java index 5f0715332..6a77f6b3b 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java @@ -26,14 +26,13 @@ package htsjdk.variant.variantcontext.writer; import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SAMSequenceRecord; import htsjdk.samtools.util.LocationAware; import htsjdk.samtools.util.RuntimeIOException; import htsjdk.tribble.index.DynamicIndexCreator; import htsjdk.tribble.index.Index; import htsjdk.tribble.index.IndexCreator; import htsjdk.tribble.index.IndexFactory; -import htsjdk.tribble.index.TribbleIndexCreator; +import htsjdk.samtools.util.PositionalOutputStream; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.vcf.VCFHeader; @@ -139,9 +138,7 @@ public void close() { // close the index stream (keep it separate to help debugging efforts) if (indexer != null) { - if (indexer instanceof TribbleIndexCreator) { - setIndexSequenceDictionary((TribbleIndexCreator)indexer, refDict); - } + indexer.setIndexSequenceDictionary(refDict); final Index index = indexer.finalizeIndex(locationSource.getPosition()); index.writeBasedOnFeatureFile(location); } @@ -180,51 +177,4 @@ public void add(final VariantContext vc) { protected static final String writerName(final File location, final OutputStream stream) { return location == null ? stream.toString() : location.getAbsolutePath(); } - - // a constant we use for marking sequence dictionary entries in the Tribble index property list - private static final String SequenceDictionaryPropertyPredicate = "DICT:"; - - private static void setIndexSequenceDictionary(final TribbleIndexCreator indexCreator, final SAMSequenceDictionary dict) { - for (final SAMSequenceRecord seq : dict.getSequences()) { - final String contig = SequenceDictionaryPropertyPredicate + seq.getSequenceName(); - final String length = String.valueOf(seq.getSequenceLength()); - indexCreator.addProperty(contig,length); - } - } -} - -/** - * Wraps output stream in a manner which keeps track of the position within the file and allowing writes - * at arbitrary points - */ -final class PositionalOutputStream extends OutputStream implements LocationAware -{ - private final OutputStream out; - private long position = 0; - - public PositionalOutputStream(final OutputStream out) { - this.out = out; - } - - public final void write(final byte[] bytes) throws IOException { - write(bytes, 0, bytes.length); - } - - public final void write(final byte[] bytes, final int startIndex, final int numBytes) throws IOException { - position += numBytes; - out.write(bytes, startIndex, numBytes); - } - - public final void write(final int c) throws IOException { - position++; - out.write(c); - } - - public final long getPosition() { return position; } - - @Override - public void close() throws IOException { - super.close(); - out.close(); - } } diff --git a/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java new file mode 100644 index 000000000..49de11d9c --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Daniel Gómez-Sánchez + * + * 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 htsjdk.samtools.util; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author Daniel Gomez-Sanchez (magicDGS) + */ +public class PositionalOutputStreamTest { + + @Test + public void basicPositionTest() throws Exception { + // wrapped null output stream to check + final PositionalOutputStream wrapped = new PositionalOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException {} + }); + int position = 0; + // check that we start at position 0 + Assert.assertEquals(wrapped.getPosition(), position); + // check that write one int just add one + wrapped.write(100); + Assert.assertEquals(wrapped.getPosition(), ++position); + // check that write a byte array adds its length + final byte[] bytes = new byte[]{1, 3, 5, 7}; + wrapped.write(bytes); + position += bytes.length; + Assert.assertEquals(wrapped.getPosition(), position); + // check that write just some bytes from an array adds its length + wrapped.write(bytes, 2, 2); + position += 2; + Assert.assertEquals(wrapped.getPosition(), position); + } + +} \ No newline at end of file From 7ab253142fa4dcf4279731ab3a5fe50798c0581e Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Mon, 24 Oct 2016 12:48:51 -0400 Subject: [PATCH 022/137] deleting SAMFileReader and SAMTruncatedFileReader (#699) these long deprecated classes were intended to be deleted in #602 but were accidentally resurrected updated documentation to point to SamReader instead of SAMFileReader renamed SAMFileReaderTest to the more accurate SamReaderTest --- src/main/java/htsjdk/samtools/BAMIndexer.java | 6 +- .../htsjdk/samtools/DefaultSAMRecordFactory.java | 2 +- src/main/java/htsjdk/samtools/SAMFileReader.java | 751 --------------------- .../htsjdk/samtools/SAMFileTruncatedReader.java | 69 -- src/main/java/htsjdk/samtools/SAMRecord.java | 2 +- .../java/htsjdk/samtools/SAMRecordSetBuilder.java | 2 +- .../java/htsjdk/samtools/SamFileValidator.java | 4 +- src/main/java/htsjdk/samtools/SamReader.java | 26 +- .../samtools/util/QualityEncodingDetector.java | 10 +- .../util/SamRecordIntervalIteratorFactory.java | 2 +- .../java/htsjdk/samtools/BAMFileWriterTest.java | 2 +- .../java/htsjdk/samtools/SamReaderSortTest.java | 2 +- .../{SAMFileReaderTest.java => SamReaderTest.java} | 2 +- 13 files changed, 30 insertions(+), 850 deletions(-) delete mode 100644 src/main/java/htsjdk/samtools/SAMFileReader.java delete mode 100644 src/main/java/htsjdk/samtools/SAMFileTruncatedReader.java rename src/test/java/htsjdk/samtools/{SAMFileReaderTest.java => SamReaderTest.java} (99%) diff --git a/src/main/java/htsjdk/samtools/BAMIndexer.java b/src/main/java/htsjdk/samtools/BAMIndexer.java index 80b557ae9..f5b15587c 100644 --- a/src/main/java/htsjdk/samtools/BAMIndexer.java +++ b/src/main/java/htsjdk/samtools/BAMIndexer.java @@ -282,7 +282,7 @@ void startNewReference() { /** * Generates a BAM index file from an input BAM file * - * @param reader SAMFileReader for input BAM file + * @param reader SamReader for input BAM file * @param output File for output index file */ public static void createIndex(SamReader reader, File output) { @@ -292,7 +292,7 @@ public static void createIndex(SamReader reader, File output) { /** * Generates a BAM index file from an input BAM file * - * @param reader SAMFileReader for input BAM file + * @param reader SamReader for input BAM file * @param output File for output index file */ public static void createIndex(SamReader reader, File output, Log log) { @@ -310,4 +310,4 @@ public static void createIndex(SamReader reader, File output, Log log) { } indexer.finish(); } -} \ No newline at end of file +} diff --git a/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java b/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java index 8a6077a33..7e3848e3d 100644 --- a/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java +++ b/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java @@ -1,7 +1,7 @@ package htsjdk.samtools; /** - * Default factory for creating SAM and BAM records used by the SAMFileReader classes. + * Default factory for creating SAM and BAM records used by the {@link SamReader} classes. * * @author Tim Fennell */ diff --git a/src/main/java/htsjdk/samtools/SAMFileReader.java b/src/main/java/htsjdk/samtools/SAMFileReader.java deleted file mode 100644 index 07189f7ac..000000000 --- a/src/main/java/htsjdk/samtools/SAMFileReader.java +++ /dev/null @@ -1,751 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2009 The Broad Institute - * - * 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 htsjdk.samtools; - - -import htsjdk.samtools.seekablestream.SeekableBufferedStream; -import htsjdk.samtools.seekablestream.SeekableHTTPStream; -import htsjdk.samtools.seekablestream.SeekableStream; -import htsjdk.samtools.util.*; - -import java.io.*; -import java.net.URL; -import java.util.NoSuchElementException; -import java.util.zip.GZIPInputStream; - -/** - * Class for reading and querying SAM/BAM files. Delegates to appropriate concrete implementation. - * - * @see SamReaderFactory - */ -@Deprecated -public class SAMFileReader implements SamReader, SamReader.Indexing { - - private static ValidationStringency defaultValidationStringency = ValidationStringency.DEFAULT_STRINGENCY; - - public static ValidationStringency getDefaultValidationStringency() { - return defaultValidationStringency; - } - - /** - * Set validation stringency for all subsequently-created SAMFileReaders. This is the only way to - * change the validation stringency for SAM header. - * NOTE: Programs that change this should make sure to have a try/finally clause wrapping the work that - * they do, so that the original stringency can be restored after the program's work is done. This facilitates - * calling a program that is usually run stand-alone from another program, without messing up the original - * validation stringency. - */ - public static void setDefaultValidationStringency(final ValidationStringency defaultValidationStringency) { - SAMFileReader.defaultValidationStringency = defaultValidationStringency; - } - - /** - * Returns the SAMSequenceDictionary from the provided FASTA. - */ - public static SAMSequenceDictionary getSequenceDictionary(final File dictionaryFile) { - final SAMFileReader samFileReader = new SAMFileReader(dictionaryFile); - final SAMSequenceDictionary dict = samFileReader.getFileHeader().getSequenceDictionary(); - CloserUtil.close(dictionaryFile); - return dict; - } - - private boolean mIsBinary = false; - private BAMIndex mIndex = null; - private SAMRecordFactory samRecordFactory = new DefaultSAMRecordFactory(); - private ReaderImplementation mReader = null; - private boolean useAsyncIO = Defaults.USE_ASYNC_IO_READ_FOR_SAMTOOLS; - - private File samFile = null; - - private static class EmptySamIterator implements CloseableIterator { - @Override - public boolean hasNext() { - return false; - } - - @Override - public SAMRecord next() { - throw new NoSuchElementException("next called on empty iterator"); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported: remove"); - } - - @Override - public void close() { - //no-op - } - } - - - /** - * Prepare to read a SAM or BAM file. Indexed lookup not allowed because reading from InputStream. - */ - public SAMFileReader(final InputStream stream) { - this(stream, false); - } - - /** - * Prepare to read a SAM or BAM file. If the given file is a BAM, and has a companion BAI index file - * that is named according to the convention, it will be found and opened, and indexed query will be allowed. - */ - public SAMFileReader(final File file) { - this(file, null, false); - } - - /** - * Prepare to read a SAM or BAM file. If the given file is a BAM, and an index is present, indexed query - * will be allowed. - * - * @param file SAM or BAM to read. - * @param indexFile Index file that is companion to BAM, or null if no index file, or if index file - * should be found automatically. - */ - public SAMFileReader(final File file, final File indexFile) { - this(file, indexFile, false); - } - - /** - * Read a SAM or BAM file. Indexed lookup not allowed because reading from InputStream. - * - * @param stream input SAM or BAM. This is buffered internally so caller need not buffer. - * @param eagerDecode if true, decode SAM record entirely when reading it. - */ - public SAMFileReader(final InputStream stream, final boolean eagerDecode) { - init(stream, null, null, eagerDecode, defaultValidationStringency); - } - - /** - * Read a SAM or BAM file, possibly with an index file if present. - * If the given file is a BAM, and an index is present, indexed query will be allowed. - * - * @param file SAM or BAM. - * @param eagerDecode if true, decode SAM record entirely when reading it. - */ - public SAMFileReader(final File file, final boolean eagerDecode) { - this(file, null, eagerDecode); - } - - /** - * Read a SAM or BAM file, possibly with an index file. If the given file is a BAM, and an index is present, - * indexed query will be allowed. - * - * @param file SAM or BAM. - * @param indexFile Location of index file, or null in order to use the default index file (if present). - * @param eagerDecode eagerDecode if true, decode SAM record entirely when reading it. - */ - public SAMFileReader(final File file, final File indexFile, final boolean eagerDecode) { - init(null, file, indexFile, eagerDecode, defaultValidationStringency); - } - - /** - * Read a BAM file by http - * indexed query will be allowed. - * - * @param url BAM. - * @param indexFile Location of index file, or null if indexed access not required. - * @param eagerDecode eagerDecode if true, decode SAM record entirely when reading it. - */ - public SAMFileReader(final URL url, final File indexFile, final boolean eagerDecode) { - init(new SeekableBufferedStream(new SeekableHTTPStream(url)), - indexFile, eagerDecode, defaultValidationStringency); - } - - /** - * Read a BAM file via caller-supplied mechanism. Indexed query will be allowed, but - * index file must be provided in that case. - * - * @param strm BAM -- If the stream is not buffered, caller should wrap in SeekableBufferedStream for - * better performance. - * @param indexFile Location of index file, or null indexed access not required. - * @param eagerDecode if true, decode SAM record entirely when reading it. - */ - public SAMFileReader(final SeekableStream strm, final File indexFile, final boolean eagerDecode) { - init(strm, indexFile, eagerDecode, defaultValidationStringency); - } - - /** - * @param strm BAM -- If the stream is not buffered, caller should wrap in SeekableBufferedStream for - * better performance. - */ - public SAMFileReader(final SeekableStream strm, final SeekableStream indexStream, final boolean eagerDecode) { - init(strm, indexStream, eagerDecode, defaultValidationStringency); - } - - public void close() { - if (mReader != null) { - mReader.close(); - } - mReader = null; - mIndex = null; - } - - /** - * If true, this reader will use asynchronous IO. - */ - public void setUseAsyncIO(final boolean useAsyncIO) { - this.useAsyncIO = useAsyncIO; - } - - /** - * If true, writes the source of every read into the source SAMRecords. - * - * @param enabled true to write source information into each SAMRecord. - */ - public void enableFileSource(final boolean enabled) { - mReader.enableFileSource(this, enabled); - } - - /** - * If true, uses the caching version of the index reader. - * - * @param enabled true to use the caching version of the reader. - */ - public void enableIndexCaching(final boolean enabled) { - if (mIndex != null) - throw new SAMException("Unable to turn on index caching; index file has already been loaded."); - mReader.enableIndexCaching(enabled); - } - - /** - * If false, disable the use of memory mapping for accessing index files (default behavior is to use memory mapping). - * This is slower but more scalable when accessing large numbers of BAM files sequentially. - * - * @param enabled True to use memory mapping, false to use regular I/O. - */ - public void enableIndexMemoryMapping(final boolean enabled) { - if (mIndex != null) { - throw new SAMException("Unable to change index memory mapping; index file has already been loaded."); - } - mReader.enableIndexMemoryMapping(enabled); - } - - /** - * Only meaningful for BAM file readers - enables or disables checking of checksums on uncompressed - * data during decompression. Enabling this will increase decompression time by 15-30%. - */ - public void enableCrcChecking(final boolean enabled) { - this.mReader.enableCrcChecking(enabled); - } - - /** - * Override the default SAMRecordFactory class used to instantiate instances of SAMRecord and BAMRecord. - */ - public void setSAMRecordFactory(final SAMRecordFactory factory) { - this.samRecordFactory = factory; - this.mReader.setSAMRecordFactory(factory); - } - - /** - * @return True if this is a BAM reader. - */ - public boolean isBinary() { - return mIsBinary; - } - - /** - * @return true if ths is a BAM file, and has an index - */ - public boolean hasIndex() { - return mReader.hasIndex(); - } - - @Override - public Indexing indexing() { - return this; - } - - /** - * Retrieves the index for the given file type. Ensure that the index is of the specified type. - * - * @return An index of the given type. - */ - public BAMIndex getIndex() { - return mReader.getIndex(); - } - - /** - * Returns true if the supported index is browseable, meaning the bins in it can be traversed - * and chunk data inspected and retrieved. - * - * @return True if the index supports the BrowseableBAMIndex interface. False otherwise. - */ - public boolean hasBrowseableIndex() { - return hasIndex() && getIndex() instanceof BrowseableBAMIndex; - } - - /** - * Gets an index tagged with the BrowseableBAMIndex interface. Throws an exception if no such - * index is available. - * - * @return An index with a browseable interface, if possible. - * @throws SAMException if no such index is available. - */ - public BrowseableBAMIndex getBrowseableIndex() { - final BAMIndex index = getIndex(); - if (!(index instanceof BrowseableBAMIndex)) - throw new SAMException("Cannot return index: index created by BAM is not browseable."); - return BrowseableBAMIndex.class.cast(index); - } - - public SAMFileHeader getFileHeader() { - return mReader.getFileHeader(); - } - - @Override - public Type type() { - return mReader.type(); - } - - @Override - public String getResourceDescription() { - return this.toString(); - } - - /** - * Control validation of SAMRecords as they are read from file. - * In order to control validation stringency for SAM Header, call SAMFileReader.setDefaultValidationStringency - * before constructing a SAMFileReader. - */ - public void setValidationStringency(final ValidationStringency validationStringency) { - mReader.setValidationStringency(validationStringency); - } - - /** - * Iterate through file in order. For a SAMFileReader constructed from an InputStream, and for any SAM file, - * a 2nd iteration starts where the 1st one left off. For a BAM constructed from a File, each new iteration - * starts at the first record. - *

- * Only a single open iterator on a SAM or BAM file may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - */ - public SAMRecordIterator iterator() { - return new AssertingIterator(mReader.getIterator()); - } - - /** - * Iterate through the given chunks in the file. - * - * @param chunks List of chunks for which to retrieve data. - * @return An iterator over the given chunks. - */ - public SAMRecordIterator iterator(final SAMFileSpan chunks) { - return new AssertingIterator(mReader.getIterator(chunks)); - } - - /** - * Gets a pointer spanning all reads in the BAM file. - * - * @return Unbounded pointer to the first record, in chunk format. - */ - public SAMFileSpan getFilePointerSpanningReads() { - return mReader.getFilePointerSpanningReads(); - } - - /** - * Iterate over records that match the given interval. Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. You can use a second SAMFileReader to iterate - * in parallel over the same underlying file. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param sequence Reference sequence of interest. - * @param start 1-based, inclusive start of interval of interest. Zero implies start of the reference sequence. - * @param end 1-based, inclusive end of interval of interest. Zero implies end of the reference sequence. - * @param contained If true, each SAMRecord returned is will have its alignment completely contained in the - * interval of interest. If false, the alignment of the returned SAMRecords need only overlap the interval of interest. - * @return Iterator over the SAMRecords matching the interval. - */ - public SAMRecordIterator query(final String sequence, final int start, final int end, final boolean contained) { - final int referenceIndex = getFileHeader().getSequenceIndex(sequence); - final CloseableIterator currentIterator; - if (referenceIndex == -1) { - currentIterator = new EmptySamIterator(); - } else { - final QueryInterval[] queryIntervals = {new QueryInterval(referenceIndex, start, end)}; - currentIterator = mReader.query(queryIntervals, contained); - } - return new AssertingIterator(currentIterator); - } - - /** - * Iterate over records that overlap the given interval. Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param sequence Reference sequence of interest. - * @param start 1-based, inclusive start of interval of interest. Zero implies start of the reference sequence. - * @param end 1-based, inclusive end of interval of interest. Zero implies end of the reference sequence. - * @return Iterator over the SAMRecords overlapping the interval. - */ - public SAMRecordIterator queryOverlapping(final String sequence, final int start, final int end) { - return query(sequence, start, end, false); - } - - /** - * Iterate over records that are contained in the given interval. Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param sequence Reference sequence of interest. - * @param start 1-based, inclusive start of interval of interest. Zero implies start of the reference sequence. - * @param end 1-based, inclusive end of interval of interest. Zero implies end of the reference sequence. - * @return Iterator over the SAMRecords contained in the interval. - */ - public SAMRecordIterator queryContained(final String sequence, final int start, final int end) { - return query(sequence, start, end, true); - } - - /** - * Iterate over records that match one of the given intervals. This may be more efficient than querying - * each interval separately, because multiple reads of the same SAMRecords is avoided. - *

- * Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. You can use a second SAMFileReader to iterate - * in parallel over the same underlying file. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match an interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param intervals Intervals to be queried. The intervals must be optimized, i.e. in order, with overlapping - * and abutting intervals merged. This can be done with {@link htsjdk.samtools.QueryInterval#optimizeIntervals} - * @param contained If true, each SAMRecord returned is will have its alignment completely contained in one of the - * intervals of interest. If false, the alignment of the returned SAMRecords need only overlap one of - * the intervals of interest. - * @return Iterator over the SAMRecords matching the interval. - */ - public SAMRecordIterator query(final QueryInterval[] intervals, final boolean contained) { - return new AssertingIterator(mReader.query(intervals, contained)); - } - - /** - * Iterate over records that overlap any of the given intervals. This may be more efficient than querying - * each interval separately, because multiple reads of the same SAMRecords is avoided. - *

- * Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param intervals Intervals to be queried. The intervals must be optimized, i.e. in order, with overlapping - * and abutting intervals merged. This can be done with {@link htsjdk.samtools.QueryInterval#optimizeIntervals} - * @return Iterator over the SAMRecords overlapping any of the intervals. - */ - public SAMRecordIterator queryOverlapping(final QueryInterval[] intervals) { - return query(intervals, false); - } - - /** - * Iterate over records that are contained in the given interval. This may be more efficient than querying - * each interval separately, because multiple reads of the same SAMRecords is avoided. - *

- * Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * is in the query region. - * - * @param intervals Intervals to be queried. The intervals must be optimized, i.e. in order, with overlapping - * and abutting intervals merged. This can be done with {@link htsjdk.samtools.QueryInterval#optimizeIntervals} - * @return Iterator over the SAMRecords contained in any of the intervals. - */ - public SAMRecordIterator queryContained(final QueryInterval[] intervals) { - return query(intervals, true); - } - - - public SAMRecordIterator queryUnmapped() { - return new AssertingIterator(mReader.queryUnmapped()); - } - - /** - * Iterate over records that map to the given sequence and start at the given position. Only valid to call this if hasIndex() == true. - *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. - *

- * Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read - * and then discarded because they do not match the interval of interest. - *

- * Note that an unmapped read will be returned by this call if it has a coordinate for the purpose of sorting that - * matches the arguments. - * - * @param sequence Reference sequence of interest. - * @param start Alignment start of interest. - * @return Iterator over the SAMRecords with the given alignment start. - */ - public SAMRecordIterator queryAlignmentStart(final String sequence, final int start) { - return new AssertingIterator(mReader.queryAlignmentStart(sequence, start)); - } - - /** - * Fetch the mate for the given read. Only valid to call this if hasIndex() == true. - * This will work whether the mate has a coordinate or not, so long as the given read has correct - * mate information. This method iterates over the SAM file, so there may not be an unclosed - * iterator on the SAM file when this method is called. - *

- * Note that it is not possible to call queryMate when iterating over the SAMFileReader, because queryMate - * requires its own iteration, and there cannot be two simultaneous iterations on the same SAMFileReader. The - * work-around is to open a second SAMFileReader on the same input file, and call queryMate on the second - * reader. - * - * @param rec Record for which mate is sought. Must be a paired read. - * @return rec's mate, or null if it cannot be found. - */ - public SAMRecord queryMate(final SAMRecord rec) { - if (!rec.getReadPairedFlag()) { - throw new IllegalArgumentException("queryMate called for unpaired read."); - } - if (rec.getFirstOfPairFlag() == rec.getSecondOfPairFlag()) { - throw new IllegalArgumentException("SAMRecord must be either first and second of pair, but not both."); - } - final boolean firstOfPair = rec.getFirstOfPairFlag(); - final CloseableIterator it; - if (rec.getMateReferenceIndex() == SAMRecord.NO_ALIGNMENT_REFERENCE_INDEX) { - it = queryUnmapped(); - } else { - it = queryAlignmentStart(rec.getMateReferenceName(), rec.getMateAlignmentStart()); - } - try { - SAMRecord mateRec = null; - while (it.hasNext()) { - final SAMRecord next = it.next(); - if (!next.getReadPairedFlag()) { - if (rec.getReadName().equals(next.getReadName())) { - throw new SAMFormatException("Paired and unpaired reads with same name: " + rec.getReadName()); - } - continue; - } - if (firstOfPair) { - if (next.getFirstOfPairFlag()) continue; - } else { - if (next.getSecondOfPairFlag()) continue; - } - if (rec.getReadName().equals(next.getReadName())) { - if (mateRec != null) { - throw new SAMFormatException("Multiple SAMRecord with read name " + rec.getReadName() + - " for " + (firstOfPair ? "second" : "first") + " end."); - } - mateRec = next; - } - } - return mateRec; - } finally { - it.close(); - } - } - - - private void init(final SeekableStream strm, final File indexFile, final boolean eagerDecode, - final ValidationStringency validationStringency) { - - try { - if (streamLooksLikeBam(strm)) { - mIsBinary = true; - mReader = new BAMFileReader(strm, indexFile, eagerDecode, useAsyncIO, validationStringency, this.samRecordFactory); - } else { - throw new SAMFormatException("Unrecognized file format: " + strm); - } - setValidationStringency(validationStringency); - } catch (final IOException e) { - throw new RuntimeIOException(e); - } - } - - private void init(final SeekableStream strm, final SeekableStream indexStream, final boolean eagerDecode, - final ValidationStringency validationStringency) { - - try { - if (streamLooksLikeBam(strm)) { - mIsBinary = true; - mReader = new BAMFileReader(strm, indexStream, eagerDecode, useAsyncIO, validationStringency, this.samRecordFactory); - } else { - throw new SAMFormatException("Unrecognized file format: " + strm); - } - setValidationStringency(validationStringency); - } catch (final IOException e) { - throw new RuntimeIOException(e); - } - } - - // Its too expensive to examine the remote file to determine type. - // Rely on file extension. - private boolean streamLooksLikeBam(final SeekableStream strm) { - String source = strm.getSource(); - if (source == null) return true; - source = source.toLowerCase(); - //Source will typically be a file path or URL - //If it's a URL we require one of the query parameters to be bam file - return source.endsWith(".bam") || source.contains(".bam?") || source.contains(".bam&") || source.contains(".bam%26"); - } - - private void init(final InputStream stream, File file, final File indexFile, final boolean eagerDecode, - final ValidationStringency validationStringency) { - if (stream != null && file != null) throw new IllegalArgumentException("stream and file are mutually exclusive"); - this.samFile = file; - - try { - BufferedInputStream bufferedStream; - // Buffering is required because mark() and reset() are called on the input stream. - final int bufferSize = Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE); - if (file != null) bufferedStream = new BufferedInputStream(new FileInputStream(file), bufferSize); - else bufferedStream = IOUtil.toBufferedStream(stream); - if (SamStreams.isBAMFile(bufferedStream)) { - mIsBinary = true; - if (file == null || !file.isFile()) { - // Handle case in which file is a named pipe, e.g. /dev/stdin or created by mkfifo - mReader = new BAMFileReader(bufferedStream, indexFile, eagerDecode, useAsyncIO, validationStringency, this.samRecordFactory); - } else { - bufferedStream.close(); - mReader = new BAMFileReader(file, indexFile, eagerDecode, useAsyncIO, validationStringency, this.samRecordFactory); - } - } else if (BlockCompressedInputStream.isValidFile(bufferedStream)) { - mIsBinary = false; - mReader = new SAMTextReader(new BlockCompressedInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isGzippedSAMFile(bufferedStream)) { - mIsBinary = false; - mReader = new SAMTextReader(new GZIPInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isCRAMFile(bufferedStream)) { - if (file == null || !file.isFile()) { - file = null; - } else { - bufferedStream.close(); - bufferedStream = null; - } - mReader = new CRAMFileReader(file, bufferedStream); - } else if (isSAMFile(bufferedStream)) { - if (indexFile != null) { - bufferedStream.close(); - throw new RuntimeException("Cannot use index file with textual SAM file"); - } - mIsBinary = false; - mReader = new SAMTextReader(bufferedStream, file, validationStringency, this.samRecordFactory); - } else { - bufferedStream.close(); - throw new SAMFormatException("Unrecognized file format"); - } - - setValidationStringency(validationStringency); - mReader.setSAMRecordFactory(this.samRecordFactory); - } catch (final IOException e) { - throw new RuntimeIOException(e); - } - } - - private static int readBytes(final InputStream stream, final byte[] buffer, final int offset, final int length) - throws IOException { - int bytesRead = 0; - while (bytesRead < length) { - final int count = stream.read(buffer, offset + bytesRead, length - bytesRead); - if (count <= 0) { - break; - } - bytesRead += count; - } - return bytesRead; - } - - private boolean isSAMFile(final InputStream stream) { - // For now, assume every non-binary file is a SAM text file. - return true; - } - - @Override - public String toString() { - if (this.samFile == null) { - return getClass().getSimpleName() + "{initialized with stream}"; - } else { - return getClass().getSimpleName() + "{" + this.samFile.getAbsolutePath() + "}"; - } - } - - /** - * Convenience method to create a QueryInterval - * - * @param sequence sequence of interest, must exist in sequence dictionary - * @param start 1-based start position, must be >= 1 - * @param end 1-based end position. - * @throws java.lang.IllegalArgumentException if sequence not found in sequence dictionary, or start position < 1 - */ - public QueryInterval makeQueryInterval(final String sequence, int start, int end) { - int referenceIndex = getFileHeader().getSequenceIndex(sequence); - if (referenceIndex < 0) { - throw new IllegalArgumentException(String.format("Sequence '%s' not found in sequence dictionary", sequence)); - } - if (start < 1) { - throw new IllegalArgumentException("Start position must be >= 1"); - } - return new QueryInterval(referenceIndex, start, end); - } - - /** - * Convenience method to create a QueryInterval that goes from start to end of given sequence. - * - * @param sequence sequence of interest, must exist in sequence dictionary - * @param start 1-based start position, must be >= 1 - * @throws java.lang.IllegalArgumentException if sequence not found in sequence dictionary, or start position < 1 - */ - public QueryInterval makeQueryInterval(final String sequence, int start) { - return makeQueryInterval(sequence, start, 0); - } - -} diff --git a/src/main/java/htsjdk/samtools/SAMFileTruncatedReader.java b/src/main/java/htsjdk/samtools/SAMFileTruncatedReader.java deleted file mode 100644 index c63dfb5be..000000000 --- a/src/main/java/htsjdk/samtools/SAMFileTruncatedReader.java +++ /dev/null @@ -1,69 +0,0 @@ -package htsjdk.samtools; - -import java.io.File; -import java.util.NoSuchElementException; - -/** - * A truncated form of a SAMFileReader that iterates over a limited number of records. - * - * @author mccowan@broadinstitute.org - */ -@Deprecated -public class SAMFileTruncatedReader extends SAMFileReader { - private class TruncatedIterator implements SAMRecordIterator { - final SAMRecordIterator i; - final long max; - long currentRecord = 0; - - TruncatedIterator(final SAMRecordIterator i, final long max) { - this.i = i; - this.max = max; - } - - public boolean hasNext() { - return i.hasNext() && max != currentRecord; - } - - public SAMRecord next() { - if (this.hasNext()) { - currentRecord += 1; - return i.next(); - } else { - throw new NoSuchElementException(); - } - } - - public void remove() { - i.remove(); - } - - public void close() { - i.close(); - } - - public SAMRecordIterator assertSorted(final SAMFileHeader.SortOrder sortOrder) { - return i.assertSorted(sortOrder); - } - } - - private final long maxRecordsToIterate; - - /** - * @param input The SAM file - * @param max The maximum number of records to read from the file via iterator() methods - */ - public SAMFileTruncatedReader(final File input, final long max) { - super(input); - this.maxRecordsToIterate = max; - } - - @Override - public SAMRecordIterator iterator() { - return new TruncatedIterator(super.iterator(), maxRecordsToIterate); - } - - @Override - public SAMRecordIterator iterator(final SAMFileSpan chunks) { - return new TruncatedIterator(super.iterator(chunks), maxRecordsToIterate); - } -} diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index a22ded540..5e82e0342 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -2047,7 +2047,7 @@ public int hashCode() { /** * Gets the source of this SAM record -- both the reader that retrieved the record and the position on disk from * whence it came. - * @return The file source. Note that the reader will be null if not activated using SAMFileReader.enableFileSource(). + * @return The file source. Note that the reader will be null if the reader source has not be set. */ public SAMFileSource getFileSource() { return mFileSource; diff --git a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java index 714199f94..2af91c30f 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java +++ b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java @@ -541,7 +541,7 @@ private void fillInBasesAndQualities(final SAMRecord rec, final int defaultQuali /** * Creates samFileReader from the data in instance of this class * - * @return SAMFileReader + * @return SamReader */ public SamReader getSamReader() { diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index fb559b0fb..cf18a7f8b 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -318,7 +318,7 @@ private void validateSamRecordsAndQualityFormat(final Iterable samRec addError(new SAMValidationError(Type.INVALID_QUALITY_FORMAT, e.getMessage(), null)); } } catch (SAMFormatException e) { - // increment record number because the iterator behind the SAMFileReader + // increment record number because the iterator behind the SamReader // reads one record ahead so we will get this failure one record ahead final String msg = "SAMFormatException on record " + progress.getCount() + 1; out.println(msg); @@ -592,7 +592,7 @@ public void setBisulfiteSequenced(boolean bisulfiteSequenced) { */ @Deprecated public SamFileValidator setValidateIndex(final boolean validateIndex) { - // The SAMFileReader must also have IndexCaching enabled to have the index validated, + // The SamReader must also have IndexCaching enabled to have the index validated, return this.setIndexValidationStringency(validateIndex ? IndexValidationStringency.EXHAUSTIVE : IndexValidationStringency.NONE); } diff --git a/src/main/java/htsjdk/samtools/SamReader.java b/src/main/java/htsjdk/samtools/SamReader.java index 9493593b1..6bd6c2165 100644 --- a/src/main/java/htsjdk/samtools/SamReader.java +++ b/src/main/java/htsjdk/samtools/SamReader.java @@ -133,7 +133,7 @@ public String toString() { public Indexing indexing(); /** - * Iterate through file in order. For a SAMFileReader constructed from an InputStream, and for any SAM file, + * Iterate through file in order. For a SamReader constructed from an InputStream, and for any SAM file, * a 2nd iteration starts where the 1st one left off. For a BAM constructed from a SeekableStream or File, each new iteration * starts at the first record. *

@@ -145,8 +145,8 @@ public String toString() { /** * Iterate over records that match the given interval. Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. You can use a second SAMFileReader to iterate + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start + * a second iteration, the first one must be closed first. You can use a second SamReader to iterate * in parallel over the same underlying file. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -167,7 +167,7 @@ public String toString() { /** * Iterate over records that overlap the given interval. Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -186,7 +186,7 @@ public String toString() { /** * Iterate over records that are contained in the given interval. Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -208,8 +208,8 @@ public String toString() { *

* Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start - * a second iteration, the first one must be closed first. You can use a second SAMFileReader to iterate + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start + * a second iteration, the first one must be closed first. You can use a second SamReader to iterate * in parallel over the same underlying file. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -233,7 +233,7 @@ public String toString() { *

* Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -253,7 +253,7 @@ public String toString() { *

* Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -274,7 +274,7 @@ public String toString() { /** * Iterate over records that map to the given sequence and start at the given position. Only valid to call this if hasIndex() == true. *

- * Only a single open iterator on a given SAMFileReader may be extant at any one time. If you want to start + * Only a single open iterator on a given SamReader may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. *

* Note that indexed lookup is not perfectly efficient in terms of disk I/O. I.e. some SAMRecords may be read @@ -295,9 +295,9 @@ public String toString() { * mate information. This method iterates over the SAM file, so there may not be an unclosed * iterator on the SAM file when this method is called. *

- * Note that it is not possible to call queryMate when iterating over the SAMFileReader, because queryMate - * requires its own iteration, and there cannot be two simultaneous iterations on the same SAMFileReader. The - * work-around is to open a second SAMFileReader on the same input file, and call queryMate on the second + * Note that it is not possible to call queryMate when iterating over the SamReader, because queryMate + * requires its own iteration, and there cannot be two simultaneous iterations on the same SamReader. The + * work-around is to open a second SamReader on the same input file, and call queryMate on the second * reader. * * @param rec Record for which mate is sought. Must be a paired read. diff --git a/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java b/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java index 83999bcc9..b0a965ca1 100644 --- a/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java +++ b/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java @@ -23,9 +23,9 @@ import static java.util.Arrays.asList; /** - * Utility for determining the type of quality encoding/format (see FastqQualityFormat) used in a SAM/BAM or Fastq. + * Utility for determining the type of quality encoding/format (see {@link FastqQualityFormat}) used in a SAM/BAM or Fastq. *

- * To use this class, invoke the detect() method with a SAMFileReader or FastqReader, as appropriate. The consumer is + * To use this class, invoke the detect() method with a {@link SamReader} or {@link FastqReader}, as appropriate. The consumer is * responsible for closing readers. * * @author mccowan@broadinstitute.org @@ -116,10 +116,10 @@ public void add(final FastqRecord fastqRecord) { * Adds the SAMRecord's quality scores. *

* Does not assume Phred quality encoding (for obvious reasons); getBaseQualityString() is used to read the - * unmodified ASCII score. To elaborate, SAMFileReader, which is generating these SAMRecords, builds the + * unmodified ASCII score. To elaborate, the {@link SamReader}, which is generating these {@link SAMRecord}s, builds the * SAMRecord by subtracting a value from each quality score and storing that transformed value internally. * Since we desire original scores here (whatever was in the file to begin with), we effectively undo this - * transformation by asking SAMRecord to convert the quality back into the ASCII that was read in the file. + * transformation by asking {@link SAMRecord} to convert the quality back into the ASCII that was read in the file. */ public void add(final SAMRecord samRecord, final boolean useOriginalQualities) { addAsciiQuality(useOriginalQualities && samRecord.getOriginalBaseQualities() != null @@ -402,4 +402,4 @@ public FastqQualityFormat generateBestGuess(final FileContext context, final Fas } } } -} \ No newline at end of file +} diff --git a/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java b/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java index 7546a0150..5d173a5a4 100644 --- a/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java +++ b/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java @@ -34,7 +34,7 @@ import java.util.NoSuchElementException; /** - * Create an iterator over a SAMFileReader that only returns reads that overlap one of the intervals + * Create an iterator over a {@link SamReader} that only returns reads that overlap one of the intervals * in an interval list. * * @author alecw@broadinstitute.org diff --git a/src/test/java/htsjdk/samtools/BAMFileWriterTest.java b/src/test/java/htsjdk/samtools/BAMFileWriterTest.java index 235f23ba0..a8944d0de 100644 --- a/src/test/java/htsjdk/samtools/BAMFileWriterTest.java +++ b/src/test/java/htsjdk/samtools/BAMFileWriterTest.java @@ -50,7 +50,7 @@ private SAMRecordSetBuilder getRecordSetBuilder(final boolean sortForMe, final S * Parse some SAM text into a SAM object, then write as BAM. If SAM text was presorted, then the BAM file can * be read and compared with the SAM object. * - * @param samRecordSetBuilder source of input SAMFileReader to be written and compared with + * @param samRecordSetBuilder source of input {@link SamReader} to be written and compared with * @param sortOrder How the BAM should be written * @param presorted If true, samText is in the order specified by sortOrder */ diff --git a/src/test/java/htsjdk/samtools/SamReaderSortTest.java b/src/test/java/htsjdk/samtools/SamReaderSortTest.java index cc496dbd2..584410fd0 100755 --- a/src/test/java/htsjdk/samtools/SamReaderSortTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderSortTest.java @@ -31,7 +31,7 @@ import java.io.File; /** - * Tests for the implementation of SAMRecordIterator in SAMFileReader + * Tests for the implementation of {@link SAMRecordIterator} in {@link SamReader} * * @author ktibbett@broadinstitute.org */ diff --git a/src/test/java/htsjdk/samtools/SAMFileReaderTest.java b/src/test/java/htsjdk/samtools/SamReaderTest.java similarity index 99% rename from src/test/java/htsjdk/samtools/SAMFileReaderTest.java rename to src/test/java/htsjdk/samtools/SamReaderTest.java index b29101140..093dffbeb 100644 --- a/src/test/java/htsjdk/samtools/SAMFileReaderTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderTest.java @@ -32,7 +32,7 @@ import java.io.File; -public class SAMFileReaderTest { +public class SamReaderTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @Test(dataProvider = "variousFormatReaderTestCases") From b49763096bbf7544ebdc1ac9d2294ecf7ce8b1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Mon, 24 Oct 2016 18:53:14 +0200 Subject: [PATCH 023/137] preventing non-deterministic test failure in RelativeIso8601DateTest #578 (#710) adding a delta to the asserts in RelativeIso8601DateTest to fix #578 --- .../java/htsjdk/samtools/util/RelativeIso8601DateTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java b/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java index 4fa56d577..e4e9ef993 100644 --- a/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java +++ b/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java @@ -10,13 +10,17 @@ /** @author mccowan */ public class RelativeIso8601DateTest { + + // 1 second resolution is ISO date + private final static double DELTA_FOR_TIME = 1000; + @Test public void testLazyInstance() { final RelativeIso8601Date lazy = RelativeIso8601Date.generateLazyNowInstance(); Assert.assertEquals(lazy.toString(), RelativeIso8601Date.LAZY_NOW_LABEL); Assert.assertEquals(lazy.toString(), RelativeIso8601Date.LAZY_NOW_LABEL); Assert.assertEquals(lazy.toString(), RelativeIso8601Date.LAZY_NOW_LABEL); - Assert.assertEquals(lazy.getTime(), new Iso8601Date(new Date(System.currentTimeMillis())).getTime(), 1000); // 1 second resolution is ISO date + Assert.assertEquals(lazy.getTime(), new Iso8601Date(new Date(System.currentTimeMillis())).getTime(), DELTA_FOR_TIME); // Assert no exception thrown; this should be valid, because toString should now return an iso-looking date. new Iso8601Date(lazy.toString()); } @@ -33,7 +37,7 @@ public void testNonLazyInstance() { for (final RelativeIso8601Date nonLazy : testDates) { Assert.assertFalse(nonLazy.toString().equals(RelativeIso8601Date.LAZY_NOW_LABEL)); - Assert.assertEquals((double) nonLazy.getTime(), (double) time); + Assert.assertEquals((double) nonLazy.getTime(), (double) time, DELTA_FOR_TIME); // Assert no exception thrown; this should be valid, because toString return an iso-looking date. new RelativeIso8601Date(nonLazy.toString()); } @@ -44,6 +48,6 @@ public void equalityTest() { final String s = new Iso8601Date(new Date(12345)).toString(); final Iso8601Date iso8601Date = new Iso8601Date(s); final RelativeIso8601Date relativeIso8601Date = new RelativeIso8601Date(s); - Assert.assertEquals(relativeIso8601Date.getTime(), iso8601Date.getTime()); + Assert.assertEquals(relativeIso8601Date.getTime(), iso8601Date.getTime(), DELTA_FOR_TIME); } } From 812d75225c7c6abc7f155d8e91e10486b217e872 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Fri, 21 Oct 2016 14:16:14 -0400 Subject: [PATCH 024/137] - tests added showing that the two codes for calculating NM do not agree with each other (on lowercase bases in the reference and reads) --- .../java/htsjdk/samtools/util/SequenceUtil.java | 84 +--------------------- .../htsjdk/samtools/util/SequenceUtilTest.java | 32 +++++++-- .../reference_with_lower_and_uppercase.dict | 3 + .../reference_with_lower_and_uppercase.fasta | 4 ++ .../reference_with_lower_and_uppercase.fasta.fai | 2 + .../SequenceUtil/upper_and_lowercase_read.sam | 10 +++ 6 files changed, 46 insertions(+), 89 deletions(-) create mode 100644 src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.dict create mode 100644 src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta create mode 100644 src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta.fai create mode 100644 src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam diff --git a/src/main/java/htsjdk/samtools/util/SequenceUtil.java b/src/main/java/htsjdk/samtools/util/SequenceUtil.java index d2fb861a7..f6fb08949 100644 --- a/src/main/java/htsjdk/samtools/util/SequenceUtil.java +++ b/src/main/java/htsjdk/samtools/util/SequenceUtil.java @@ -442,33 +442,6 @@ public static int countMismatches(final SAMRecord read, final byte[] referenceBa } /** - * Sadly, this is a duplicate of the method above, except that it takes char[] for referenceBases rather - * than byte[]. This is because GATK needs it this way. - *

- * TODO: Remove this method when GATK map method is changed to take refseq as byte[]. - * TODO: UPDATE: Seems to be removed from GATK. Deprecated now to be removed in a future version. - */ - @Deprecated - private static int countMismatches(final SAMRecord read, final char[] referenceBases, final int referenceOffset) { - int mismatches = 0; - - final byte[] readBases = read.getReadBases(); - - for (final AlignmentBlock block : read.getAlignmentBlocks()) { - final int readBlockStart = block.getReadStart() - 1; - final int referenceBlockStart = block.getReferenceStart() - 1 - referenceOffset; - final int length = block.getLength(); - - for (int i = 0; i < length; ++i) { - if (!basesEqual(readBases[readBlockStart + i], StringUtil.charToByte(referenceBases[referenceBlockStart + i]))) { - ++mismatches; - } - } - } - return mismatches; - } - - /** * Calculates the sum of qualities for mismatched bases in the read. * * @param referenceBases Array of ASCII bytes in which the 0th position in the array corresponds @@ -537,41 +510,6 @@ public static int sumQualitiesOfMismatches(final SAMRecord read, final byte[] re return qualities; } - /** - * Sadly, this is a duplicate of the method above, except that it takes char[] for referenceBases rather - * than byte[]. This is because GATK needs it this way. - *

- * TODO: Remove this method when GATK map method is changed to take refseq as byte[]. - * TODO: UPDATE: Seems to be removed from GATK. Deprecated now to be removed in a future version. - */ - @Deprecated - public static int sumQualitiesOfMismatches(final SAMRecord read, final char[] referenceBases, - final int referenceOffset) { - int qualities = 0; - - final byte[] readBases = read.getReadBases(); - final byte[] readQualities = read.getBaseQualities(); - - if (read.getAlignmentStart() <= referenceOffset) { - throw new IllegalArgumentException("read.getAlignmentStart(" + read.getAlignmentStart() + - ") <= referenceOffset(" + referenceOffset + ")"); - } - - for (final AlignmentBlock block : read.getAlignmentBlocks()) { - final int readBlockStart = block.getReadStart() - 1; - final int referenceBlockStart = block.getReferenceStart() - 1 - referenceOffset; - final int length = block.getLength(); - - for (int i = 0; i < length; ++i) { - if (!basesEqual(readBases[readBlockStart + i], StringUtil.charToByte(referenceBases[referenceBlockStart + i]))) { - qualities += readQualities[readBlockStart + i]; - } - } - } - - return qualities; - } - public static int countInsertedBases(final Cigar cigar) { int ret = 0; for (final CigarElement element : cigar.getCigarElements()) { @@ -658,26 +596,6 @@ public static int calculateSamNmTagFromCigar(final SAMRecord record) { return samNm; } - - /** - * Sadly, this is a duplicate of the method above, except that it takes char[] for referenceBases rather - * than byte[]. This is because GATK needs it this way. - *

- * TODO: Remove this method when GATK map method is changed to take refseq as byte[]. - * TODO: UPDATE: Seems to be removed from GATK. Deprecated now to be removed in a future version. - */ - @Deprecated - public static int calculateSamNmTag(final SAMRecord read, final char[] referenceBases, - final int referenceOffset) { - int samNm = countMismatches(read, referenceBases, referenceOffset); - for (final CigarElement el : read.getCigar().getCigarElements()) { - if (el.getOperator() == CigarOperator.INSERTION || el.getOperator() == CigarOperator.DELETION) { - samNm += el.getLength(); - } - } - return samNm; - } - /** Returns the complement of a single byte. */ public static byte complement(final byte b) { switch (b) { @@ -1059,7 +977,7 @@ public static byte upperCase(final byte base) { /** Generates all possible unambiguous kmers (upper-case) of length and returns them as byte[]s. */ public static List generateAllKmers(final int length) { - final List sofar = new LinkedList(); + final List sofar = new LinkedList<>(); if (sofar.isEmpty()) { sofar.add(new byte[length]); diff --git a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java index c5c797e7b..1f5e12115 100644 --- a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java @@ -23,16 +23,15 @@ */ package htsjdk.samtools.util; -import htsjdk.samtools.Cigar; -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SAMTag; -import htsjdk.samtools.SAMTextHeaderCodec; -import htsjdk.samtools.TextCigarCodec; +import htsjdk.samtools.*; +import htsjdk.samtools.reference.ReferenceSequence; +import htsjdk.samtools.reference.ReferenceSequenceFile; +import htsjdk.samtools.reference.ReferenceSequenceFileFactory; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.File; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -429,4 +428,25 @@ public void testGetSamReadNameFromFastqHeader(final String fastqHeader, {"Name/1/2", "Name"} }; } + + @Test + public void testCalculateNmTag() { + final File TEST_DIR = new File("src/test/resources/htsjdk/samtools/SequenceUtil"); + final File referenceFile = new File(TEST_DIR, "reference_with_lower_and_uppercase.fasta"); + final File samFile = new File(TEST_DIR, "upper_and_lowercase_read.sam"); + + SamReader reader = SamReaderFactory.makeDefault().open(samFile); + ReferenceSequenceFile ref = ReferenceSequenceFileFactory.getReferenceSequenceFile(referenceFile); + + reader.iterator().stream().forEach(r -> { + Integer nm = SequenceUtil.calculateSamNmTag(r, ref.getSequence(r.getContig()).getBases()); + String md = r.getStringAttribute(SAMTag.MD.name()); + Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with read \'" + r.getReadName() + "\':"); + SequenceUtil.calculateMdAndNmTags(r, ref.getSequence(r.getContig()).getBases(), true, true); + + Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with read \'" + r.getReadName() + "\':"); + Assert.assertEquals(r.getStringAttribute(SAMTag.MD.name()), md, "problem with read \'" + r.getReadName() + "\':"); + + }); + } } diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.dict b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.dict new file mode 100644 index 000000000..db5b251d0 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.dict @@ -0,0 +1,3 @@ +@HD VN:1.5 SO:unsorted +@SQ SN:chr1 LN:16 M5:56b74a652b3ed2f610263b8bb423167c UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta +@SQ SN:chr2 LN:16 M5:b835d2c026aa66c52a05838dcc0b59d4 UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta new file mode 100644 index 000000000..0b446caa8 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta @@ -0,0 +1,4 @@ +>chr1 +ACGTACGTacgtacgt +>chr2 +TCGATCGAtcgatcga \ No newline at end of file diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta.fai b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta.fai new file mode 100644 index 000000000..9314c8fe5 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta.fai @@ -0,0 +1,2 @@ +chr1 16 6 16 17 +chr2 16 29 16 16 diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam new file mode 100644 index 000000000..fbaec8124 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam @@ -0,0 +1,10 @@ +@HD VN:1.5 SO:coordinate +@SQ SN:chr1 LN:16 M5:56b74a652b3ed2f610263b8bb423167c UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta +@SQ SN:chr2 LN:16 M5:b835d2c026aa66c52a05838dcc0b59d4 UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta +@CO chr1 value is ACGTACGTacgtacgt +@CO chr1 value is TCGATCGAtcgatcga +read1 0 chr1 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:0 +read2 0 chr2 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:8 MD:Z:3t0A2T0a2G0t0A2t +read3 0 chr2 1 0 8M * 0 0 TCGATCGA AAAAAAAA NM:i:0 +read4 0 chr2 1 0 4M1D2M1S * 0 0 TCGACGAA AAAAAAAA NM:i:1 MD:Z:4^T2 + From 17fb6719ed70d5e7f65aea266b4e690d914823e5 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Fri, 21 Oct 2016 14:50:47 -0400 Subject: [PATCH 025/137] - case-sensitivity of NM fixed. now tests pass. also refactored code so that variables have meaningful names. --- .../java/htsjdk/samtools/util/SequenceUtil.java | 81 +++++++++++----------- .../htsjdk/samtools/util/SequenceUtilTest.java | 9 +-- .../SequenceUtil/upper_and_lowercase_read.sam | 10 +-- 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/SequenceUtil.java b/src/main/java/htsjdk/samtools/util/SequenceUtil.java index f6fb08949..73b46b1bf 100644 --- a/src/main/java/htsjdk/samtools/util/SequenceUtil.java +++ b/src/main/java/htsjdk/samtools/util/SequenceUtil.java @@ -903,66 +903,63 @@ public static void calculateMdAndNmTags(final SAMRecord record, final byte[] ref final Cigar cigar = record.getCigar(); final List cigarElements = cigar.getCigarElements(); final byte[] seq = record.getReadBases(); - final int start = record.getAlignmentStart() - 1; - int i, x, y, u = 0; - int nm = 0; - final StringBuilder str = new StringBuilder(); - - final int size = cigarElements.size(); - for (i = y = 0, x = start; i < size; ++i) { - final CigarElement ce = cigarElements.get(i); - int j; - final int length = ce.getLength(); + final int alignmentStart = record.getAlignmentStart() - 1; + int cigarIndex, blockRefPos, blockReadStart, matchCount = 0; + int nmCount = 0; + final StringBuilder mdString = new StringBuilder(); + + final int nElements = cigarElements.size(); + for (cigarIndex = blockReadStart = 0, blockRefPos = alignmentStart; cigarIndex < nElements; ++cigarIndex) { + final CigarElement ce = cigarElements.get(cigarIndex); + int inBlockOffset; + final int blockLength = ce.getLength(); final CigarOperator op = ce.getOperator(); if (op == CigarOperator.MATCH_OR_MISMATCH || op == CigarOperator.EQ || op == CigarOperator.X) { - for (j = 0; j < length; ++j) { - final int z = y + j; + for (inBlockOffset = 0; inBlockOffset < blockLength; ++inBlockOffset) { + final int readOffset = blockReadStart + inBlockOffset; - if (ref.length <= x + j) break; // out of boundary + if (ref.length <= blockRefPos + inBlockOffset) break; // out of boundary - int c1 = 0; - int c2 = 0; - // try { - c1 = seq[z]; - c2 = ref[x + j]; + final byte readBase = seq[readOffset]; + final byte refBase = ref[blockRefPos + inBlockOffset]; - if ((c1 == c2) || c1 == 0) { + if ((bases[readBase] == bases[refBase]) || readBase == 0) { // a match - ++u; + ++matchCount; } else { - str.append(u); - str.appendCodePoint(ref[x + j]); - u = 0; - ++nm; + mdString.append(matchCount); + mdString.appendCodePoint(refBase); + matchCount = 0; + ++nmCount; } } - if (j < length) break; - x += length; - y += length; + if (inBlockOffset < blockLength) break; + blockRefPos += blockLength; + blockReadStart += blockLength; } else if (op == CigarOperator.DELETION) { - str.append(u); - str.append('^'); - for (j = 0; j < length; ++j) { - if (ref[x + j] == 0) break; - str.appendCodePoint(ref[x + j]); + mdString.append(matchCount); + mdString.append('^'); + for (inBlockOffset = 0; inBlockOffset < blockLength; ++inBlockOffset) { + if (ref[blockRefPos + inBlockOffset] == 0) break; + mdString.appendCodePoint(ref[blockRefPos + inBlockOffset]); } - u = 0; - if (j < length) break; - x += length; - nm += length; + matchCount = 0; + if (inBlockOffset < blockLength) break; + blockRefPos += blockLength; + nmCount += blockLength; } else if (op == CigarOperator.INSERTION || op == CigarOperator.SOFT_CLIP) { - y += length; - if (op == CigarOperator.INSERTION) nm += length; + blockReadStart += blockLength; + if (op == CigarOperator.INSERTION) nmCount += blockLength; } else if (op == CigarOperator.SKIPPED_REGION) { - x += length; + blockRefPos += blockLength; } } - str.append(u); + mdString.append(matchCount); - if (calcMD) record.setAttribute(SAMTag.MD.name(), str.toString()); - if (calcNM) record.setAttribute(SAMTag.NM.name(), nm); + if (calcMD) record.setAttribute(SAMTag.MD.name(), mdString.toString()); + if (calcNM) record.setAttribute(SAMTag.NM.name(), nmCount); } public static byte upperCase(final byte base) { diff --git a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java index 1f5e12115..008cca507 100644 --- a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java @@ -441,12 +441,13 @@ public void testCalculateNmTag() { reader.iterator().stream().forEach(r -> { Integer nm = SequenceUtil.calculateSamNmTag(r, ref.getSequence(r.getContig()).getBases()); String md = r.getStringAttribute(SAMTag.MD.name()); - Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with read \'" + r.getReadName() + "\':"); + Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with NM in read \'" + r.getReadName() + "\':"); SequenceUtil.calculateMdAndNmTags(r, ref.getSequence(r.getContig()).getBases(), true, true); - Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with read \'" + r.getReadName() + "\':"); - Assert.assertEquals(r.getStringAttribute(SAMTag.MD.name()), md, "problem with read \'" + r.getReadName() + "\':"); - + Assert.assertEquals(r.getIntegerAttribute(SAMTag.NM.name()), nm, "problem with NM in read \'" + r.getReadName() + "\':"); + if (md != null) { + Assert.assertEquals(r.getStringAttribute(SAMTag.MD.name()), md, "problem with MD in read \'" + r.getReadName() + "\':"); + } }); } } diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam index fbaec8124..82efe858e 100644 --- a/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam @@ -2,9 +2,9 @@ @SQ SN:chr1 LN:16 M5:56b74a652b3ed2f610263b8bb423167c UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta @SQ SN:chr2 LN:16 M5:b835d2c026aa66c52a05838dcc0b59d4 UR:file:src/test/resources/htsjdk/samtools/SequenceUtil/reference_with_lower_and_uppercase.fasta @CO chr1 value is ACGTACGTacgtacgt -@CO chr1 value is TCGATCGAtcgatcga +@CO chr2 value is TCGATCGAtcgatcga read1 0 chr1 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:0 -read2 0 chr2 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:8 MD:Z:3t0A2T0a2G0t0A2t -read3 0 chr2 1 0 8M * 0 0 TCGATCGA AAAAAAAA NM:i:0 -read4 0 chr2 1 0 4M1D2M1S * 0 0 TCGACGAA AAAAAAAA NM:i:1 MD:Z:4^T2 - +read2 0 chr1 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:0 +read3 0 chr2 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:8 MD:Z:0T2A0T2A0t2a0t2a0 +read4 0 chr2 1 0 8M * 0 0 TCGATCGA AAAAAAAA NM:i:0 +read5 0 chr2 1 0 4M1D2M1S * 0 0 TCGACGAA AAAAAAAA NM:i:1 MD:Z:4^T2 From 58f4154e037a5721d6f534050e0765d49b655b1f Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Mon, 24 Oct 2016 11:30:47 -0400 Subject: [PATCH 026/137] updated javadoc --- src/main/java/htsjdk/samtools/util/SequenceUtil.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/SequenceUtil.java b/src/main/java/htsjdk/samtools/util/SequenceUtil.java index 73b46b1bf..3108cee0d 100644 --- a/src/main/java/htsjdk/samtools/util/SequenceUtil.java +++ b/src/main/java/htsjdk/samtools/util/SequenceUtil.java @@ -890,10 +890,11 @@ public static String calculateMD5String(final byte[] data, final int offset, fin /** * Calculate MD and NM similarly to Samtools, except that N->N is a match. * - * @param record - * @param ref - * @param calcMD - * @return + * @param record Input record for which to calculate NM and MD. + * The appropriate tags will be added/updated in the record + * @param ref The reference bases for the sequence to which the record is mapped + * @param calcMD A flag indicating whether to update the MD tag in the record + * @param calcNM A flag indicating whether to update the NM tag in the record */ public static void calculateMdAndNmTags(final SAMRecord record, final byte[] ref, final boolean calcMD, final boolean calcNM) { From 02c0ff21a09b9178bd5b73d18ce2d11cf8da5c79 Mon Sep 17 00:00:00 2001 From: meganshand Date: Thu, 29 Sep 2016 15:51:12 -0400 Subject: [PATCH 027/137] Includes broken test for reverseComplementing without a deep copy --- src/main/java/htsjdk/samtools/SAMRecord.java | 3 +- src/main/java/htsjdk/samtools/SAMRecordUtil.java | 54 +++++++++++++++++----- .../java/htsjdk/samtools/SAMRecordUtilTest.java | 50 ++++++++++++++++---- 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 5e82e0342..a298666b7 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -2112,7 +2112,8 @@ private String buildMessage(final String baseMessage, final boolean isMate) { /** * Note that this does a shallow copy of everything, except for the attribute list, for which a copy of the list * is made, but the attributes themselves are copied by reference. This should be safe because callers should - * never modify a mutable value returned by any of the get() methods anyway. + * never modify a mutable value returned by any of the get() methods anyway. If one of the cloned records SEQ or + * QUAL needs to be modified, a deeper copy should be made, or other precautions taken (Eg. Reverse Complement). */ @Override public Object clone() throws CloneNotSupportedException { diff --git a/src/main/java/htsjdk/samtools/SAMRecordUtil.java b/src/main/java/htsjdk/samtools/SAMRecordUtil.java index d2900747f..c9bfac021 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordUtil.java +++ b/src/main/java/htsjdk/samtools/SAMRecordUtil.java @@ -39,11 +39,26 @@ /** * Reverse-complement bases and reverse quality scores along with known optional attributes that - * need the same treatment. See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} + * need the same treatment. This does not make a deep copy of the bases, qualities or attributes: + * for safe reverseComplement see {@link #reverseComplement(SAMRecord, boolean)}. + * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. */ public static void reverseComplement(final SAMRecord rec) { - reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE); + reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, true); + } + + /** + * Reverse-complement bases and reverse quality scores along with known optional attributes that + * need the same treatment. Optionally makes a deep copy of the bases, qualities or attributes. + * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} + * for the default set of tags that are handled. + * + * @param rec Record to reverse complement. + * @param unsafe Setting this to false will clone all attributes, bases and qualities before changing the values. + */ + public static void reverseComplement(final SAMRecord rec, boolean unsafe) { + reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, unsafe); } /** @@ -51,11 +66,11 @@ public static void reverseComplement(final SAMRecord rec) { * non-null attributes specified by tagsToRevcomp and reverse and non-null attributes * specified by tagsToReverse. */ - public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse) { - final byte[] readBases = rec.getReadBases(); + public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean unsafe) { + final byte[] readBases = unsafe ? rec.getReadBases() : rec.getReadBases().clone(); SequenceUtil.reverseComplement(readBases); rec.setReadBases(readBases); - final byte qualities[] = rec.getBaseQualities(); + final byte qualities[] = unsafe ? rec.getBaseQualities() : rec.getBaseQualities().clone(); reverseArray(qualities); rec.setBaseQualities(qualities); @@ -64,8 +79,13 @@ public static void reverseComplement(final SAMRecord rec, final Collection Date: Thu, 29 Sep 2016 21:48:13 -0400 Subject: [PATCH 028/137] Fixes test --- src/test/java/htsjdk/samtools/SAMRecordUtilTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/htsjdk/samtools/SAMRecordUtilTest.java b/src/test/java/htsjdk/samtools/SAMRecordUtilTest.java index a8e808ff4..fe23c826a 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUtilTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUtilTest.java @@ -24,8 +24,8 @@ @Test public void testSafeReverseComplement() throws CloneNotSupportedException { final SAMRecord original = createTestSamRec(); final SAMRecord cloneOfOriginal = (SAMRecord) original.clone(); - //Runs an unsafe reverseComplement - SAMRecordUtil.reverseComplement(cloneOfOriginal, Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), true); + //Runs a safe reverseComplement + SAMRecordUtil.reverseComplement(cloneOfOriginal, Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), false); Assert.assertEquals(original.getReadString(), "ACACACACAC"); Assert.assertEquals(original.getBaseQualityString(), "HHHHHIIIII"); From 799b06ac30a98eb4bf2d66f3ccedce048f364832 Mon Sep 17 00:00:00 2001 From: meganshand Date: Tue, 4 Oct 2016 16:44:22 -0400 Subject: [PATCH 029/137] Addressing most comments --- src/main/java/htsjdk/samtools/SAMRecord.java | 4 +-- src/main/java/htsjdk/samtools/SAMRecordUtil.java | 31 +++++++++++----------- .../java/htsjdk/samtools/SAMRecordUtilTest.java | 4 +-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index a298666b7..025f10cbf 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -2112,8 +2112,8 @@ private String buildMessage(final String baseMessage, final boolean isMate) { /** * Note that this does a shallow copy of everything, except for the attribute list, for which a copy of the list * is made, but the attributes themselves are copied by reference. This should be safe because callers should - * never modify a mutable value returned by any of the get() methods anyway. If one of the cloned records SEQ or - * QUAL needs to be modified, a deeper copy should be made, or other precautions taken (Eg. Reverse Complement). + * never modify a mutable value returned by any of the get() methods anyway. If one of the cloned record's SEQ or + * QUAL needs to be modified, a deeper copy should be made (e.g. Reverse Complement). */ @Override public Object clone() throws CloneNotSupportedException { diff --git a/src/main/java/htsjdk/samtools/SAMRecordUtil.java b/src/main/java/htsjdk/samtools/SAMRecordUtil.java index c9bfac021..8c4ac44e0 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordUtil.java +++ b/src/main/java/htsjdk/samtools/SAMRecordUtil.java @@ -39,8 +39,8 @@ /** * Reverse-complement bases and reverse quality scores along with known optional attributes that - * need the same treatment. This does not make a deep copy of the bases, qualities or attributes: - * for safe reverseComplement see {@link #reverseComplement(SAMRecord, boolean)}. + * need the same treatment. Changes made in-place, instead of making a copy of the bases, qualities, + * or attributes. If a copy is needed use {@link #reverseComplement(SAMRecord, boolean)}. * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. */ @@ -50,15 +50,15 @@ public static void reverseComplement(final SAMRecord rec) { /** * Reverse-complement bases and reverse quality scores along with known optional attributes that - * need the same treatment. Optionally makes a deep copy of the bases, qualities or attributes. - * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} + * need the same treatment. Optionally makes a copy of the bases, qualities or attributes instead + * of altering them in-place. See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. * * @param rec Record to reverse complement. - * @param unsafe Setting this to false will clone all attributes, bases and qualities before changing the values. + * @param inplace Setting this to false will clone all attributes, bases and qualities before changing the values. */ - public static void reverseComplement(final SAMRecord rec, boolean unsafe) { - reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, unsafe); + public static void reverseComplement(final SAMRecord rec, boolean inplace) { + reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); } /** @@ -66,11 +66,11 @@ public static void reverseComplement(final SAMRecord rec, boolean unsafe) { * non-null attributes specified by tagsToRevcomp and reverse and non-null attributes * specified by tagsToReverse. */ - public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean unsafe) { - final byte[] readBases = unsafe ? rec.getReadBases() : rec.getReadBases().clone(); + public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { + final byte[] readBases = inplace ? rec.getReadBases() : rec.getReadBases().clone(); SequenceUtil.reverseComplement(readBases); rec.setReadBases(readBases); - final byte qualities[] = unsafe ? rec.getBaseQualities() : rec.getBaseQualities().clone(); + final byte qualities[] = inplace ? rec.getBaseQualities() : rec.getBaseQualities().clone(); reverseArray(qualities); rec.setBaseQualities(qualities); @@ -80,10 +80,11 @@ public static void reverseComplement(final SAMRecord rec, final Collection Date: Tue, 4 Oct 2016 17:49:47 -0400 Subject: [PATCH 030/137] Refactor reverseComplement to SAMRecord and make default copy attributes --- src/main/java/htsjdk/samtools/SAMRecord.java | 143 +++++++++++++++++++-- src/main/java/htsjdk/samtools/SAMRecordUtil.java | 97 +------------- .../java/htsjdk/samtools/SAMRecordUnitTest.java | 51 ++++++++ .../java/htsjdk/samtools/SAMRecordUtilTest.java | 61 --------- 4 files changed, 190 insertions(+), 162 deletions(-) delete mode 100644 src/test/java/htsjdk/samtools/SAMRecordUtilTest.java diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 025f10cbf..ba06c3ff9 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -26,17 +26,12 @@ import htsjdk.samtools.util.CoordMath; import htsjdk.samtools.util.Locatable; +import htsjdk.samtools.util.SequenceUtil; import htsjdk.samtools.util.StringUtil; import java.io.Serializable; import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** @@ -161,6 +156,16 @@ */ public static final int MAX_INSERT_SIZE = 1<<29; + /** + * Tags that are known to need the reverse complement if the read is reverse complemented. + */ + public static List TAGS_TO_REVERSE_COMPLEMENT = Arrays.asList(SAMTag.E2.name(), SAMTag.SQ.name()); + + /** + * Tags that are known to need the reverse if the read is reverse complemented. + */ + public static List TAGS_TO_REVERSE = Arrays.asList(SAMTag.OQ.name(), SAMTag.U2.name()); + private String mReadName = null; private byte[] mReadBases = NULL_SEQUENCE; private byte[] mBaseQualities = NULL_QUALS; @@ -2249,5 +2254,127 @@ public final Object removeTransientAttribute(final Object key) { if (this.transientAttributes != null) return this.transientAttributes.remove(key); else return null; } -} + /** + * Reverse-complement bases and reverse quality scores along with known optional attributes that + * need the same treatment. Changes made after making a copy of the bases, qualities, + * and attributes. If in-place update is needed use {@link #reverseComplement(SAMRecord, boolean)}. + * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} + * for the default set of tags that are handled. + */ + public static void reverseComplement(final SAMRecord rec) { + reverseComplement(rec, false); + } + + /** + * Reverse-complement bases and reverse quality scores along with known optional attributes that + * need the same treatment. Optionally makes a copy of the bases, qualities or attributes instead + * of altering them in-place. See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} + * for the default set of tags that are handled. + * + * @param rec Record to reverse complement. + * @param inplace Setting this to false will clone all attributes, bases and qualities before changing the values. + */ + public static void reverseComplement(final SAMRecord rec, boolean inplace) { + reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); + } + + /** + * Reverse complement bases and reverse quality scores. In addition reverse complement any + * non-null attributes specified by tagsToRevcomp and reverse and non-null attributes + * specified by tagsToReverse. + */ + public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { + final byte[] readBases = inplace ? rec.getReadBases() : rec.getReadBases().clone(); + SequenceUtil.reverseComplement(readBases); + rec.setReadBases(readBases); + final byte qualities[] = inplace ? rec.getBaseQualities() : rec.getBaseQualities().clone(); + reverseArray(qualities); + rec.setBaseQualities(qualities); + + // Deal with tags that need to be reverse complemented + if (tagsToRevcomp != null) { + for (final String tag: tagsToRevcomp) { + Object value = rec.getAttribute(tag); + if (value != null) { + if (value instanceof byte[]) { + value = inplace ? value : ((byte[]) value).clone(); + SequenceUtil.reverseComplement((byte[]) value); + } + else if (value instanceof String) { + //SequenceUtil.reverseComplement is in-place for bytes but copies Strings since they are immutable. + value = SequenceUtil.reverseComplement((String) value); + } + else throw new UnsupportedOperationException("Don't know how to reverse complement: " + value); + rec.setAttribute(tag, value); + } + } + } + + // Deal with tags that needed to just be reversed + if (tagsToReverse != null) { + for (final String tag : tagsToReverse) { + Object value = rec.getAttribute(tag); + if (value != null) { + if (value instanceof String) { + value = StringUtil.reverseString((String) value); + } + else if (value.getClass().isArray()) { + if (value instanceof byte[]) { + value = inplace ? value : ((byte[]) value).clone(); + reverseArray((byte[]) value); + } + else if (value instanceof short[]) { + value = inplace ? value : ((short[]) value).clone(); + reverseArray((short[]) value); + } + else if (value instanceof int[]) { + value = inplace ? value : ((int[]) value).clone(); + reverseArray((int[]) value); + } + else if (value instanceof float[]) { + value = inplace ? value : ((float[]) value).clone(); + reverseArray((float[]) value); + } + else throw new UnsupportedOperationException("Reversing array attribute of type " + value.getClass().getComponentType() + " not supported."); + } + else throw new UnsupportedOperationException("Don't know how to reverse: " + value); + + rec.setAttribute(tag, value); + } + } + } + } + + private static void reverseArray(final byte[] array) { + for (int i=0, j=array.length-1; i TAGS_TO_REVERSE_COMPLEMENT = Arrays.asList(SAMTag.E2.name(), SAMTag.SQ.name()); public static List TAGS_TO_REVERSE = Arrays.asList(SAMTag.OQ.name(), SAMTag.U2.name()); @@ -45,7 +46,7 @@ * for the default set of tags that are handled. */ public static void reverseComplement(final SAMRecord rec) { - reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, true); + SAMRecord.reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, true); } /** @@ -58,7 +59,7 @@ public static void reverseComplement(final SAMRecord rec) { * @param inplace Setting this to false will clone all attributes, bases and qualities before changing the values. */ public static void reverseComplement(final SAMRecord rec, boolean inplace) { - reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); + SAMRecord.reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); } /** @@ -67,96 +68,6 @@ public static void reverseComplement(final SAMRecord rec, boolean inplace) { * specified by tagsToReverse. */ public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { - final byte[] readBases = inplace ? rec.getReadBases() : rec.getReadBases().clone(); - SequenceUtil.reverseComplement(readBases); - rec.setReadBases(readBases); - final byte qualities[] = inplace ? rec.getBaseQualities() : rec.getBaseQualities().clone(); - reverseArray(qualities); - rec.setBaseQualities(qualities); - - // Deal with tags that need to be reverse complemented - if (tagsToRevcomp != null) { - for (final String tag: tagsToRevcomp) { - Object value = rec.getAttribute(tag); - if (value != null) { - if (value instanceof byte[]) { - value = inplace ? value : ((byte[]) value).clone(); - SequenceUtil.reverseComplement((byte[]) value); - } - else if (value instanceof String) { - //SequenceUtil.reverseComplement is in-place for bytes but copies Strings since they are immutable. - value = SequenceUtil.reverseComplement((String) value); - } - else throw new UnsupportedOperationException("Don't know how to reverse complement: " + value); - rec.setAttribute(tag, value); - } - } - } - - // Deal with tags that needed to just be reversed - if (tagsToReverse != null) { - for (final String tag : tagsToReverse) { - Object value = rec.getAttribute(tag); - if (value != null) { - if (value instanceof String) { - value = StringUtil.reverseString((String) value); - } - else if (value.getClass().isArray()) { - if (value instanceof byte[]) { - value = inplace ? value : ((byte[]) value).clone(); - reverseArray((byte[]) value); - } - else if (value instanceof short[]) { - value = inplace ? value : ((short[]) value).clone(); - reverseArray((short[]) value); - } - else if (value instanceof int[]) { - value = inplace ? value : ((int[]) value).clone(); - reverseArray((int[]) value); - } - else if (value instanceof float[]) { - value = inplace ? value : ((float[]) value).clone(); - reverseArray((float[]) value); - } - else throw new UnsupportedOperationException("Reversing array attribute of type " + value.getClass().getComponentType() + " not supported."); - } - else throw new UnsupportedOperationException("Don't know how to reverse: " + value); - - rec.setAttribute(tag, value); - } - } - } - } - - private static void reverseArray(final byte[] array) { - for (int i=0, j=array.length-1; i Date: Tue, 11 Oct 2016 09:24:47 -0400 Subject: [PATCH 031/137] Addressing new comments --- src/main/java/htsjdk/samtools/SAMRecord.java | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index ba06c3ff9..fd1801016 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -2258,7 +2258,8 @@ public final Object removeTransientAttribute(final Object key) { /** * Reverse-complement bases and reverse quality scores along with known optional attributes that * need the same treatment. Changes made after making a copy of the bases, qualities, - * and attributes. If in-place update is needed use {@link #reverseComplement(SAMRecord, boolean)}. + * and any attributes that will be altered. If in-place update is needed use + * {@link #reverseComplement(SAMRecord, boolean)}. * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. */ @@ -2300,12 +2301,12 @@ public static void reverseComplement(final SAMRecord rec, final Collection Date: Fri, 21 Oct 2016 10:42:47 -0400 Subject: [PATCH 032/137] latest comments --- src/main/java/htsjdk/samtools/SAMRecord.java | 29 ++++++++--------- src/main/java/htsjdk/samtools/SAMRecordUtil.java | 10 ++++-- .../java/htsjdk/samtools/SAMRecordUnitTest.java | 38 +++++++++++++++------- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index fd1801016..13ec3860a 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -2259,12 +2259,12 @@ public final Object removeTransientAttribute(final Object key) { * Reverse-complement bases and reverse quality scores along with known optional attributes that * need the same treatment. Changes made after making a copy of the bases, qualities, * and any attributes that will be altered. If in-place update is needed use - * {@link #reverseComplement(SAMRecord, boolean)}. + * {@link #reverseComplement(boolean)}. * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. */ - public static void reverseComplement(final SAMRecord rec) { - reverseComplement(rec, false); + public void reverseComplement() { + reverseComplement(false); } /** @@ -2273,11 +2273,10 @@ public static void reverseComplement(final SAMRecord rec) { * of altering them in-place. See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. * - * @param rec Record to reverse complement. * @param inplace Setting this to false will clone all attributes, bases and qualities before changing the values. */ - public static void reverseComplement(final SAMRecord rec, boolean inplace) { - reverseComplement(rec, TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); + public void reverseComplement(boolean inplace) { + reverseComplement(TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); } /** @@ -2285,18 +2284,18 @@ public static void reverseComplement(final SAMRecord rec, boolean inplace) { * non-null attributes specified by tagsToRevcomp and reverse and non-null attributes * specified by tagsToReverse. */ - public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { - final byte[] readBases = inplace ? rec.getReadBases() : rec.getReadBases().clone(); + public void reverseComplement(final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { + final byte[] readBases = inplace ? getReadBases() : getReadBases().clone(); SequenceUtil.reverseComplement(readBases); - rec.setReadBases(readBases); - final byte qualities[] = inplace ? rec.getBaseQualities() : rec.getBaseQualities().clone(); + setReadBases(readBases); + final byte qualities[] = inplace ? getBaseQualities() : getBaseQualities().clone(); reverseArray(qualities); - rec.setBaseQualities(qualities); + setBaseQualities(qualities); // Deal with tags that need to be reverse complemented if (tagsToRevcomp != null) { for (final String tag: tagsToRevcomp) { - Object value = rec.getAttribute(tag); + Object value = getAttribute(tag); if (value != null) { if (value instanceof byte[]) { value = inplace ? value : ((byte[]) value).clone(); @@ -2307,7 +2306,7 @@ public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { - SAMRecord.reverseComplement(rec, tagsToRevcomp, tagsToReverse, inplace); + rec.reverseComplement(tagsToRevcomp, tagsToReverse, inplace); } } diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index 140fede2e..951ecee78 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -973,10 +973,11 @@ public void testResolveNameNullHeader() { SAMRecord.resolveNameFromIndex(1, null); } - @Test public void testReverseComplement() { + @Test + public void testReverseComplement() { final SAMRecord rec = createTestSamRec(); - SAMRecord.reverseComplement(rec, Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), true); + rec.reverseComplement(Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), false); Assert.assertEquals(rec.getReadString(), "GTGTGTGTGT"); Assert.assertEquals(rec.getBaseQualityString(), "IIIIIHHHHH"); Assert.assertEquals(rec.getByteArrayAttribute("X1"), new byte[] {5,4,3,2,1}); @@ -986,19 +987,33 @@ public void testResolveNameNullHeader() { Assert.assertEquals(rec.getStringAttribute("Y1"), "GTTTTCTTTT"); } - @Test public void testSafeReverseComplement() throws CloneNotSupportedException { + /** + * Note that since strings are immutable the Y1 attribute, which is a String, is not reversed in the original even + * if an in-place reverse complement occurred. The bases and qualities are byte[] so they are reversed if in-place + * is true. + */ + @DataProvider + public Object [][] reverseComplementData() { + return new Object[][]{ + {false, "ACACACACAC", "HHHHHIIIII", "AAAAGAAAAC", new byte[] {1,2,3,4,5}, new short[] {1,2,3,4,5}, new int[] {1,2,3,4,5}, new float[] {1,2,3,4,5}}, + {true, "GTGTGTGTGT", "IIIIIHHHHH", "AAAAGAAAAC", new byte[] {5,4,3,2,1}, new short[] {5,4,3,2,1}, new int[] {5,4,3,2,1}, new float[] {5,4,3,2,1}}, + }; + } + + @Test(dataProvider = "reverseComplementData") + public void testSafeReverseComplement(boolean inplace, String bases, String quals, String y1, byte[] x1, short[] x2, int[] x3, float[] x4) throws CloneNotSupportedException { final SAMRecord original = createTestSamRec(); final SAMRecord cloneOfOriginal = (SAMRecord) original.clone(); //Runs a copy (rather than in-place) reverseComplement - SAMRecord.reverseComplement(cloneOfOriginal, Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), false); + cloneOfOriginal.reverseComplement(Arrays.asList("Y1"), Arrays.asList("X1", "X2", "X3", "X4", "X5"), inplace); - Assert.assertEquals(original.getReadString(), "ACACACACAC"); - Assert.assertEquals(original.getBaseQualityString(), "HHHHHIIIII"); - Assert.assertEquals(original.getByteArrayAttribute("X1"), new byte[] {1,2,3,4,5}); - Assert.assertEquals(original.getSignedShortArrayAttribute("X2"), new short[] {1,2,3,4,5}); - Assert.assertEquals(original.getSignedIntArrayAttribute("X3"), new int[] {1,2,3,4,5}); - Assert.assertEquals(original.getFloatArrayAttribute("X4"), new float[] {1.0f,2.0f,3.0f,4.0f,5.0f}); - Assert.assertEquals(original.getStringAttribute("Y1"), "AAAAGAAAAC"); + Assert.assertEquals(original.getReadString(), bases); + Assert.assertEquals(original.getBaseQualityString(), quals); + Assert.assertEquals(original.getByteArrayAttribute("X1"), x1); + Assert.assertEquals(original.getSignedShortArrayAttribute("X2"), x2); + Assert.assertEquals(original.getSignedIntArrayAttribute("X3"), x3); + Assert.assertEquals(original.getFloatArrayAttribute("X4"), x4); + Assert.assertEquals(original.getStringAttribute("Y1"), y1); Assert.assertEquals(cloneOfOriginal.getReadString(), "GTGTGTGTGT"); Assert.assertEquals(cloneOfOriginal.getBaseQualityString(), "IIIIIHHHHH"); @@ -1023,5 +1038,4 @@ public SAMRecord createTestSamRec() { return(rec); } - } From 6f0512b20523c05492811025ffc612440eec0838 Mon Sep 17 00:00:00 2001 From: Darina Nikolaeva Date: Thu, 27 Oct 2016 20:36:27 +0300 Subject: [PATCH 033/137] Fix for Issue 574: CRAM index (#716) --- src/main/java/htsjdk/samtools/CRAMFileReader.java | 66 ++++++++++----- .../java/htsjdk/samtools/CRAMFileReaderTest.java | 96 ++++++++++++++++++---- 2 files changed, 128 insertions(+), 34 deletions(-) diff --git a/src/main/java/htsjdk/samtools/CRAMFileReader.java b/src/main/java/htsjdk/samtools/CRAMFileReader.java index acdb8ba8e..9a29d367f 100644 --- a/src/main/java/htsjdk/samtools/CRAMFileReader.java +++ b/src/main/java/htsjdk/samtools/CRAMFileReader.java @@ -26,7 +26,7 @@ import htsjdk.samtools.seekablestream.SeekableStream; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; -import htsjdk.samtools.util.CoordMath; +import htsjdk.samtools.util.Log; import htsjdk.samtools.util.RuntimeEOFException; import java.io.File; @@ -57,6 +57,8 @@ private ValidationStringency validationStringency; + private final static Log log = Log.getInstance(CRAMFileReader.class); + /** * Create a CRAMFileReader from either a file or input stream using the reference source returned by * {@link ReferenceSource#getDefaultCRAMReferenceSource() getDefaultCRAMReferenceSource}. @@ -95,6 +97,9 @@ public CRAMFileReader(final File cramFile, final InputStream inputStream, this.cramFile = cramFile; this.inputStream = inputStream; this.referenceSource = referenceSource; + if (cramFile != null) { + mIndexFile = findIndexForFile(null, cramFile); + } getIterator(); } @@ -117,7 +122,7 @@ public CRAMFileReader(final File cramFile, final File indexFile, } this.cramFile = cramFile; - this.mIndexFile = indexFile; + mIndexFile = findIndexForFile(indexFile, cramFile); this.referenceSource = referenceSource; getIterator(); @@ -140,6 +145,7 @@ public CRAMFileReader(final File cramFile, final CRAMReferenceSource referenceSo this.cramFile = cramFile; this.referenceSource = referenceSource; + mIndexFile = findIndexForFile(null, cramFile); getIterator(); } @@ -164,21 +170,8 @@ public CRAMFileReader(final InputStream inputStream, final SeekableStream indexI if (referenceSource == null) { throw new IllegalArgumentException("A reference is required for CRAM readers"); } - - this.inputStream = inputStream; this.referenceSource = referenceSource; - this.validationStringency = validationStringency; - - iterator = new CRAMIterator(inputStream, referenceSource, validationStringency); - if (indexInputStream != null) { - SeekableStream baiStream = SamIndexes.asBaiSeekableStreamOrNull(indexInputStream, iterator.getSAMFileHeader().getSequenceDictionary()); - if (null != baiStream) { - mIndex = new CachingBAMFileIndex(baiStream, iterator.getSAMFileHeader().getSequenceDictionary()); - } - else { - throw new IllegalArgumentException("CRAM index must be a BAI or CRAI stream"); - } - } + initWithStreams(inputStream, indexInputStream, validationStringency); } /** @@ -196,7 +189,7 @@ public CRAMFileReader(final InputStream inputStream, final SeekableStream indexI public CRAMFileReader(final InputStream stream, final File indexFile, final CRAMReferenceSource referenceSource, final ValidationStringency validationStringency) throws IOException { - this(stream, indexFile == null ? null: new SeekableFileStream(indexFile), referenceSource, validationStringency); + this(stream, indexFile == null ? null : new SeekableFileStream(indexFile), referenceSource, validationStringency); } /** @@ -211,11 +204,44 @@ public CRAMFileReader(final InputStream stream, * * @throws IllegalArgumentException if the {@code cramFile} or the {@code CRAMReferenceSource} is null */ - public CRAMFileReader(final File cramFile, - final File indexFile, final CRAMReferenceSource referenceSource, + public CRAMFileReader(final File cramFile, final File indexFile, final CRAMReferenceSource referenceSource, final ValidationStringency validationStringency) throws IOException { - this(new FileInputStream(cramFile), indexFile, referenceSource, validationStringency); + if (cramFile == null) { + throw new IllegalArgumentException("Input file can not be null for CRAM reader"); + } + if (referenceSource == null) { + throw new IllegalArgumentException("A reference is required for CRAM readers"); + } this.cramFile = cramFile; + this.referenceSource = referenceSource; + this.mIndexFile = findIndexForFile(indexFile, cramFile); + final SeekableFileStream indexStream = this.mIndexFile == null ? null : new SeekableFileStream(this.mIndexFile); + initWithStreams(new FileInputStream(cramFile), indexStream, validationStringency); + } + + private void initWithStreams(final InputStream inputStream, final SeekableStream indexInputStream, + final ValidationStringency validationStringency) throws IOException { + this.inputStream = inputStream; + this.validationStringency = validationStringency; + iterator = new CRAMIterator(inputStream, referenceSource, validationStringency); + if (indexInputStream != null) { + SeekableStream baiStream = SamIndexes.asBaiSeekableStreamOrNull(indexInputStream, iterator.getSAMFileHeader().getSequenceDictionary()); + if (null != baiStream) { + mIndex = new CachingBAMFileIndex(baiStream, iterator.getSAMFileHeader().getSequenceDictionary()); + } + else { + throw new IllegalArgumentException("CRAM index must be a BAI or CRAI stream"); + } + } + } + + private File findIndexForFile(File indexFile, final File cramFile) { + indexFile = indexFile == null ? SamFiles.findIndex(cramFile) : indexFile; + if (indexFile != null && indexFile.lastModified() < cramFile.lastModified()) { + log.warn("CRAM index file " + indexFile.getAbsolutePath() + + " is older than CRAM " + cramFile.getAbsolutePath()); + } + return indexFile; } @Override diff --git a/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java b/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java index 8c610bdf7..3fcb3bdc9 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java @@ -27,6 +27,7 @@ import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.util.Log; +import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -42,13 +43,18 @@ public class CRAMFileReaderTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); + private static final File CRAM_WITH_CRAI = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); + private static final File CRAM_WITHOUT_CRAI = new File(TEST_DATA_DIR, "cram_query_sorted.cram"); + private static final ReferenceSource REFERENCE = createReferenceSource(); + private static final File INDEX_FILE = new File(TEST_DATA_DIR, "cram_with_crai_index.cram.crai"); + @BeforeClass public void initClass() { Log.setGlobalLogLevel(Log.LogLevel.ERROR); } - private ReferenceSource createReferenceSource() { + private static ReferenceSource createReferenceSource() { byte[] refBases = new byte[10 * 10]; Arrays.fill(refBases, (byte) 'A'); InMemoryReferenceSequenceFile rsf = new InMemoryReferenceSequenceFile(); @@ -60,19 +66,17 @@ private ReferenceSource createReferenceSource() { @Test(description = "Test CRAMReader 1 reference required", expectedExceptions = IllegalStateException.class) public void testCRAMReader1_ReferenceRequired() { - File file = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); InputStream bis = null; // assumes that reference_fasta property is not set and the download service is not enabled - new CRAMFileReader(file, bis); + new CRAMFileReader(CRAM_WITH_CRAI, bis); } // constructor 2: CRAMFileReader(final File cramFile, final InputStream inputStream, final ReferenceSource referenceSource) @Test(description = "Test CRAMReader 2 reference required", expectedExceptions = IllegalArgumentException.class) public void testCRAMReader2ReferenceRequired() { - File file = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); InputStream bis = null; - new CRAMFileReader(file, bis, null); + new CRAMFileReader(CRAM_WITH_CRAI, bis, null); } @Test(description = "Test CRAMReader 2 input required", expectedExceptions = IllegalArgumentException.class) @@ -82,31 +86,66 @@ public void testCRAMReader2_InputRequired() { new CRAMFileReader(file, bis, createReferenceSource()); } + @Test + public void testCRAMReader2_ShouldAutomaticallyFindCRAMIndex() { + InputStream inputStream = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, inputStream, REFERENCE); + reader.getIndex(); + Assert.assertTrue(reader.hasIndex(), "Can't find CRAM existing index."); + } + + @Test(expectedExceptions = SAMException.class) + public void testCRAMReader2_WithoutCRAMIndex() { + InputStream inputStream = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITHOUT_CRAI, inputStream, REFERENCE); + reader.getIndex(); + } + // constructor 3: CRAMFileReader(final File cramFile, final File indexFile, final ReferenceSource referenceSource) @Test(description = "Test CRAMReader 3 reference required", expectedExceptions = IllegalArgumentException.class) public void testCRAMReader3_RequiredReference() { - File inputFile = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); File indexFile = null; ReferenceSource refSource = null; - new CRAMFileReader(inputFile, indexFile, refSource); + new CRAMFileReader(CRAM_WITH_CRAI, indexFile, refSource); } @Test(description = "Test CRAMReader 3 input required", expectedExceptions = IllegalArgumentException.class) - public void testCRAMReader3_InputRequirted() { + public void testCRAMReader3_InputRequired() { File inputFile = null; File indexFile = null; ReferenceSource refSource = null; new CRAMFileReader(inputFile, indexFile, refSource); } + @Test + public void testCRAMReader3_ShouldAutomaticallyFindCRAMIndex() { + File indexFile = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, indexFile, REFERENCE); + reader.getIndex(); + Assert.assertTrue(reader.hasIndex(), "Can't find existing CRAM index."); + } + + @Test + public void testCRAMReader3_ShouldUseCRAMIndex() { + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, INDEX_FILE, REFERENCE); + reader.getIndex(); + Assert.assertTrue(reader.hasIndex(), "Can't find existing CRAM index."); + } + + @Test(expectedExceptions = SAMException.class) + public void testCRAMReader3_WithoutCRAMIndex() { + File indexFile = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITHOUT_CRAI, indexFile, REFERENCE); + reader.getIndex(); + } + // constructor 4: CRAMFileReader(final File cramFile, final ReferenceSource referenceSource) @Test(description = "Test CRAMReader 4 reference required", expectedExceptions = IllegalArgumentException.class) public void testCRAMReader4_ReferenceRequired() { - File inputFile = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); ReferenceSource refSource = null; - new CRAMFileReader(inputFile, refSource); + new CRAMFileReader(CRAM_WITH_CRAI, refSource); } @Test(description = "Test CRAMReader 4 input required", expectedExceptions = IllegalArgumentException.class) @@ -115,6 +154,19 @@ public void testCRAMReader4_InputRequired() { new CRAMFileReader(inputFile, createReferenceSource()); } + @Test + public void testCRAMReader4_ShouldAutomaticallyFindCRAMIndex() { + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, REFERENCE); + reader.getIndex(); + Assert.assertTrue(reader.hasIndex(), "Can't find existing CRAM index."); + } + + @Test(expectedExceptions = SAMException.class) + public void testCRAMReader4_WithoutCRAMIndex() { + CRAMFileReader reader = new CRAMFileReader(CRAM_WITHOUT_CRAI, REFERENCE); + reader.getIndex(); + } + // constructor 5: CRAMFileReader(final InputStream inputStream, final SeekableStream indexInputStream, // final ReferenceSource referenceSource, final ValidationStringency validationStringency) @Test(description = "Test CRAMReader 5 reference required", expectedExceptions = IllegalArgumentException.class) @@ -146,7 +198,6 @@ public void testCRAMReader6_ReferenceRequired() throws IOException { public void testCRAMReader6_InputRequired() throws IOException { InputStream bis = null; File file = null; - ReferenceSource refSource = null; new CRAMFileReader(bis, file, createReferenceSource(), ValidationStringency.STRICT); } @@ -154,10 +205,27 @@ public void testCRAMReader6_InputRequired() throws IOException { // final ValidationStringency validationStringency) @Test(description = "Test CRAMReader 7 reference required", expectedExceptions = IllegalArgumentException.class) public void testCRAMReader7_ReferenceRequired() throws IOException { - InputStream bis = new ByteArrayInputStream(new byte[0]); - File file = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); ReferenceSource refSource = null; - new CRAMFileReader(file, file, refSource, ValidationStringency.STRICT); + new CRAMFileReader(CRAM_WITH_CRAI, CRAM_WITH_CRAI, refSource, ValidationStringency.STRICT); } + @Test + public void testCRAMReader7_ShouldAutomaticallyFindCRAMIndex()throws IOException { + File indexFile = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, indexFile, REFERENCE, ValidationStringency.STRICT); + Assert.assertTrue(reader.hasIndex(), "Can't find existing CRAM index."); + } + + @Test + public void testCRAMReader7_ShouldUseCRAMIndex() throws IOException { + CRAMFileReader reader = new CRAMFileReader(CRAM_WITH_CRAI, INDEX_FILE, REFERENCE, ValidationStringency.STRICT); + Assert.assertTrue(reader.hasIndex(), "Can't find existing CRAM index."); + } + + @Test(expectedExceptions = SAMException.class) + public void testCRAMReader7_WithoutCRAMIndex() throws IOException { + File indexFile = null; + CRAMFileReader reader = new CRAMFileReader(CRAM_WITHOUT_CRAI, indexFile, REFERENCE, ValidationStringency.STRICT); + reader.getIndex(); + } } From 08085880c02a3d0ede155589a1d01ab5dd8c22ae Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Sat, 20 Aug 2016 15:27:33 -0400 Subject: [PATCH 034/137] Add a mergeDictionary utility to SAMSequenceDictionary to be used by picard in MergeBamAlignment --- .../htsjdk/samtools/SAMSequenceDictionary.java | 114 ++++++++++++++++++--- .../htsjdk/samtools/SAMSequenceDictionaryTest.java | 41 ++++++++ 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java index 2b8d18a31..f19598fdd 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java @@ -23,20 +23,20 @@ */ package htsjdk.samtools; +import htsjdk.samtools.util.Log; + import java.io.Serializable; import java.math.BigInteger; import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; +import static htsjdk.samtools.SAMSequenceRecord.UNKNOWN_SEQUENCE_LENGTH; + /** * Collection of SAMSequenceRecords. */ @@ -64,6 +64,8 @@ public SAMSequenceDictionary(final List list) { return Collections.unmodifiableList(mSequences); } + private static Log log = Log.getInstance(SAMSequenceDictionary.class); + public SAMSequenceRecord getSequence(final String name) { return mSequenceMap.get(name); } @@ -135,7 +137,7 @@ public long getReferenceLength() { } return len; } - + /** * @return true is the dictionary is empty */ @@ -146,7 +148,7 @@ public boolean isEmpty() { private static String DICT_MISMATCH_TEMPLATE = "SAM dictionaries are not the same: %s."; /** * Non-comprehensive {@link #equals(Object)}-assertion: instead of calling {@link SAMSequenceRecord#equals(Object)} on constituent - * {@link SAMSequenceRecord}s in this dictionary against its pair in the target dictionary, in order, call + * {@link SAMSequenceRecord}s in this dictionary against its pair in the target dictionary, in order, call * {@link SAMSequenceRecord#isSameSequence(SAMSequenceRecord)}. * Aliases are ignored. * @@ -154,7 +156,7 @@ public boolean isEmpty() { */ public void assertSameDictionary(final SAMSequenceDictionary that) { if (this == that) return; - + final Iterator thatSequences = that.mSequences.iterator(); for (final SAMSequenceRecord thisSequence : mSequences) { if (!thatSequences.hasNext()) @@ -189,7 +191,7 @@ public boolean equals(Object o) { * alternate names fo a given contig. e.g: * 1,chr1,chr01,01,CM000663,NC_000001.10 e.g: * MT,chrM - * + * * @param originalName * existing contig name * @param altName @@ -219,11 +221,11 @@ public SAMSequenceRecord addSequenceAlias(final String originalName, /** * return a MD5 sum for ths dictionary, the checksum is re-computed each * time this method is called. - * + * *

      * md5( (seq1.md5_if_available) + ' '+(seq2.name+seq2.length) + ' '+...)
      * 
- * + * * @return a MD5 checksum for this dictionary or the empty string if it is * empty */ @@ -266,5 +268,93 @@ public String toString() { " length:"+ getReferenceLength()+" "+ " md5:"+md5()+")"; } + + public static final List DEFAULT_DICTIONARY_EQUAL_TAG = Arrays.asList( + SAMSequenceRecord.URI_TAG, + SAMSequenceRecord.MD5_TAG, + SAMSequenceRecord.SEQUENCE_LENGTH_TAG); + + /** + * Will merge dictionaryTags from two dictionaries into one focusing on merging the tags rather than the sequences. + * + * Requires that dictionaries have the same SAMSequence records in the same order. + * For each sequenceIndex, the union of the tags from both sequences will be added to the new sequence, mismatching + * values (for tags that are in both) will generate a warning, and the value from dict1 will be used. + * For tags that are in tagsToEquate. an unequal value will generate an error (an IllegalArgumentException will + * be thrown.) + * + * @param dict1 first dictionary + * @param dict2 first dictionary + * @param tagsToMatch list of tags that must be equal if present in both sequence. Typical values will be MD, LN, and MD + * @return dictionary consisting of the same sequences as the two inputs with the merged values of tags. + */ + static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionary dict1, final SAMSequenceDictionary dict2, final List tagsToMatch) { + + final SAMSequenceDictionary finalDict = new SAMSequenceDictionary(dict1.getSequences()); + for (final SAMSequenceRecord sequence : finalDict.getSequences()) { + final int sequenceIndex = sequence.getSequenceIndex(); + final Set allTags = new HashSet<>(); + + for (SAMSequenceRecord seq : Arrays.asList( + dict1.getSequence(sequenceIndex), + dict2.getSequence(sequenceIndex))) { + + final Set dictTags = seq + .getAttributes().parallelStream() + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + allTags.addAll(dictTags); + } + + for (final String tag : allTags) { + final String value1 = dict1.getSequence(sequenceIndex).getAttribute(tag); + final String value2 = dict2.getSequence(sequenceIndex).getAttribute(tag); + + if (value1 != null && value2 != null && !value1.equals(value2)) { + if (tagsToMatch.contains(tag)) { + final String error = String.format("Cannot merge the two dictionaries. Found sequence entry for which " + + "tags differ: %s and tag %s has the two values: %s and %s", + dict1.getSequence(sequenceIndex).getSequenceName(), + tag, + value1, + value2); + + log.error(error); + throw new IllegalArgumentException(error); + } else { + log.warn("Found sequence entry for which " + + "tags differ: %s and tag %s has the two values: %s and %s. using Value %s", + dict1.getSequence(sequenceIndex).getSequenceName(), + tag, + value1, + value2, value1); + } + } + sequence.setAttribute(tag, value1 == null ? value2 : value1); + } + + final int length1 = dict1.getSequence(sequenceIndex).getSequenceLength(); + final int length2 = dict2.getSequence(sequenceIndex).getSequenceLength(); + + if (length1 != UNKNOWN_SEQUENCE_LENGTH && length2 != UNKNOWN_SEQUENCE_LENGTH && length1 != length2) { + if (tagsToMatch.contains(SAMSequenceRecord.SEQUENCE_LENGTH_TAG)) { + final String error = String.format("Cannot merge the two dictionaries. Found sequence entry for which " + + "lengths differ: %s has lengths %s and %s", + dict1.getSequence(sequenceIndex).getSequenceName(), + length1, length2); + + log.error(error); + throw new IllegalArgumentException(error); + } else { + log.warn("Found sequence entry for which " + + "lengths differ:%s has lengths %s and %s. using Value %s", + dict1.getSequence(sequenceIndex).getSequenceName(), + length1, length2, length1); + } + } + sequence.setSequenceLength(length1 == UNKNOWN_SEQUENCE_LENGTH ? length2 : length1); + } + return finalDict; + } } diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java index 89e1b35fb..44eb54925 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java @@ -27,11 +27,13 @@ package htsjdk.samtools; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.StringReader; import java.io.StringWriter; import java.util.Arrays; +import java.util.Collections; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -89,4 +91,43 @@ public void testXmlSeralization() throws JAXBException { Assert.assertEquals(dict1, dict2); } + @DataProvider(name="testMergeDictionariesData") + Object[][] testMergeDictionariesData(){ + + final SAMSequenceRecord rec1, rec2, rec3, rec4; + rec1 = new SAMSequenceRecord("chr1", 100); + rec2 = new SAMSequenceRecord("chr1", 101); + rec2.setMd5("dummy"); + rec3 = new SAMSequenceRecord("chr1", SAMSequenceRecord.UNKNOWN_SEQUENCE_LENGTH); + rec3.setMd5("dummy2"); + + rec4 = new SAMSequenceRecord("chr1", 100); + rec4.setAttribute(SAMSequenceRecord.URI_TAG,"file://some/file/name.ok"); + + + return new Object[][]{ + new Object[]{rec1,rec2,false} + }; + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMergeDictionaries(final SAMSequenceRecord rec1 ,final SAMSequenceRecord rec2, boolean canMerge) throws Exception { + final SAMSequenceDictionary dict1 = new SAMSequenceDictionary(Collections.singletonList(rec1)); + final SAMSequenceDictionary dict2 = new SAMSequenceDictionary(Collections.singletonList(rec2)); + + try { + SAMSequenceDictionary.mergeDictionaries(dict1, dict2, SAMSequenceDictionary.DEFAULT_DICTIONARY_EQUAL_TAG); + } catch (final IllegalArgumentException e) { + if (!canMerge){ + throw e; + } else{ + throw new Exception("Expected to be able to merge dictionaries, but wasn't"); + } + } + if (canMerge){ + throw new Exception("Expected to be able to merge dictionaries, but wasn't"); + } else { + throw new Exception("Expected to not be able to merge dictionaries, but was able"); + } + } } From d3cabf498331e81286acc11ddbeac14f029770ce Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Sat, 20 Aug 2016 20:47:31 -0400 Subject: [PATCH 035/137] tests (found a bug!) --- .../htsjdk/samtools/SAMSequenceDictionary.java | 35 +++++++++++++++------- .../htsjdk/samtools/SAMSequenceDictionaryTest.java | 24 +++++++++++---- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java index f19598fdd..551500e03 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java @@ -285,14 +285,29 @@ public String toString() { * * @param dict1 first dictionary * @param dict2 first dictionary - * @param tagsToMatch list of tags that must be equal if present in both sequence. Typical values will be MD, LN, and MD + * @param tagsToMatch list of tags that must be equal if present in both sequence. Typical values will be MD, and LN * @return dictionary consisting of the same sequences as the two inputs with the merged values of tags. */ - static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionary dict1, final SAMSequenceDictionary dict2, final List tagsToMatch) { - - final SAMSequenceDictionary finalDict = new SAMSequenceDictionary(dict1.getSequences()); - for (final SAMSequenceRecord sequence : finalDict.getSequences()) { + static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionary dict1, + final SAMSequenceDictionary dict2, + final List tagsToMatch) { + + if(dict1.getSequences().size()!=dict2.getSequences().size()) { + throw new IllegalArgumentException(String.format("Do not use this function to merge dictionaries with " + + "different number of sequences in them. Found %d and %d sequences", + dict1.getSequences().size(), dict2.getSequences().size())); + } + final SAMSequenceDictionary finalDict = new SAMSequenceDictionary(); + for (final SAMSequenceRecord sequence : dict1.getSequences()) { + final SAMSequenceRecord mergedSequence = new SAMSequenceRecord(sequence.getSequenceName(), + UNKNOWN_SEQUENCE_LENGTH); + finalDict.addSequence(mergedSequence); final int sequenceIndex = sequence.getSequenceIndex(); + if (!dict2.getSequence(sequenceIndex).getSequenceName().equals(sequence.getSequenceName())) { + throw new IllegalArgumentException(String.format("Non-equal sequence names (%s and %s) at index %d in " + + "the dictionaries.", + sequence.getSequenceName(), dict2.getSequence(sequenceIndex).getSequenceName(), sequenceIndex)); + } final Set allTags = new HashSet<>(); for (SAMSequenceRecord seq : Arrays.asList( @@ -313,7 +328,7 @@ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionar if (value1 != null && value2 != null && !value1.equals(value2)) { if (tagsToMatch.contains(tag)) { final String error = String.format("Cannot merge the two dictionaries. Found sequence entry for which " + - "tags differ: %s and tag %s has the two values: %s and %s", + "tag values differ: Sequence %s at tag %s has the values: %s and %s", dict1.getSequence(sequenceIndex).getSequenceName(), tag, value1, @@ -323,14 +338,14 @@ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionar throw new IllegalArgumentException(error); } else { log.warn("Found sequence entry for which " + - "tags differ: %s and tag %s has the two values: %s and %s. using Value %s", + "tags differ: %s and tag %s has the two values: %s and %s. Using Value %s", dict1.getSequence(sequenceIndex).getSequenceName(), tag, value1, value2, value1); } } - sequence.setAttribute(tag, value1 == null ? value2 : value1); + mergedSequence.setAttribute(tag, value1 == null ? value2 : value1); } final int length1 = dict1.getSequence(sequenceIndex).getSequenceLength(); @@ -347,12 +362,12 @@ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionar throw new IllegalArgumentException(error); } else { log.warn("Found sequence entry for which " + - "lengths differ:%s has lengths %s and %s. using Value %s", + "lengths differ: Sequence %s has lengths %s and %s. Using Value %s", dict1.getSequence(sequenceIndex).getSequenceName(), length1, length2, length1); } } - sequence.setSequenceLength(length1 == UNKNOWN_SEQUENCE_LENGTH ? length2 : length1); + mergedSequence.setSequenceLength(length1 == UNKNOWN_SEQUENCE_LENGTH ? length2 : length1); } return finalDict; } diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java index 44eb54925..a7d369d48 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java @@ -92,9 +92,9 @@ public void testXmlSeralization() throws JAXBException { } @DataProvider(name="testMergeDictionariesData") - Object[][] testMergeDictionariesData(){ + public Object[][] testMergeDictionariesData(){ - final SAMSequenceRecord rec1, rec2, rec3, rec4; + final SAMSequenceRecord rec1, rec2, rec3, rec4, rec5; rec1 = new SAMSequenceRecord("chr1", 100); rec2 = new SAMSequenceRecord("chr1", 101); rec2.setMd5("dummy"); @@ -104,14 +104,26 @@ public void testXmlSeralization() throws JAXBException { rec4 = new SAMSequenceRecord("chr1", 100); rec4.setAttribute(SAMSequenceRecord.URI_TAG,"file://some/file/name.ok"); + rec5 = new SAMSequenceRecord("chr2", 200); + rec4.setAttribute(SAMSequenceRecord.URI_TAG,"file://some/file/name.ok"); return new Object[][]{ - new Object[]{rec1,rec2,false} + new Object[]{rec1, rec1, true}, + new Object[]{rec2, rec2, true}, + new Object[]{rec3, rec3, true}, + new Object[]{rec4, rec4, true}, + new Object[]{rec1, rec2, false},//since 100 != 101 in Length + new Object[]{rec1, rec3, true}, + new Object[]{rec1, rec4, true}, + new Object[]{rec2, rec3, false}, // since MD5 is not equal + new Object[]{rec2, rec4, false}, //length differs + new Object[]{rec3, rec4, true}, + new Object[]{rec4, rec5, false}, // different name }; } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testMergeDictionaries(final SAMSequenceRecord rec1 ,final SAMSequenceRecord rec2, boolean canMerge) throws Exception { + @Test(dataProvider = "testMergeDictionariesData", expectedExceptions = IllegalArgumentException.class) + public void testMergeDictionaries(final SAMSequenceRecord rec1, final SAMSequenceRecord rec2, boolean canMerge) throws Exception { final SAMSequenceDictionary dict1 = new SAMSequenceDictionary(Collections.singletonList(rec1)); final SAMSequenceDictionary dict2 = new SAMSequenceDictionary(Collections.singletonList(rec2)); @@ -125,7 +137,7 @@ public void testMergeDictionaries(final SAMSequenceRecord rec1 ,final SAMSequenc } } if (canMerge){ - throw new Exception("Expected to be able to merge dictionaries, but wasn't"); + throw new IllegalArgumentException("Expected to be able to merge dictionaries, and was indeed able to do so."); } else { throw new Exception("Expected to not be able to merge dictionaries, but was able"); } From 76818d68dcc093ea2c7f4f7a819ec9d36c1458cd Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Mon, 22 Aug 2016 07:30:38 -0400 Subject: [PATCH 036/137] more reviewer comments now incorporated --- src/main/java/htsjdk/samtools/SAMSequenceDictionary.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java index 551500e03..14cc52321 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java @@ -270,7 +270,6 @@ public String toString() { } public static final List DEFAULT_DICTIONARY_EQUAL_TAG = Arrays.asList( - SAMSequenceRecord.URI_TAG, SAMSequenceRecord.MD5_TAG, SAMSequenceRecord.SEQUENCE_LENGTH_TAG); @@ -308,18 +307,11 @@ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionar "the dictionaries.", sequence.getSequenceName(), dict2.getSequence(sequenceIndex).getSequenceName(), sequenceIndex)); } - final Set allTags = new HashSet<>(); - for (SAMSequenceRecord seq : Arrays.asList( - dict1.getSequence(sequenceIndex), - dict2.getSequence(sequenceIndex))) { - - final Set dictTags = seq - .getAttributes().parallelStream() - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); - allTags.addAll(dictTags); - } + final Set allTags = new HashSet<>(dict1.getSequence(sequenceIndex).getAttributes() + .stream().map(Map.Entry::getKey).collect(Collectors.toSet())); + allTags.addAll(dict2.getSequence(sequenceIndex).getAttributes() + .stream().map(Map.Entry::getKey).collect(Collectors.toSet())); for (final String tag : allTags) { final String value1 = dict1.getSequence(sequenceIndex).getAttribute(tag); From 45ee380541f8512665f0452a96fb3cb5b83868a0 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Thu, 22 Sep 2016 10:42:56 -0400 Subject: [PATCH 037/137] - responding to review comments --- .../htsjdk/samtools/SAMSequenceDictionary.java | 100 +++++++++------------ .../htsjdk/samtools/SAMSequenceDictionaryTest.java | 8 +- 2 files changed, 49 insertions(+), 59 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java index 14cc52321..b7744d796 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java @@ -29,13 +29,15 @@ import java.math.BigInteger; import java.security.MessageDigest; import java.util.*; +import java.util.stream.Collector; import java.util.stream.Collectors; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; -import static htsjdk.samtools.SAMSequenceRecord.UNKNOWN_SEQUENCE_LENGTH; +import static htsjdk.samtools.SAMSequenceRecord.*; +import static java.util.stream.Collectors.toList; /** * Collection of SAMSequenceRecords. @@ -279,87 +281,73 @@ public String toString() { * Requires that dictionaries have the same SAMSequence records in the same order. * For each sequenceIndex, the union of the tags from both sequences will be added to the new sequence, mismatching * values (for tags that are in both) will generate a warning, and the value from dict1 will be used. - * For tags that are in tagsToEquate. an unequal value will generate an error (an IllegalArgumentException will - * be thrown.) + * For tags that are in tagsToEquate an unequal value will generate an error (an IllegalArgumentException will + * be thrown.) tagsToEquate must include LN and MD. * * @param dict1 first dictionary * @param dict2 first dictionary - * @param tagsToMatch list of tags that must be equal if present in both sequence. Typical values will be MD, and LN + * @param tagsToMatch list of tags that must be equal if present in both sequence. Must contain MD, and LN * @return dictionary consisting of the same sequences as the two inputs with the merged values of tags. */ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionary dict1, final SAMSequenceDictionary dict2, final List tagsToMatch) { - if(dict1.getSequences().size()!=dict2.getSequences().size()) { + // We require MD and LN to match. + if (!tagsToMatch.contains(MD5_TAG) || !tagsToMatch.contains(SEQUENCE_LENGTH_TAG)) { + throw new IllegalArgumentException("Both " + MD5_TAG + " and " + SEQUENCE_LENGTH_TAG + " must be matched " + + "when merging dictionaries. Found: " + String.join(",", tagsToMatch)); + } + + if (!dict1.getSequences().stream().map(SAMSequenceRecord::getSequenceName).collect(Collectors.toList()).equals( + dict2.getSequences().stream().map(SAMSequenceRecord::getSequenceName).collect(Collectors.toList()))) { + throw new IllegalArgumentException(String.format("Do not use this function to merge dictionaries with " + - "different number of sequences in them. Found %d and %d sequences", - dict1.getSequences().size(), dict2.getSequences().size())); + "different sequences in them. Sequences must be in the same order as well. Found [%s] and [%s].", + String.join(", ", dict1.getSequences().stream().map(SAMSequenceRecord::getSequenceName).collect(toList())), + String.join(", ", dict2.getSequences().stream().map(SAMSequenceRecord::getSequenceName).collect(toList())))); } + final SAMSequenceDictionary finalDict = new SAMSequenceDictionary(); - for (final SAMSequenceRecord sequence : dict1.getSequences()) { - final SAMSequenceRecord mergedSequence = new SAMSequenceRecord(sequence.getSequenceName(), - UNKNOWN_SEQUENCE_LENGTH); - finalDict.addSequence(mergedSequence); - final int sequenceIndex = sequence.getSequenceIndex(); - if (!dict2.getSequence(sequenceIndex).getSequenceName().equals(sequence.getSequenceName())) { - throw new IllegalArgumentException(String.format("Non-equal sequence names (%s and %s) at index %d in " + - "the dictionaries.", - sequence.getSequenceName(), dict2.getSequence(sequenceIndex).getSequenceName(), sequenceIndex)); - } + for (int sequenceIndex = 0; sequenceIndex < dict1.getSequences().size(); sequenceIndex++) { + final SAMSequenceRecord s1 = dict1.getSequence(sequenceIndex); + final SAMSequenceRecord s2 = dict2.getSequence(sequenceIndex); - final Set allTags = new HashSet<>(dict1.getSequence(sequenceIndex).getAttributes() - .stream().map(Map.Entry::getKey).collect(Collectors.toSet())); - allTags.addAll(dict2.getSequence(sequenceIndex).getAttributes() - .stream().map(Map.Entry::getKey).collect(Collectors.toSet())); + final String sName = s1.getSequenceName(); + final SAMSequenceRecord sMerged = new SAMSequenceRecord(sName, UNKNOWN_SEQUENCE_LENGTH); + finalDict.addSequence(sMerged); + + final Set allTags = new HashSet<>(); + s1.getAttributes().stream().forEach(a -> allTags.add(a.getKey())); + s2.getAttributes().stream().forEach(a -> allTags.add(a.getKey())); for (final String tag : allTags) { - final String value1 = dict1.getSequence(sequenceIndex).getAttribute(tag); - final String value2 = dict2.getSequence(sequenceIndex).getAttribute(tag); + final String value1 = s1.getAttribute(tag); + final String value2 = s2.getAttribute(tag); if (value1 != null && value2 != null && !value1.equals(value2)) { + String baseMessage = String.format("Found sequence entry for which " + + "tags differ: %s and tag %s has the two values: %s and %s.", + sName, tag, value1, value2); + if (tagsToMatch.contains(tag)) { - final String error = String.format("Cannot merge the two dictionaries. Found sequence entry for which " + - "tag values differ: Sequence %s at tag %s has the values: %s and %s", - dict1.getSequence(sequenceIndex).getSequenceName(), - tag, - value1, - value2); - - log.error(error); - throw new IllegalArgumentException(error); + log.error("Cannot merge dictionaries. ", baseMessage); + throw new IllegalArgumentException("Cannot merge dictionaries. " + baseMessage); } else { - log.warn("Found sequence entry for which " + - "tags differ: %s and tag %s has the two values: %s and %s. Using Value %s", - dict1.getSequence(sequenceIndex).getSequenceName(), - tag, - value1, - value2, value1); + log.warn(baseMessage, " Using ", value1); } } - mergedSequence.setAttribute(tag, value1 == null ? value2 : value1); + sMerged.setAttribute(tag, value1 == null ? value2 : value1); } - final int length1 = dict1.getSequence(sequenceIndex).getSequenceLength(); - final int length2 = dict2.getSequence(sequenceIndex).getSequenceLength(); + final int length1 = s1.getSequenceLength(); + final int length2 = s2.getSequenceLength(); if (length1 != UNKNOWN_SEQUENCE_LENGTH && length2 != UNKNOWN_SEQUENCE_LENGTH && length1 != length2) { - if (tagsToMatch.contains(SAMSequenceRecord.SEQUENCE_LENGTH_TAG)) { - final String error = String.format("Cannot merge the two dictionaries. Found sequence entry for which " + - "lengths differ: %s has lengths %s and %s", - dict1.getSequence(sequenceIndex).getSequenceName(), - length1, length2); - - log.error(error); - throw new IllegalArgumentException(error); - } else { - log.warn("Found sequence entry for which " + - "lengths differ: Sequence %s has lengths %s and %s. Using Value %s", - dict1.getSequence(sequenceIndex).getSequenceName(), - length1, length2, length1); - } + throw new IllegalArgumentException(String.format("Cannot merge the two dictionaries. " + + "Found sequence entry for which " + "lengths differ: %s has lengths %s and %s", sName, length1, length2)); } - mergedSequence.setSequenceLength(length1 == UNKNOWN_SEQUENCE_LENGTH ? length2 : length1); + sMerged.setSequenceLength(length1 == UNKNOWN_SEQUENCE_LENGTH ? length2 : length1); } return finalDict; } diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java index a7d369d48..0b1a50780 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java @@ -32,8 +32,10 @@ import java.io.StringReader; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -130,10 +132,10 @@ public void testMergeDictionaries(final SAMSequenceRecord rec1, final SAMSequenc try { SAMSequenceDictionary.mergeDictionaries(dict1, dict2, SAMSequenceDictionary.DEFAULT_DICTIONARY_EQUAL_TAG); } catch (final IllegalArgumentException e) { - if (!canMerge){ + if (canMerge) { + throw new Exception("Expected to be able to merge dictionaries, but wasn't:" , e); + } else { throw e; - } else{ - throw new Exception("Expected to be able to merge dictionaries, but wasn't"); } } if (canMerge){ From 2e6643b850683cebf5578cf794a1a1f9971cd2b4 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Tue, 8 Nov 2016 13:56:26 -0500 Subject: [PATCH 038/137] Change default logging level to INFO --- src/main/java/htsjdk/samtools/util/Log.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/util/Log.java b/src/main/java/htsjdk/samtools/util/Log.java index efd7b67d8..acbd3c425 100644 --- a/src/main/java/htsjdk/samtools/util/Log.java +++ b/src/main/java/htsjdk/samtools/util/Log.java @@ -43,7 +43,7 @@ /** Enumeration for setting log levels. */ public static enum LogLevel { ERROR, WARNING, INFO, DEBUG } - private static LogLevel globalLogLevel = LogLevel.DEBUG; + private static LogLevel globalLogLevel = LogLevel.INFO; private final Class clazz; private final String className; From 8e7fe7a0b204096c6d0736d473f20fb144908572 Mon Sep 17 00:00:00 2001 From: Mariia Zueva Date: Sun, 20 Nov 2016 02:31:54 +0300 Subject: [PATCH 039/137] Missing license header added to the following classes: SamInputResource, SamReader, SamReaderFactory, ByteArraySeekableStream (#742) --- .../java/htsjdk/samtools/SamInputResource.java | 26 +++++++++++++++++++++- src/main/java/htsjdk/samtools/SamReader.java | 24 ++++++++++++++++++++ .../java/htsjdk/samtools/SamReaderFactory.java | 24 ++++++++++++++++++++ .../seekablestream/ByteArraySeekableStream.java | 24 ++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/SamInputResource.java b/src/main/java/htsjdk/samtools/SamInputResource.java index 39d679d81..f25d97bb6 100644 --- a/src/main/java/htsjdk/samtools/SamInputResource.java +++ b/src/main/java/htsjdk/samtools/SamInputResource.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools; import htsjdk.samtools.seekablestream.SeekableFileStream; @@ -470,4 +494,4 @@ InputStream asUnbufferedInputStream() { public SRAAccession asSRAAccession() { return accession; } -} \ No newline at end of file +} diff --git a/src/main/java/htsjdk/samtools/SamReader.java b/src/main/java/htsjdk/samtools/SamReader.java index 6bd6c2165..2f1b2f9dd 100644 --- a/src/main/java/htsjdk/samtools/SamReader.java +++ b/src/main/java/htsjdk/samtools/SamReader.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools; import htsjdk.samtools.util.CloseableIterator; diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index 40f7113a1..bf890d569 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools; import htsjdk.samtools.cram.ref.CRAMReferenceSource; diff --git a/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java b/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java index a0ebaaa85..4f8c322c5 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java @@ -1,3 +1,27 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.seekablestream; import htsjdk.samtools.seekablestream.SeekableStream; From 398c0ee37abf98e06cc29d3252fad9f9bb7ff134 Mon Sep 17 00:00:00 2001 From: ekazachkova Date: Sun, 20 Nov 2016 02:37:36 +0300 Subject: [PATCH 040/137] test coverage for SamFileValidator (#743) --- .../java/htsjdk/samtools/SamFileValidator.java | 39 +++++--- .../java/htsjdk/samtools/ValidateSamFileTest.java | 108 ++++++++++++++++++--- 2 files changed, 119 insertions(+), 28 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index cf18a7f8b..e40bfe94f 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2009 The Broad Institute + * Copyright (c) 2009-2016 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -81,25 +81,40 @@ * @see SAMRecord#isValid() */ public class SamFileValidator { - private Histogram errorsByType = new Histogram(); + + private final static Log log = Log.getInstance(SamFileValidator.class); + private final PrintWriter out; + private Histogram errorsByType; private PairEndInfoMap pairEndInfoByName; - private ReferenceSequenceFileWalker refFileWalker = null; - private boolean verbose = false; - private int maxVerboseOutput = 100; + private ReferenceSequenceFileWalker refFileWalker; + private boolean verbose; + private int maxVerboseOutput; private SAMSortOrderChecker orderChecker; - private Set errorsToIgnore = EnumSet.noneOf(Type.class); - private boolean ignoreWarnings = false; - private boolean bisulfiteSequenced = false; - private IndexValidationStringency indexValidationStringency = IndexValidationStringency.NONE; - private boolean sequenceDictionaryEmptyAndNoWarningEmitted = false; - private final int maxTempFiles; + private Set errorsToIgnore; + private boolean ignoreWarnings; + private boolean bisulfiteSequenced; + private IndexValidationStringency indexValidationStringency; + private boolean sequenceDictionaryEmptyAndNoWarningEmitted; - private final static Log log = Log.getInstance(SamFileValidator.class); + private final int maxTempFiles; public SamFileValidator(final PrintWriter out, final int maxTempFiles) { this.out = out; this.maxTempFiles = maxTempFiles; + this.errorsByType = new Histogram<>(); + this.refFileWalker = null; + this.maxVerboseOutput = 100; + this.indexValidationStringency = IndexValidationStringency.NONE; + this.errorsToIgnore = EnumSet.noneOf(Type.class); + this.verbose = false; + this.ignoreWarnings = false; + this.bisulfiteSequenced = false; + this.sequenceDictionaryEmptyAndNoWarningEmitted = false; + } + + Histogram getErrorsByType() { + return errorsByType; } /** diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index 33a34da3e..4ce0b7a29 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2009 The Broad Institute + * Copyright (c) 2009-2016 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,8 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; @@ -45,6 +47,8 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; /** @@ -55,6 +59,8 @@ */ public class ValidateSamFileTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/ValidateSamFileTest"); + private static final int TERMINATION_GZIP_BLOCK_SIZE = 28; + private static final int RANDOM_NUMBER_TRUNC_BYTE = 128; @Test public void testValidSamFile() throws Exception { @@ -412,21 +418,6 @@ public void testIndexFileValidation() throws Exception { } - private Histogram executeValidation(final SamReader samReader, final ReferenceSequenceFile reference, final IndexValidationStringency stringency) throws IOException { - final File outFile = File.createTempFile("validation", ".txt"); - outFile.deleteOnExit(); - final PrintWriter out = new PrintWriter(outFile); - new SamFileValidator(out, 8000).setIndexValidationStringency(stringency).validateSamFileSummary(samReader, reference); - final LineNumberReader reader = new LineNumberReader(new FileReader(outFile)); - if (reader.readLine().equals("No errors found")) { - return new Histogram(); - } - final MetricsFile outputFile = new MetricsFile(); - outputFile.read(new FileReader(outFile)); - Assert.assertNotNull(outputFile.getHistogram()); - return outputFile.getHistogram(); - } - private void testHeaderVersion(final String version, final boolean expectValid) throws Exception { final File samFile = File.createTempFile("validateHeader.", ".sam"); samFile.deleteOnExit(); @@ -468,4 +459,89 @@ public void duplicateReadsOutOfOrder() throws Exception { Assert.assertFalse(results.isEmpty()); Assert.assertEquals(results.get(SAMValidationError.Type.MATES_ARE_SAME_END.getHistogramString()).getValue(), 2.0); } + + + @DataProvider(name = "TagCorrectlyProcessData") + public Object[][] tagCorrectlyProcessData() throws IOException { + final String E2TagCorrectlyProcessTestData = + "@HD\tVN:1.0\tSO:unsorted\n" + + "@SQ\tSN:chr1\tLN:101\n" + + "@RG\tID:0\tSM:Hi,Mom!\n" + + "E\t147\tchr1\t15\t255\t10M\t=\t2\t-30\tCAACAGAAGC\t)'.*.+2,))\tE2:Z:CAA"; + + final String U2TagCorrectlyProcessTestData = + "@HD\tVN:1.0\tSO:unsorted\n" + + "@SQ\tSN:chr1\tLN:101\n" + + "@RG\tID:0\tSM:Hi,Mom!\n" + + "E\t147\tchr1\t15\t255\t10M\t=\t2\t-30\tCAACAGAAGC\t)'.*.+2,))\tU2:Z:CAA"; + + return new Object[][]{ + {E2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.E2_BASE_EQUALS_PRIMARY_BASE}, + {E2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_E2_LENGTH}, + {U2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_U2_LENGTH} + }; + } + + @Test(dataProvider = "TagCorrectlyProcessData") + public void tagCorrectlyProcessTest(byte[] bytesFromFile, + SAMValidationError.Type errorType) throws Exception { + final SamReader samReader = SamReaderFactory + .makeDefault() + .validationStringency(ValidationStringency.SILENT) + .open( + SamInputResource.of( + new ByteArrayInputStream(bytesFromFile) + ) + ); + final Histogram results = executeValidation(samReader, null, IndexValidationStringency.EXHAUSTIVE); + Assert.assertEquals(results.get(errorType.getHistogramString()).getValue(), 1.0); + } + + @DataProvider(name = "validateBamFileTerminationData") + public Object[][] validateBamFileTerminationData() throws IOException { + return new Object[][]{ + {getBrokenFile(TERMINATION_GZIP_BLOCK_SIZE), SAMValidationError.Type.BAM_FILE_MISSING_TERMINATOR_BLOCK}, + {getBrokenFile(RANDOM_NUMBER_TRUNC_BYTE), SAMValidationError.Type.TRUNCATED_FILE} + }; + } + + @Test(dataProvider = "validateBamFileTerminationData") + public void validateBamFileTerminationTest(File file, SAMValidationError.Type errorType) throws IOException { + final SamFileValidator samFileValidator = new SamFileValidator(new PrintWriter(System.out), 8000); + samFileValidator.validateBamFileTermination(file); + Assert.assertEquals(samFileValidator.getErrorsByType().get(errorType).getValue(), 1.0); + } + + private Histogram executeValidation(final SamReader samReader, final ReferenceSequenceFile reference, + final IndexValidationStringency stringency) throws IOException { + return executeValidationWithErrorIgnoring(samReader, reference, stringency, Collections.EMPTY_LIST); + } + + private Histogram executeValidationWithErrorIgnoring(final SamReader samReader, final ReferenceSequenceFile reference, + final IndexValidationStringency stringency, Collection ignoringError) throws IOException { + final File outFile = File.createTempFile("validation", ".txt"); + outFile.deleteOnExit(); + + final PrintWriter out = new PrintWriter(outFile); + final SamFileValidator samFileValidator = new SamFileValidator(out, 8000); + samFileValidator.setIndexValidationStringency(stringency).setErrorsToIgnore(ignoringError); + samFileValidator.validateSamFileSummary(samReader, reference); + + final LineNumberReader reader = new LineNumberReader(new FileReader(outFile)); + if (reader.readLine().equals("No errors found")) { + return new Histogram<>(); + } + final MetricsFile outputFile = new MetricsFile<>(); + outputFile.read(new FileReader(outFile)); + Assert.assertNotNull(outputFile.getHistogram()); + return outputFile.getHistogram(); + } + + private File getBrokenFile(int truncByte) throws IOException { + final FileChannel stream = FileChannel.open(new File(TEST_DATA_DIR + "/test_samfile_version_1pt5.bam").toPath()); + final File breakingFile = File.createTempFile("trunc", ".bam"); + breakingFile.deleteOnExit(); + FileChannel.open(breakingFile.toPath(), StandardOpenOption.WRITE).transferFrom(stream, 0, stream.size() - truncByte); + return breakingFile; + } } From 5dddddec67103bf90d00d74596fafe2896c7de19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Sun, 20 Nov 2016 14:22:55 +0100 Subject: [PATCH 041/137] More getters for VariantContext (#712) * fixes #371 * removed unused imports * unit tests + solved bug in getAttributesAsList with primitive arrays * added documentation about primitive arrays --- .../htsjdk/variant/variantcontext/CommonInfo.java | 54 ++++++++++++++-- .../variant/variantcontext/VariantContext.java | 4 +- .../variantcontext/VariantContextUnitTest.java | 73 +++++++++++++++++++++- 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java b/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java index 88b02fafb..e2f9083cf 100644 --- a/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java +++ b/src/main/java/htsjdk/variant/variantcontext/CommonInfo.java @@ -37,6 +37,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; /** @@ -243,18 +245,62 @@ public Object getAttribute(String key, Object defaultValue) { return defaultValue; } - /** returns the value as an empty list if the key was not found, - as a java.util.List if the value is a List or an Array, - as a Collections.singletonList if there is only one value */ + /** + * Gets the attributes from a key as a list. + * + * Note: int[] and double[] arrays are boxed. + * + * @return empty list if the key was not found; {@link Collections#singletonList(Object)} if + * there is only one value; a list containing the values if the value is a {@link List} or array. + */ @SuppressWarnings("unchecked") public List getAttributeAsList(String key) { Object o = getAttribute(key); if ( o == null ) return Collections.emptyList(); if ( o instanceof List ) return (List)o; - if ( o.getClass().isArray() ) return Arrays.asList((Object[])o); + if ( o.getClass().isArray() ) { + if (o instanceof int[]) { + return Arrays.stream((int[])o).boxed().collect(Collectors.toList()); + } else if (o instanceof double[]) { + return Arrays.stream((double[])o).boxed().collect(Collectors.toList()); + } + return Arrays.asList((Object[])o); + } return Collections.singletonList(o); } + private List getAttributeAsList(String key, Function transformer) { + return getAttributeAsList(key).stream().map(transformer).collect(Collectors.toList()); + } + + public List getAttributeAsStringList(String key, String defaultValue) { + return getAttributeAsList(key, x -> (x == null) ? defaultValue : String.valueOf(x)); + } + + public List getAttributeAsIntList(String key, Integer defaultValue) { + return getAttributeAsList(key, x -> { + if (x == null || x == VCFConstants.MISSING_VALUE_v4) { + return defaultValue; + } else if (x instanceof Number) { + return ((Number) x).intValue(); + } else { + return Integer.valueOf((String)x); // throws an exception if this isn't a string + } + }); + } + + public List getAttributeAsDoubleList(String key, Double defaultValue) { + return getAttributeAsList(key, x -> { + if (x == null || x == VCFConstants.MISSING_VALUE_v4) { + return defaultValue; + } else if (x instanceof Number) { + return ((Number) x).doubleValue(); + } else { + return Double.valueOf((String)x); // throws an exception if this isn't a string + } + }); + } + public String getAttributeAsString(String key, String defaultValue) { Object x = getAttribute(key); if ( x == null ) return defaultValue; diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 0baf0bd70..5c94bff24 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -747,7 +747,9 @@ public Object getAttribute(String key, Object defaultValue) { as a java.util.List if the value is a List or an Array, as a Collections.singletonList if there is only one value */ public List getAttributeAsList(String key) { return commonInfo.getAttributeAsList(key); } - + public List getAttributeAsStringList(String key, String defaultValue) { return commonInfo.getAttributeAsStringList(key, defaultValue); } + public List getAttributeAsIntList(String key, int defaultValue) { return commonInfo.getAttributeAsIntList(key, defaultValue); } + public List getAttributeAsDoubleList(String key, double defaultValue) { return commonInfo.getAttributeAsDoubleList(key, defaultValue); } public CommonInfo getCommonInfo() { return commonInfo; } diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 34854eaea..32e5596b5 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -38,7 +38,6 @@ import htsjdk.variant.bcf2.BCF2Codec; import htsjdk.variant.vcf.VCFCodec; import htsjdk.tribble.TribbleException; -import htsjdk.variant.VariantBaseTest; import htsjdk.variant.vcf.VCFConstants; import htsjdk.variant.vcf.VCFFileReader; @@ -1478,4 +1477,76 @@ public void testExtractStructuralVariationsData(final File vcfFile) { CloserUtil.close(reader); } } + + @Test + public void testGetAttributeAsIntList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsIntList("Empty", 5).isEmpty()); + // test as integer + Assert.assertEquals(context.getAttributeAsIntList("DefaultIntegerList", 5), Arrays.asList(0, 0, 0, 0, 0)); + Assert.assertEquals(context.getAttributeAsIntList("ListWithMissing", 5), Arrays.asList(1, 5, 5)); + Assert.assertEquals(context.getAttributeAsIntList("IntegerList", 5), Arrays.asList(0, 1, 2, 3)); + Assert.assertEquals(context.getAttributeAsIntList("DoubleList", 5), Arrays.asList(1, 1, 2)); + Assert.assertEquals(context.getAttributeAsIntList("StringList", 5), Arrays.asList(1, 2)); + Assert.assertThrows(() -> context.getAttributeAsIntList("NotNumeric", 5)); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsIntList("MissingList", 5).isEmpty()); + } + + @Test + public void testGetAttributeAsDoubleList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsDoubleList("Empty", 5).isEmpty()); + // test as double + Assert.assertEquals(context.getAttributeAsDoubleList("DefaultIntegerList", 5), Arrays.asList(0d, 0d, 0d, 0d, 0d)); + Assert.assertEquals(context.getAttributeAsDoubleList("ListWithMissing", 5), Arrays.asList(1d, 5d, 5d)); + Assert.assertEquals(context.getAttributeAsDoubleList("IntegerList", 5), Arrays.asList(0d, 1d, 2d, 3d)); + Assert.assertEquals(context.getAttributeAsDoubleList("DoubleList", 5), Arrays.asList(1.8, 1.6, 2.1)); + Assert.assertEquals(context.getAttributeAsDoubleList("StringList", 5), Arrays.asList(1d, 2d)); + Assert.assertThrows(() -> context.getAttributeAsDoubleList("NotNumeric", 5)); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsDoubleList("MissingList", 5).isEmpty()); + } + + @Test + public void testGetAttributeAsStringList() { + final VariantContext context = basicBuilder + .attribute("Empty", new int[0]) + .attribute("DefaultIntegerList", new int[5]) + .attribute("ListWithMissing", new Object[]{1, null, null}) + .attribute("IntegerList", new int[]{0, 1, 2, 3}) + .attribute("DoubleList", new double[]{1.8, 1.6, 2.1}) + .attribute("StringList", new String[]{"1", "2"}) + .attribute("NotNumeric", new String[]{"A", "B"}) + .make(); + // test an empty value + Assert.assertTrue(context.getAttributeAsStringList("Empty", "empty").isEmpty()); + // test as string + Assert.assertEquals(context.getAttributeAsStringList("DefaultIntegerList", "empty"), Arrays.asList("0", "0", "0", "0", "0")); + Assert.assertEquals(context.getAttributeAsStringList("ListWithMissing", "empty"), Arrays.asList("1", "empty", "empty")); + Assert.assertEquals(context.getAttributeAsStringList("IntegerList", "empty"), Arrays.asList("0", "1", "2", "3")); + Assert.assertEquals(context.getAttributeAsStringList("DoubleList", "empty"), Arrays.asList("1.8", "1.6", "2.1")); + Assert.assertEquals(context.getAttributeAsStringList("StringList", "empty"), Arrays.asList("1", "2")); + Assert.assertEquals(context.getAttributeAsStringList("NotNumeric", "empty"), Arrays.asList("A", "B")); + // test the case of a missing key + Assert.assertTrue(context.getAttributeAsStringList("MissingList", "empty").isEmpty()); + } } From 55c0ca85f4b34a7ad9494688bca8b33e51fd072b Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Mon, 21 Nov 2016 17:55:24 -0500 Subject: [PATCH 042/137] fixes VariantContext throws exception on getAltAlleleWithHighestAlleleCount() (when no alt alleles) - fixes https://github.com/broadinstitute/picard/issues/519 - Added test --- .../variant/variantcontext/VariantContext.java | 19 +++------------ .../variantcontext/VariantContextUnitTest.java | 27 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 5c94bff24..8bf37bbf8 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -37,18 +37,7 @@ import htsjdk.variant.vcf.VCFHeaderLineType; import java.io.Serializable; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** @@ -1701,10 +1690,8 @@ public Allele getAltAlleleWithHighestAlleleCount() { return getAlternateAllele(0); return getAlternateAlleles().stream() - .map(allele -> new Tuple<>(allele, getCalledChrCount(allele))) - .max((alleleAndCount1, alleleAndCount2) -> Integer.compare(alleleAndCount1.b, alleleAndCount2.b)) - .get() - .a; + .max(Comparator.comparing(this::getCalledChrCount)) + .orElse(null); } /** diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 32e5596b5..61b917cdc 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -468,6 +468,8 @@ public void testAccessingCompleteGenotypes() { Assert.assertEquals(4, vc.getCalledChrCount(T)); Assert.assertEquals(3, vc.getCalledChrCount(ATC)); Assert.assertEquals(2, vc.getCalledChrCount(Allele.NO_CALL)); + + Assert.assertEquals(T, vc.getAltAlleleWithHighestAlleleCount()); } @Test @@ -491,6 +493,16 @@ public void testAccessingRefGenotypes() { Assert.assertEquals(4, vc.getCalledChrCount(Aref)); Assert.assertEquals(0, vc.getCalledChrCount(T)); Assert.assertEquals(2, vc.getCalledChrCount(Allele.NO_CALL)); + + //bi allelic, only one alt allele + Allele expected; + if (alleles.size()>1) { + expected = alleles.get(1); + } else { + expected = null; + } + + Assert.assertEquals( vc.getAltAlleleWithHighestAlleleCount(), expected); } } @@ -604,6 +616,21 @@ public void testVCFfromGenotypes() { Assert.assertEquals(4, vc125.getCalledChrCount(Aref)); } + @Test + public void testMonomorphicVariant() { + Genotype g1 = GenotypeBuilder.create("AA", Arrays.asList(Aref, Aref)); + Genotype g2 = GenotypeBuilder.create("BB", Arrays.asList(Aref, Allele.NO_CALL)); + Genotype g3 = GenotypeBuilder.create("CC", Arrays.asList(Allele.NO_CALL,Allele.NO_CALL)); + GenotypesContext gc = GenotypesContext.create(g1, g2, g3); + VariantContext vc = new VariantContextBuilder("genotypes", snpLoc, snpLocStart, snpLocStop, Collections.singletonList(Aref)).genotypes(gc).make(); + + Assert.assertEquals(vc.getType(), VariantContext.Type.NO_VARIATION); + Assert.assertNull(vc.getAltAlleleWithHighestAlleleCount()); + Assert.assertEquals(vc.getCalledChrCount(Aref), 3); + + } + + public void testGetGenotypeMethods() { Genotype g1 = GenotypeBuilder.create("AA", Arrays.asList(Aref, Aref)); Genotype g2 = GenotypeBuilder.create("AT", Arrays.asList(Aref, T)); From 4558e37fcab4a32bf0d7aa8827c6320a888309d3 Mon Sep 17 00:00:00 2001 From: Pierre Lindenbaum Date: Tue, 22 Nov 2016 23:33:44 +0100 Subject: [PATCH 043/137] Added getDescription() in VCFFilterHeaderLine (#726) * Added getDescription() in VCFFilterHeaderLine --- .../java/htsjdk/variant/vcf/VCFFilterHeaderLine.java | 18 ++++++++++++++---- .../java/htsjdk/variant/vcf/VCFHeaderUnitTest.java | 4 +++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/variant/vcf/VCFFilterHeaderLine.java b/src/main/java/htsjdk/variant/vcf/VCFFilterHeaderLine.java index 6c7186535..5130963ac 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFFilterHeaderLine.java +++ b/src/main/java/htsjdk/variant/vcf/VCFFilterHeaderLine.java @@ -33,6 +33,8 @@ * A class representing a key=value entry for FILTER fields in the VCF header */ public class VCFFilterHeaderLine extends VCFSimpleHeaderLine { + + private static final long serialVersionUID = 1L; /** * create a VCF filter header line @@ -40,7 +42,7 @@ * @param name the name for this header line * @param description the description for this header line */ - public VCFFilterHeaderLine(String name, String description) { + public VCFFilterHeaderLine(final String name, final String description) { super("FILTER", name, description); } @@ -48,7 +50,7 @@ public VCFFilterHeaderLine(String name, String description) { * Convenience constructor for FILTER whose description is the name * @param name */ - public VCFFilterHeaderLine(String name) { + public VCFFilterHeaderLine(final String name) { super("FILTER", name, name); } @@ -58,7 +60,7 @@ public VCFFilterHeaderLine(String name) { * @param line the header line * @param version the vcf header version */ - public VCFFilterHeaderLine(String line, VCFHeaderVersion version) { + public VCFFilterHeaderLine(final String line, final VCFHeaderVersion version) { super(line, version, "FILTER", Arrays.asList("ID", "Description")); } @@ -66,4 +68,12 @@ public VCFFilterHeaderLine(String line, VCFHeaderVersion version) { public boolean shouldBeAddedToDictionary() { return true; } -} \ No newline at end of file + + /** + * get the "Description" field + * @return the "Description" field + */ + public String getDescription() { + return getGenericFieldValue("Description"); + } +} diff --git a/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java b/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java index af875fcdc..e9135cc72 100644 --- a/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java +++ b/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java @@ -196,7 +196,9 @@ public void testVCFHeaderAddFormatLine() { @Test public void testVCFHeaderAddFilterLine() { final VCFHeader header = getHiSeqVCFHeader(); - final VCFFilterHeaderLine filterLine = new VCFFilterHeaderLine("TestFilterLine"); + final String filterDesc = "TestFilterLine Description"; + final VCFFilterHeaderLine filterLine = new VCFFilterHeaderLine("TestFilterLine",filterDesc); + Assert.assertEquals(filterDesc,filterLine.getDescription()); header.addMetaDataLine(filterLine); Assert.assertTrue(header.getFilterLines().contains(filterLine), "TestFilterLine not found in filter header lines"); From 24c65a1c761a670990ef2733aca53729d7dc292e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 22 Nov 2016 23:38:42 +0100 Subject: [PATCH 044/137] make CustomGzipOutputStream public to be able to set compression level (#746) --- .../samtools/util/CustomGzipOutputStream.java | 24 ++++++++++++++++++++++ src/main/java/htsjdk/samtools/util/IOUtil.java | 19 ----------------- 2 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java diff --git a/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java b/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java new file mode 100644 index 000000000..cb3652ed5 --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java @@ -0,0 +1,24 @@ +package htsjdk.samtools.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +/** + * Hacky little class used to allow us to set the compression level on a GZIP output stream which, for some + * bizarre reason, is not exposed in the standard API. + * + * @author Tim Fennell + */ +public class CustomGzipOutputStream extends GZIPOutputStream { + CustomGzipOutputStream(final OutputStream outputStream, final int bufferSize, final int compressionLevel) throws + IOException { + super(outputStream, bufferSize); + this.def.setLevel(compressionLevel); + } + + CustomGzipOutputStream(final OutputStream outputStream, final int compressionLevel) throws IOException { + super(outputStream); + this.def.setLevel(compressionLevel); + } +} diff --git a/src/main/java/htsjdk/samtools/util/IOUtil.java b/src/main/java/htsjdk/samtools/util/IOUtil.java index 7f0495d4b..b87b40978 100644 --- a/src/main/java/htsjdk/samtools/util/IOUtil.java +++ b/src/main/java/htsjdk/samtools/util/IOUtil.java @@ -944,22 +944,3 @@ public static String slurp(final InputStream is, final Charset charSet) { return output; } } - - -/** - * Hacky little class used to allow us to set the compression level on a GZIP output stream which, for some - * bizarre reason, is not exposed in the standard API. - * - * @author Tim Fennell - */ -class CustomGzipOutputStream extends GZIPOutputStream { - CustomGzipOutputStream(final OutputStream outputStream, final int bufferSize, final int compressionLevel) throws IOException { - super(outputStream, bufferSize); - this.def.setLevel(compressionLevel); - } - - CustomGzipOutputStream(final OutputStream outputStream, final int compressionLevel) throws IOException { - super(outputStream); - this.def.setLevel(compressionLevel); - } -} From a1e31b57a2093c9e93b9642fe21aab868a501443 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Mon, 21 Nov 2016 09:04:22 -0500 Subject: [PATCH 045/137] Add file name to exception messages --- .../samtools/util/BlockCompressedInputStream.java | 61 +++++++++------------ .../util/BlockCompressedOutputStreamTest.java | 57 +++++++++++++++++-- .../htsjdk/tribble/vcfexample.vcf.truncated.gz | Bin 0 -> 470 bytes .../htsjdk/tribble/vcfexample.vcf.truncated.hdr.gz | Bin 0 -> 460 bytes 4 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 src/test/resources/htsjdk/tribble/vcfexample.vcf.truncated.gz create mode 100644 src/test/resources/htsjdk/tribble/vcfexample.vcf.truncated.hdr.gz diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java index 0261b19d8..b0ac0018e 100755 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java @@ -50,6 +50,13 @@ * c.f. http://samtools.sourceforge.net/SAM1.pdf for details of BGZF format */ public class BlockCompressedInputStream extends InputStream implements LocationAware { + + public final static String INCORRECT_HEADER_SIZE_MSG = "Incorrect header size for file: "; + public final static String UNEXPECTED_BLOCK_LENGTH_MSG = "Unexpected compressed block length: "; + public final static String PREMATURE_END_MSG = "Premature end of file: "; + public final static String CANNOT_SEEK_STREAM_MSG = "Cannot seek on stream based file "; + public final static String INVALID_FILE_PTR_MSG = "Invalid file pointer: "; + private InputStream mStream = null; private SeekableStream mFile = null; private byte[] mFileBuffer = null; @@ -84,8 +91,7 @@ public BlockCompressedInputStream(final InputStream stream, final boolean allowB /** * Use this ctor if you wish to call seek() */ - public BlockCompressedInputStream(final File file) - throws IOException { + public BlockCompressedInputStream(final File file) throws IOException { mFile = new SeekableFileStream(file); mStream = null; @@ -121,8 +127,7 @@ public void setCheckCrcs(final boolean check) { * Note that although the next caller can read this many bytes without blocking, the available() method call itself * may block in order to fill an internal buffer if it has been exhausted. */ - public int available() - throws IOException { + public int available() throws IOException { if (mCurrentBlock == null || mCurrentOffset == mCurrentBlock.length) { readBlock(); } @@ -143,8 +148,7 @@ public boolean endOfBlock() { /** * Closes the underlying InputStream or RandomAccessFile */ - public void close() - throws IOException { + public void close() throws IOException { if (mFile != null) { mFile.close(); mFile = null; @@ -164,8 +168,7 @@ public void close() * @return the next byte of data, or -1 if the end of the stream is reached. */ - public int read() - throws IOException { + public int read() throws IOException { return (available() > 0) ? (mCurrentBlock[mCurrentOffset++] & 0xFF) : -1; } @@ -180,8 +183,7 @@ public int read() * @return the total number of bytes read into the buffer, or -1 is there is no more data because the end of * the stream has been reached. */ - public int read(final byte[] buffer) - throws IOException { + public int read(final byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); } @@ -253,8 +255,7 @@ public String readLine() throws IOException { * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of * the stream has been reached. */ - public int read(final byte[] buffer, int offset, int length) - throws IOException { + public int read(final byte[] buffer, int offset, int length) throws IOException { final int originalLength = length; while (length > 0) { final int available = available(); @@ -280,10 +281,9 @@ public int read(final byte[] buffer, int offset, int length) * * @param pos virtual file pointer */ - public void seek(final long pos) - throws IOException { + public void seek(final long pos) throws IOException { if (mFile == null) { - throw new IOException("Cannot seek on stream based file"); + throw new IOException(CANNOT_SEEK_STREAM_MSG); } // Decode virtual file pointer // Upper 48 bits is the byte offset into the compressed stream of a block. @@ -302,7 +302,7 @@ public void seek(final long pos) } if (uncompressedOffset > available || (uncompressedOffset == available && !eof())) { - throw new IOException("Invalid file pointer: " + pos); + throw new IOException(INVALID_FILE_PTR_MSG + pos + " for " + mFile.getSource()); } mCurrentOffset = uncompressedOffset; } @@ -342,8 +342,7 @@ public static long getFileBlock(final long bgzfOffset) { * @param stream Must be at start of file. Throws RuntimeException if !stream.markSupported(). * @return true if the given file looks like a valid BGZF file. */ - public static boolean isValidFile(final InputStream stream) - throws IOException { + public static boolean isValidFile(final InputStream stream) throws IOException { if (!stream.markSupported()) { throw new RuntimeException("Cannot test non-buffered stream"); } @@ -363,8 +362,7 @@ private static boolean isValidBlockHeader(final byte[] buffer) { buffer[13] == BlockCompressedStreamConstants.BGZF_ID2); } - private void readBlock() - throws IOException { + private void readBlock() throws IOException { if (mFileBuffer == null) { mFileBuffer = new byte[BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE]; @@ -378,16 +376,16 @@ private void readBlock() return; } if (count != BlockCompressedStreamConstants.BLOCK_HEADER_LENGTH) { - throw new IOException("Premature end of file"); + throw new IOException(INCORRECT_HEADER_SIZE_MSG + mFile.getSource()); } final int blockLength = unpackInt16(mFileBuffer, BlockCompressedStreamConstants.BLOCK_LENGTH_OFFSET) + 1; if (blockLength < BlockCompressedStreamConstants.BLOCK_HEADER_LENGTH || blockLength > mFileBuffer.length) { - throw new IOException("Unexpected compressed block length: " + blockLength); + throw new IOException(UNEXPECTED_BLOCK_LENGTH_MSG + blockLength + " for " + mFile.getSource()); } final int remaining = blockLength - BlockCompressedStreamConstants.BLOCK_HEADER_LENGTH; count = readBytes(mFileBuffer, BlockCompressedStreamConstants.BLOCK_HEADER_LENGTH, remaining); if (count != remaining) { - throw new FileTruncatedException("Premature end of file"); + throw new FileTruncatedException(PREMATURE_END_MSG + mFile.getSource()); } inflateBlock(mFileBuffer, blockLength); mCurrentOffset = 0; @@ -395,8 +393,7 @@ private void readBlock() mLastBlockLength = blockLength; } - private void inflateBlock(final byte[] compressedBlock, final int compressedLength) - throws IOException { + private void inflateBlock(final byte[] compressedBlock, final int compressedLength) throws IOException { final int uncompressedLength = unpackInt32(compressedBlock, compressedLength-4); byte[] buffer = mCurrentBlock; mCurrentBlock = null; @@ -404,15 +401,14 @@ private void inflateBlock(final byte[] compressedBlock, final int compressedLeng try { buffer = new byte[uncompressedLength]; } catch (final NegativeArraySizeException e) { - throw new RuntimeIOException("BGZF file has invalid uncompressedLength: " + uncompressedLength, e); + throw new RuntimeIOException(mFile.getSource() + " has invalid uncompressedLength: " + uncompressedLength, e); } } blockGunzipper.unzipBlock(buffer, compressedBlock, compressedLength); mCurrentBlock = buffer; } - private int readBytes(final byte[] buffer, final int offset, final int length) - throws IOException { + private int readBytes(final byte[] buffer, final int offset, final int length) throws IOException { if (mFile != null) { return readBytes(mFile, buffer, offset, length); } else if (mStream != null) { @@ -422,8 +418,7 @@ private int readBytes(final byte[] buffer, final int offset, final int length) } } - private static int readBytes(final SeekableStream file, final byte[] buffer, final int offset, final int length) - throws IOException { + private static int readBytes(final SeekableStream file, final byte[] buffer, final int offset, final int length) throws IOException { int bytesRead = 0; while (bytesRead < length) { final int count = file.read(buffer, offset + bytesRead, length - bytesRead); @@ -435,8 +430,7 @@ private static int readBytes(final SeekableStream file, final byte[] buffer, fin return bytesRead; } - private static int readBytes(final InputStream stream, final byte[] buffer, final int offset, final int length) - throws IOException { + private static int readBytes(final InputStream stream, final byte[] buffer, final int offset, final int length) throws IOException { int bytesRead = 0; while (bytesRead < length) { final int count = stream.read(buffer, offset + bytesRead, length - bytesRead); @@ -462,8 +456,7 @@ private int unpackInt32(final byte[] buffer, final int offset) { public enum FileTermination {HAS_TERMINATOR_BLOCK, HAS_HEALTHY_LAST_BLOCK, DEFECTIVE} - public static FileTermination checkTermination(final File file) - throws IOException { + public static FileTermination checkTermination(final File file) throws IOException { final long fileSize = file.length(); if (fileSize < BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length) { return FileTermination.DEFECTIVE; diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java index b9884159c..8a0d97ffe 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java @@ -23,12 +23,16 @@ */ package htsjdk.samtools.util; +import htsjdk.samtools.FileTruncatedException; import htsjdk.samtools.util.zip.DeflaterFactory; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; @@ -37,11 +41,13 @@ public class BlockCompressedOutputStreamTest { + private static final String HTSJDK_TRIBBLE_RESOURCES = "src/test/resources/htsjdk/tribble/"; + @Test public void testBasic() throws Exception { final File f = File.createTempFile("BCOST.", ".gz"); f.deleteOnExit(); - final List linesWritten = new ArrayList(); + final List linesWritten = new ArrayList<>(); System.out.println("Creating file " + f); final BlockCompressedOutputStream bcos = new BlockCompressedOutputStream(f); String s = "Hi, Mom!\n"; @@ -76,11 +82,54 @@ public void testBasic() throws Exception { bcis2.close(); } - @Test - public void testOverflow() throws Exception { + @DataProvider(name = "seekReadExceptionsData") + private Object[][] seekReadExceptionsData() + { + return new Object[][]{ + {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.gz", FileTruncatedException.class, + BlockCompressedInputStream.PREMATURE_END_MSG + System.getProperty("user.dir") + "/" + + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.gz", true, false, 0}, + {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.hdr.gz", IOException.class, + BlockCompressedInputStream.INCORRECT_HEADER_SIZE_MSG + System.getProperty("user.dir") + "/" + + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.hdr.gz", true, false, 0}, + {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", IOException.class, + BlockCompressedInputStream.CANNOT_SEEK_STREAM_MSG, false, true, 0}, + {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", IOException.class, + BlockCompressedInputStream.INVALID_FILE_PTR_MSG + 1000 + " for " + System.getProperty("user.dir") + "/" + + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", true, true, 1000 } + }; + } + + @Test(dataProvider = "seekReadExceptionsData") + public void testSeekReadExceptions(final String filePath, final Class c, final String msg, final boolean isFile, final boolean isSeek, final int pos) throws Exception { + + final BlockCompressedInputStream bcis = isFile ? + new BlockCompressedInputStream(new File(filePath)) : + new BlockCompressedInputStream(new FileInputStream(filePath)); + boolean haveException = false; + try { + if ( isSeek ) { + bcis.seek(pos); + } else { + final BufferedReader reader = new BufferedReader(new InputStreamReader(bcis)); + reader.readLine(); + } + } catch (final Exception e) { + if ( e.getClass().equals(c) ) { + haveException = true; + Assert.assertEquals(e.getMessage(), msg); + } + } + + if ( !haveException ) { + Assert.fail("Expected " + c.getSimpleName()); + } + } + + @Test public void testOverflow() throws Exception { final File f = File.createTempFile("BCOST.", ".gz"); f.deleteOnExit(); - final List linesWritten = new ArrayList(); + final List linesWritten = new ArrayList<>(); System.out.println("Creating file " + f); final BlockCompressedOutputStream bcos = new BlockCompressedOutputStream(f); Random r = new Random(15555); diff --git a/src/test/resources/htsjdk/tribble/vcfexample.vcf.truncated.gz b/src/test/resources/htsjdk/tribble/vcfexample.vcf.truncated.gz new file mode 100644 index 0000000000000000000000000000000000000000..eaeb4998e3be240b221f49e5d4e371ae57da28c3 GIT binary patch literal 470 zcmV;{0V)0;iwFb&00000{{{d;LjnN00i}>lZ<|06hR@@_!IhHPZ`g>##sq@o*qG4V zQG^pDV_YDWD)q;AS?nlLjy=r|jGkxS@w`JnboCTJugl*opexTiw8ti?rzWO|5r!!2 zx3Pa8UYfZ3lXvI$sh-;YR1E#=P*TdA-$CbB!D0h!g?4BS`puA7PY%19QG#g0sO?6*FzD;+K?#f3~!PZzX zl1rtR%2pJCaHbH3QKH~fD9FG7jR0Z$6pQdSDPn4iNn;FDC?TtKw%19aANGKMQS(p} zEr>4&kfjZ;?G4eKY2hS-F@yjr3uo|)EOg=MNEbR%{+%9aO9*{|wk7jhOW`xX8~e}p zj!g(g(k0-?gs_SXq=L^5B^L*$#K%CW=+>CH`l6e+r$kFlnNO9|QH`2}K6N@KGlZ<|06hR@@_!IhHPZ`g>##sq@o*qG4V zQG^pDV_YDWD)q;AS?nlLjy=r|jGkxS@w`JnboCTJugl*opexTiw8ti?rzWO|5r!!2 zx3Pa8UYfZ3lXvI$sh-;YR1E#=P*TdA-$CbB!D0h!g?4BS`puA7PY%19QG#g0sO?6*FzD;+K?#f3~!PZzX zl1rtR%2pJCaHbH3QKH~fD9FG7jR0Z$6pQdSDPn4iNn;FDC?TtKw%19aANGKMQS(p} zEr>4&kfjZ;?G4eKY2hS-F@yjr3uo|)EOg=MNEbR%{+%9aO9*{|wk7jhOW`xX8~e}p zj!g(g(k0-?gs_SXq=L^5B^L*$#K%CW=+>CH`l6e+r$kFlnNO9|QH`2}K6N@KG Date: Thu, 24 Nov 2016 16:55:26 +0300 Subject: [PATCH 046/137] Fix for issue #432 in Picard Tools. CreateSequenceDictionary stalls indefinitely with large genomes (#744) * preparation for fix issue broadinstitute/picard#432 * added a codec for SAMSequenceDictionary * added an option to encode each Sequence individually, rather than having to old the entire Dictionary in memory (for cases were there are many sequences, the source of trouble). * add tests --- .../samtools/SAMSequenceDictionaryCodec.java | 114 +++++++++++++++++++ .../java/htsjdk/samtools/SAMTextHeaderCodec.java | 38 ++++++- .../samtools/SAMSequenceDictionaryCodecTest.java | 122 +++++++++++++++++++++ 3 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/SAMSequenceDictionaryCodec.java create mode 100644 src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionaryCodec.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionaryCodec.java new file mode 100644 index 000000000..e6e3ba592 --- /dev/null +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionaryCodec.java @@ -0,0 +1,114 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools; + +import htsjdk.samtools.util.LineReader; +import java.io.BufferedWriter; + +/** + * "On the fly" codec SAMSequenceDictionaryCodec. + * Encodes each sequence and directly writes it to the Dictionary file. + * + * To use this class you should provide BufferedWriter to it, and so you should close it as you stop using this class. + * You can work with this class as shown below. + * + * Example of using this class: + * + * List dict = ...; + * + * //open BufferedReader and close in try-with-resources + * try(BufferedWriter writer = new BufferedWriter(new FileWriter("path/to/file"))) { + * SAMSequenceDictionaryCodec codec = new SAMSequenceDictionaryCodec(writer); + * + * //we have list of sequences, so encode header line and after that encode each sequence + * codec.encodeHeaderLine(false); + * dict.forEach(codec::encodeSequenceRecord); + *} + * + * or + * + * SAMSequenceDictionary dict = ...; + * + * //open BufferedReader and close in try-with-resources + * try(BufferedWriter writer = new BufferedWriter(new FileWriter("path/to/file"))) { + * SAMSequenceDictionaryCodec codec = new SAMSequenceDictionaryCodec(writer); + * + * //we have complete {@link SAMSequenceDictionary}, so just encode it. + * codec.encode(dict); + *} + * + * @author Pavel_Silin@epam.com, EPAM Systems, Inc. + */ +public class SAMSequenceDictionaryCodec { + + private static final SAMFileHeader EMPTY_HEADER = new SAMFileHeader(); + + private final SAMTextHeaderCodec codec; + + public SAMSequenceDictionaryCodec(final BufferedWriter writer) { + codec = new SAMTextHeaderCodec(); + codec.setmFileHeader(EMPTY_HEADER); + codec.setWriter(writer); + } + + /** + * Write {@link SAMSequenceRecord}. + * @param sequenceRecord object to be converted to text. + */ + public void encodeSequenceRecord(final SAMSequenceRecord sequenceRecord) { + codec.encodeSequenceRecord(sequenceRecord); + } + + /** + * Write Header line. + * @param keepExistingVersionNumber boolean flag to keep existing version number. + */ + public void encodeHeaderLine(final boolean keepExistingVersionNumber) { + codec.encodeHeaderLine(keepExistingVersionNumber); + } + + /** + * Reads text SAM header and converts to a SAMSequenceDictionary object. + * @param reader Where to get header text from. + * @param source Name of the input file, for error messages. May be null. + * @return complete SAMSequenceDictionary object. + */ + public SAMSequenceDictionary decode(final LineReader reader, final String source) { + return codec.decode(reader, source).getSequenceDictionary(); + } + + /** + * Convert {@link SAMSequenceDictionary} from in-memory representation to text representation. + * @param dictionary object to be converted to text. + */ + public void encode(final SAMSequenceDictionary dictionary) { + codec.encodeHeaderLine(false); + dictionary.getSequences().forEach(this::encodeSequenceRecord); + } + + public void setValidationStringency(final ValidationStringency validationStringency) { + codec.setValidationStringency(validationStringency); + } +} diff --git a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java index 491bf9b4b..fb4b02ac3 100644 --- a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java +++ b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java @@ -70,6 +70,14 @@ public static final String COMMENT_PREFIX = HEADER_LINE_START + HeaderRecordType.CO.name() + FIELD_SEPARATOR; + void setWriter(final BufferedWriter writer) { + this.writer = writer; + } + + void setmFileHeader(final SAMFileHeader header) { + this.mFileHeader = header; + } + /** * Reads text SAM header and converts to a SAMFileHeader object. * @param reader Where to get header text from. @@ -80,8 +88,8 @@ public SAMFileHeader decode(final LineReader reader, final String source) { mFileHeader = new SAMFileHeader(); mReader = reader; mSource = source; - sequences = new ArrayList(); - readGroups = new ArrayList(); + sequences = new ArrayList<>(); + readGroups = new ArrayList<>(); while (advanceLine() != null) { final ParsedHeaderLine parsedHeaderLine = new ParsedHeaderLine(mCurrentLine); @@ -387,6 +395,30 @@ public void encode(final Writer writer, final SAMFileHeader header, final boolea } } + /** + * Encode {@link SAMSequenceRecord}. + * Designed for using in {@link SAMSequenceDictionaryCodec}, allows to implement recording on the fly. + * @throws IllegalStateException, if writer is null. + */ + void encodeSequenceRecord(final SAMSequenceRecord sequenceRecord) { + if (writer == null) { + throw new IllegalStateException("writer couldn't be null"); + } + writeSQLine(sequenceRecord); + } + + /** + * Encode HD line. + * Designed for using in {@link SAMSequenceDictionaryCodec}, allows to implement recording on the fly. + * @throws IllegalStateException, if writer is null. + */ + void encodeHeaderLine(final boolean keepExistingVersionNumber) { + if (writer == null) { + throw new IllegalStateException("writer couldn't be null"); + } + writeHDLine(keepExistingVersionNumber); + } + private void println(final String s) { try { writer.append(s); @@ -438,7 +470,7 @@ private void writeHDLine(final boolean keepExistingVersionNumber) { } private void writeSQLine(final SAMSequenceRecord sequenceRecord) { - final int numAttributes =sequenceRecord.getAttributes() != null ? sequenceRecord.getAttributes().size() : 0; + final int numAttributes = sequenceRecord.getAttributes() != null ? sequenceRecord.getAttributes().size() : 0; final String[] fields = new String[3 + numAttributes]; fields[0] = HEADER_LINE_START + HeaderRecordType.SQ; fields[1] = SAMSequenceRecord.SEQUENCE_NAME_TAG + TAG_KEY_VALUE_SEPARATOR + sequenceRecord.getSequenceName(); diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java new file mode 100644 index 000000000..32de1cd82 --- /dev/null +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright (c) 20016 The Broad Institute + * + * 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 htsjdk.samtools; + +import htsjdk.samtools.util.LineReader; +import htsjdk.samtools.util.StringLineReader; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import javax.sound.sampled.Line; +import java.io.BufferedWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.Random; + +import static org.testng.Assert.*; + +/** + * @author Pavel_Silin@epam.com, EPAM Systems, Inc. + */ +public class SAMSequenceDictionaryCodecTest { + + private static final Random random = new Random(); + private SAMSequenceDictionary dictionary; + private StringWriter writer; + private SAMSequenceDictionaryCodec codec; + private BufferedWriter bufferedWriter; + + @BeforeMethod + public void setUp() throws Exception { + String[] seqs = new String[]{"chr1", "chr2", "chr12", "chr16", "chrX"}; + dictionary = new SAMSequenceDictionary(); + for (String seq : seqs) { + dictionary.addSequence(new SAMSequenceRecord(seq, random.nextInt(10_000_000))); + } + writer = new StringWriter(); + bufferedWriter = new BufferedWriter(writer); + codec = new SAMSequenceDictionaryCodec(bufferedWriter); + } + + @Test + public void testEncodeDecodeDictionary() throws Exception { + LineReader readerOne = null; + LineReader readerTwo = null; + try { + codec.encode(dictionary); + bufferedWriter.close(); + readerOne = new StringLineReader(writer.toString()); + SAMSequenceDictionary actual = codec.decode(readerOne, null); + assertEquals(actual, dictionary); + + readerTwo = new StringLineReader(writer.toString()); + + String line = readerTwo.readLine(); + assertTrue(line.startsWith("@HD")); + + line = readerTwo.readLine(); + while (line != null) { + assertTrue(line.startsWith("@SQ")); + line = readerTwo.readLine(); + } + } finally { + assert readerOne != null; + assert readerTwo != null; + readerOne.close(); + readerTwo.close(); + } + } + + @Test + public void testEncodeDecodeListOfSeqs() throws Exception { + LineReader readerOne = null; + LineReader readerTwo = null; + + try { + List sequences = dictionary.getSequences(); + codec.encodeHeaderLine(false); + sequences.forEach(codec::encodeSequenceRecord); + bufferedWriter.close(); + readerOne = new StringLineReader(writer.toString()); + SAMSequenceDictionary actual = codec.decode(readerOne, null); + assertEquals(actual, dictionary); + readerTwo = new StringLineReader(writer.toString()); + + String line = readerTwo.readLine(); + assertTrue(line.startsWith("@HD")); + + line = readerTwo.readLine(); + while (line != null) { + assertTrue(line.startsWith("@SQ")); + line = readerTwo.readLine(); + } + } finally { + assert readerOne != null; + assert readerTwo != null; + readerOne.close(); + readerTwo.close(); + } + } +} From 646996935a17283dd4fcbbd1acc7984c8cab13c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Wed, 30 Nov 2016 23:18:02 +0100 Subject: [PATCH 047/137] Clean some deprecated classes/methods usages in htsjdk (#707) * clean Feature.getChr() * deprecating SAMPairUtil.setMateInfo methods with a SAMFileHeader * remove SRAAccession.isSupported() usages * adding deprecated javadoc new usage in CollectionUtil * adding deprecated javadoc to IntervalList * add javadoc link in deprecated static IOUtils.STANDARD_BUFFER_SIZE * addressing comments in SamPairUtil * removing commented lines in CountRecords --- src/main/java/htsjdk/samtools/SamPairUtil.java | 49 +++++++++++++++++----- .../java/htsjdk/samtools/util/CollectionUtil.java | 10 ++++- src/main/java/htsjdk/samtools/util/IOUtil.java | 2 +- .../java/htsjdk/samtools/util/IntervalList.java | 49 ++++++++++++++-------- src/main/java/htsjdk/tribble/Feature.java | 4 +- src/main/java/htsjdk/tribble/SimpleFeature.java | 5 --- .../java/htsjdk/tribble/bed/SimpleBEDFeature.java | 8 ---- .../java/htsjdk/tribble/example/CountRecords.java | 4 -- .../htsjdk/tribble/example/ExampleBinaryCodec.java | 2 +- .../htsjdk/tribble/gelitext/GeliTextFeature.java | 6 --- .../java/htsjdk/tribble/index/IndexFactory.java | 12 +++--- .../index/interval/IntervalIndexCreator.java | 4 +- .../tribble/index/linear/LinearIndexCreator.java | 4 +- .../tribble/index/tabix/TabixIndexCreator.java | 2 +- .../variant/variantcontext/VariantContext.java | 6 +-- .../variantcontext/VariantContextComparator.java | 2 +- .../variant/variantcontext/VariantJEXLContext.java | 2 +- .../variantcontext/writer/BCF2FieldWriter.java | 2 +- .../variant/variantcontext/writer/BCF2Writer.java | 6 +-- .../writer/SortingVariantContextWriterBase.java | 10 ++--- src/main/java/htsjdk/variant/vcf/VCFEncoder.java | 4 +- .../java/htsjdk/variant/vcf/VCFFileReader.java | 2 +- .../java/htsjdk/samtools/sra/AbstractSRATest.java | 4 +- src/test/java/htsjdk/tribble/bed/BEDCodecTest.java | 18 ++++---- .../java/htsjdk/tribble/gelitext/GeliTextTest.java | 2 +- .../variantcontext/VariantContextTestProvider.java | 2 +- .../variantcontext/VariantContextUnitTest.java | 18 ++++---- 27 files changed, 132 insertions(+), 107 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamPairUtil.java b/src/main/java/htsjdk/samtools/SamPairUtil.java index 01a59cb52..ee1707bd5 100644 --- a/src/main/java/htsjdk/samtools/SamPairUtil.java +++ b/src/main/java/htsjdk/samtools/SamPairUtil.java @@ -182,6 +182,15 @@ public static int computeInsertSize(final SAMRecord firstEnd, final SAMRecord se } /** + * Write the mate info for two SAMRecords. This will always clear/remove any mate cigar tag that is present. + * @param rec1 the first SAM record + * @param rec2 the second SAM record + */ + public static void setMateInfo(final SAMRecord rec1, final SAMRecord rec2) { + setMateInfo(rec1, rec2, false); + } + + /** * Write the mate info for two SAMRecords * @param rec1 the first SAM record. Must have a non-null SAMFileHeader. * @param rec2 the second SAM record. Must have a non-null SAMFileHeader. @@ -270,6 +279,7 @@ else if (rec1.getReadUnmappedFlag() && rec2.getReadUnmappedFlag()) { * @param rec2 the second SAM record * @param header the SAM file header * @param setMateCigar true if we are to update/create the Mate CIGAR (MC) optional tag, false if we are to clear any mate cigar tag that is present. + * @deprecated use {@link #setMateInfo(SAMRecord, SAMRecord, boolean)} instead */ @Deprecated public static void setMateInfo(final SAMRecord rec1, final SAMRecord rec2, final SAMFileHeader header, final boolean setMateCigar) { @@ -281,9 +291,11 @@ public static void setMateInfo(final SAMRecord rec1, final SAMRecord rec2, final * @param rec1 the first SAM record * @param rec2 the second SAM record * @param header the SAM file header + * @deprecated use {@link #setMateInfo(SAMRecord, SAMRecord)} instead */ + @Deprecated public static void setMateInfo(final SAMRecord rec1, final SAMRecord rec2, final SAMFileHeader header) { - setMateInfo(rec1, rec2, false); + setMateInfo(rec1, rec2); } /** @@ -322,26 +334,43 @@ public static void setMateInformationOnSupplementalAlignment( final SAMRecord su /** * This method will clear any mate cigar already present. + * @deprecated use {@link #setProperPairAndMateInfo(SAMRecord, SAMRecord, List)} instead */ + @Deprecated public static void setProperPairAndMateInfo(final SAMRecord rec1, final SAMRecord rec2, final SAMFileHeader header, - final List exepectedOrientations) { - setProperPairAndMateInfo(rec1, rec2, header, exepectedOrientations, false); + final List expectedOrientations) { + setProperPairAndMateInfo(rec1, rec2, expectedOrientations); } /** - * @param rec1 - * @param rec2 - * @param header - * @param exepectedOrientations * @param addMateCigar true if we are to update/create the Mate CIGAR (MC) optional tag, false if we are to clear any mate cigar tag that is present. + * @deprecated use {@link #setProperPairAndMateInfo(SAMRecord, SAMRecord, List, boolean)} */ + @Deprecated public static void setProperPairAndMateInfo(final SAMRecord rec1, final SAMRecord rec2, final SAMFileHeader header, - final List exepectedOrientations, + final List expectedOrientations, + final boolean addMateCigar) { + setProperPairAndMateInfo(rec1, rec2, expectedOrientations, addMateCigar); + } + + /** + * This method will clear any mate cigar already present. + */ + public static void setProperPairAndMateInfo(final SAMRecord rec1, final SAMRecord rec2, + final List expectedOrientations) { + setProperPairAndMateInfo(rec1, rec2, expectedOrientations, false); + } + + /** + * @param addMateCigar true if we are to update/create the Mate CIGAR (MC) optional tag, false if we are to clear any mate cigar tag that is present. + */ + public static void setProperPairAndMateInfo(final SAMRecord rec1, final SAMRecord rec2, + final List expectedOrientations, final boolean addMateCigar) { - setMateInfo(rec1, rec2, header, addMateCigar); - setProperPairFlags(rec1, rec2, exepectedOrientations); + setMateInfo(rec1, rec2, addMateCigar); + setProperPairFlags(rec1, rec2, expectedOrientations); } public static void setProperPairFlags(final SAMRecord rec1, final SAMRecord rec2, final List expectedOrientations) { diff --git a/src/main/java/htsjdk/samtools/util/CollectionUtil.java b/src/main/java/htsjdk/samtools/util/CollectionUtil.java index a80319bc5..0354a5b88 100755 --- a/src/main/java/htsjdk/samtools/util/CollectionUtil.java +++ b/src/main/java/htsjdk/samtools/util/CollectionUtil.java @@ -104,8 +104,10 @@ private void initializeKeyIfUninitialized(final K k) { /** * Partitions a collection into groups based on a characteristics of that group. Partitions are embodied in a map, whose keys are the * value of that characteristic, and the values are the partition of elements whose characteristic evaluate to that key. + * + * @deprecated use java8 .stream().collect(Collectors.groupingBy(()-> function)) instead */ - @Deprecated //use java8 .stream().collect(Collectors.groupingBy(()-> function)) instead + @Deprecated public static Map> partition(final Collection collection, final Partitioner p) { final MultiMap partitionToValues = new MultiMap<>(); for (final V entry : collection) { @@ -113,7 +115,11 @@ private void initializeKeyIfUninitialized(final K k) { } return partitionToValues; } - @Deprecated //not needed, use Collectors.groupingBy instead + + /** + * @deprecated use Collectors.groupingBy instead + */ + @Deprecated public static abstract class Partitioner { public abstract K getPartition(final V v); } diff --git a/src/main/java/htsjdk/samtools/util/IOUtil.java b/src/main/java/htsjdk/samtools/util/IOUtil.java index b87b40978..07ae9006a 100644 --- a/src/main/java/htsjdk/samtools/util/IOUtil.java +++ b/src/main/java/htsjdk/samtools/util/IOUtil.java @@ -71,7 +71,7 @@ */ public class IOUtil { /** - * @deprecated Use Defaults.NON_ZERO_BUFFER_SIZE instead. + * @deprecated Use {@link Defaults#NON_ZERO_BUFFER_SIZE} instead. */ @Deprecated public static final int STANDARD_BUFFER_SIZE = Defaults.NON_ZERO_BUFFER_SIZE; diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index 32b7176f5..76cb5084c 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -102,9 +102,14 @@ public void addall(final Collection intervals) { } } - /** Sorts the internal collection of intervals by coordinate. */ - @Deprecated // Use sorted() instead of sort(). The sort() function modifies the object in-place and - // is therefore difficult to work with. sorted() returns a new IntervalList that is sorted + /** + * Sorts the internal collection of intervals by coordinate. + * + * Note: this function modifies the object in-place and is therefore difficult to work with. + * + * @deprecated use {@link #sorted()} instead. + */ + @Deprecated public void sort() { Collections.sort(this.intervals, new IntervalCoordinateComparator(this.header)); this.header.setSortOrder(SAMFileHeader.SortOrder.coordinate); @@ -154,19 +159,27 @@ public IntervalList uniqued(final boolean concatenateNames) { return value; } - /** Sorts and uniques the list of intervals held within this interval list. */ - @Deprecated//use uniqued() instead. This function modifies the object in-place and - // is therefore difficult to work with. + /** + * Sorts and uniques the list of intervals held within this interval list. + * + * Note: this function modifies the object in-place and is therefore difficult to work with. + * + * @deprecated use {@link #uniqued()} instead. + */ + @Deprecated public void unique() { unique(true); } /** * Sorts and uniques the list of intervals held within this interval list. + * + * Note: this function modifies the object in-place and is therefore difficult to work with. + * * @param concatenateNames If false, interval names are not concatenated when merging intervals to save space. + * @deprecated use {@link #uniqued(boolean)} instead. */ - @Deprecated//use uniqued() instead. This function modifies the object in-place and - // is therefore difficult to work with + @Deprecated public void unique(final boolean concatenateNames) { sort(); final List tmp = getUniqueIntervals(concatenateNames); @@ -186,10 +199,12 @@ public void unique(final boolean concatenateNames) { * * Note: has the side-effect of sorting the stored intervals in coordinate order if not already sorted. * + * Note: this function modifies the object in-place and is therefore difficult to work with. + * * @return the set of unique intervals condensed from the contained intervals + * @deprecated use {@link #uniqued()#getIntervals()} instead. */ - @Deprecated//use uniqued().getIntervals() instead. This function modifies the object in-place and - // is therefore difficult to work with + @Deprecated public List getUniqueIntervals() { return getUniqueIntervals(true); } @@ -249,14 +264,14 @@ else if (current.intersects(next) || current.abuts(next)) { } /** - * Merges list of intervals and reduces them like htsjdk.samtools.util.IntervalList#getUniqueIntervals() - * @param concatenateNames If false, the merged interval has the name of the earlier interval. This keeps name shorter. - */ - @Deprecated //use uniqued(concatenateNames).getIntervals() or the static version instead to avoid changing the underlying object. - /** - * Merges list of intervals and reduces them like htsjdk.samtools.util.IntervalList#getUniqueIntervals() - * @param concatenateNames If false, the merged interval has the name of the earlier interval. This keeps name shorter. + * Merges list of intervals and reduces them like {@link #getUniqueIntervals()}. + * + * Note: this function modifies the object in-place and is therefore difficult to work with. + * + * @param concatenateNames If false, the merged interval has the name of the earlier interval. This keeps name shorter. + * @deprecated use {@link #uniqued(boolean)#getIntervals()} or {@link #getUniqueIntervals(IntervalList, boolean)} instead. */ + @Deprecated public List getUniqueIntervals(final boolean concatenateNames) { if (getHeader().getSortOrder() != SAMFileHeader.SortOrder.coordinate) { sort(); diff --git a/src/main/java/htsjdk/tribble/Feature.java b/src/main/java/htsjdk/tribble/Feature.java index 791986ded..941790f34 100644 --- a/src/main/java/htsjdk/tribble/Feature.java +++ b/src/main/java/htsjdk/tribble/Feature.java @@ -36,6 +36,8 @@ * @deprecated use getContig() instead */ @Deprecated - public String getChr(); + default public String getChr() { + return getContig(); + } } diff --git a/src/main/java/htsjdk/tribble/SimpleFeature.java b/src/main/java/htsjdk/tribble/SimpleFeature.java index c85cdcc24..ddc62fa10 100644 --- a/src/main/java/htsjdk/tribble/SimpleFeature.java +++ b/src/main/java/htsjdk/tribble/SimpleFeature.java @@ -39,11 +39,6 @@ public SimpleFeature(final String contig, final int start, final int end) { this.end = end; } - @Deprecated - public String getChr() { - return contig; - } - public String getContig() { return contig; } diff --git a/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java b/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java index 415b628c2..77a030fa9 100644 --- a/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java +++ b/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java @@ -51,14 +51,6 @@ public SimpleBEDFeature(int start, int end, String chr) { this.chr = chr; } - - @Deprecated - @Override - public String getChr() { - return getContig(); - } - - @Override public String getContig() { return chr; diff --git a/src/main/java/htsjdk/tribble/example/CountRecords.java b/src/main/java/htsjdk/tribble/example/CountRecords.java index 94d970776..230c1bf3d 100644 --- a/src/main/java/htsjdk/tribble/example/CountRecords.java +++ b/src/main/java/htsjdk/tribble/example/CountRecords.java @@ -193,12 +193,8 @@ public static FeatureCodec getFeatureCodec(File featureFile) { // return new VCFCodec(); if (featureFile.getName().endsWith(".bed") || featureFile.getName().endsWith(".BED") ) return new BEDCodec(); - //if (featureFile.getName().endsWith(".snp") || featureFile.getName().endsWith(".rod") ) - // return new OldDbSNPCodec(); if (featureFile.getName().endsWith(".geli.calls") || featureFile.getName().endsWith(".geli") ) return new GeliTextCodec(); - //if (featureFile.getName().endsWith(".txt") || featureFile.getName().endsWith(".TXT") ) - // return new SoapSNPCodec(); throw new IllegalArgumentException("Unable to determine correct file type based on the file name, for file -> " + featureFile); } } diff --git a/src/main/java/htsjdk/tribble/example/ExampleBinaryCodec.java b/src/main/java/htsjdk/tribble/example/ExampleBinaryCodec.java index 219d13aca..9628cc4fe 100644 --- a/src/main/java/htsjdk/tribble/example/ExampleBinaryCodec.java +++ b/src/main/java/htsjdk/tribble/example/ExampleBinaryCodec.java @@ -118,7 +118,7 @@ public boolean canDecode(final String path) { Iterator it = reader.iterator(); while ( it.hasNext() ) { final Feature f = it.next(); - dos.writeUTF(f.getChr()); + dos.writeUTF(f.getContig()); dos.writeInt(f.getStart()); dos.writeInt(f.getEnd()); } diff --git a/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java b/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java index 41d38778c..baad1caab 100644 --- a/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java +++ b/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java @@ -83,12 +83,6 @@ public GeliTextFeature(String contig, this.likelihoods = likelihoods; } - /** Return the features reference sequence name, e.g chromosome or contig */ - @Deprecated - public String getChr() { - return getContig(); - } - @Override public String getContig() { return this.contig; diff --git a/src/main/java/htsjdk/tribble/index/IndexFactory.java b/src/main/java/htsjdk/tribble/index/IndexFactory.java index 3cd1b7958..4e23e934d 100644 --- a/src/main/java/htsjdk/tribble/index/IndexFactory.java +++ b/src/main/java/htsjdk/tribble/index/IndexFactory.java @@ -345,8 +345,8 @@ private static Index createIndex(final File inputFile, final FeatureIterator ite checkSorted(inputFile, lastFeature, currentFeature); //should only visit chromosomes once - final String curChr = currentFeature.getChr(); - final String lastChr = lastFeature != null ? lastFeature.getChr() : null; + final String curChr = currentFeature.getContig(); + final String lastChr = lastFeature != null ? lastFeature.getContig() : null; if(!curChr.equals(lastChr)){ if(visitedChromos.containsKey(curChr)){ String msg = "Input file must have contiguous chromosomes."; @@ -369,15 +369,15 @@ private static Index createIndex(final File inputFile, final FeatureIterator ite } private static String featToString(final Feature feature){ - return feature.getChr() + ":" + feature.getStart() + "-" + feature.getEnd(); + return feature.getContig() + ":" + feature.getStart() + "-" + feature.getEnd(); } private static void checkSorted(final File inputFile, final Feature lastFeature, final Feature currentFeature){ // if the last currentFeature is after the current currentFeature, exception out - if (lastFeature != null && currentFeature.getStart() < lastFeature.getStart() && lastFeature.getChr().equals(currentFeature.getChr())) + if (lastFeature != null && currentFeature.getStart() < lastFeature.getStart() && lastFeature.getContig().equals(currentFeature.getContig())) throw new TribbleException.MalformedFeatureFile("Input file is not sorted by start position. \n" + - "We saw a record with a start of " + currentFeature.getChr() + ":" + currentFeature.getStart() + - " after a record with a start of " + lastFeature.getChr() + ":" + lastFeature.getStart(), inputFile.getAbsolutePath()); + "We saw a record with a start of " + currentFeature.getContig() + ":" + currentFeature.getStart() + + " after a record with a start of " + lastFeature.getContig() + ":" + lastFeature.getStart(), inputFile.getAbsolutePath()); } diff --git a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java index 854b05dff..e826edaa7 100644 --- a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java @@ -64,13 +64,13 @@ public IntervalIndexCreator(final File inputFile) { public void addFeature(final Feature feature, final long filePosition) { // if we don't have a chrIndex yet, or if the last one was for the previous contig, create a new one - if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getChr())) { + if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getContig())) { // if we're creating a new chrIndex (not the first), make sure to dump the intervals to the old chrIndex if (!chrList.isEmpty()) addIntervalsToLastChr(filePosition); // create a new chr index for the current contig - chrList.add(new ChrIndex(feature.getChr())); + chrList.add(new ChrIndex(feature.getContig())); intervals.clear(); } diff --git a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java index 9e680721c..1158fdfd3 100644 --- a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java @@ -66,14 +66,14 @@ public LinearIndexCreator(final File inputFile) { */ public void addFeature(final Feature feature, final long filePosition) { // fi we don't have a chrIndex yet, or if the last one was for the previous contig, create a new one - if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getChr())) { + if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getContig())) { // if we're creating a new chrIndex (not the first), make sure to dump the blocks to the old chrIndex if (!chrList.isEmpty()) for (int x = 0; x < blocks.size(); x++) { blocks.get(x).setEndPosition((x + 1 == blocks.size()) ? filePosition : blocks.get(x + 1).getStartPosition()); chrList.getLast().addBlock(blocks.get(x)); } - chrList.add(new LinearIndex.ChrIndex(feature.getChr(),binWidth)); + chrList.add(new LinearIndex.ChrIndex(feature.getContig(),binWidth)); blocks.clear(); // Add the first block diff --git a/src/main/java/htsjdk/tribble/index/tabix/TabixIndexCreator.java b/src/main/java/htsjdk/tribble/index/tabix/TabixIndexCreator.java index 9f502cbdf..001dabc77 100644 --- a/src/main/java/htsjdk/tribble/index/tabix/TabixIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/tabix/TabixIndexCreator.java @@ -73,7 +73,7 @@ public TabixIndexCreator(final TabixFormat formatSpec) { @Override public void addFeature(final Feature feature, final long filePosition) { - final String sequenceName = feature.getChr(); + final String sequenceName = feature.getContig(); final int referenceIndex; if (sequenceName.equals(currentSequenceName)) { referenceIndex = sequenceNames.size() - 1; diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 8bf37bbf8..a6b8a5fce 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -331,7 +331,7 @@ * @param other the VariantContext to copy */ protected VariantContext(VariantContext other) { - this(other.getSource(), other.getID(), other.getChr(), other.getStart(), other.getEnd(), + this(other.getSource(), other.getID(), other.getContig(), other.getStart(), other.getEnd(), other.getAlleles(), other.getGenotypes(), other.getLog10PError(), other.getFiltersMaybeNull(), other.getAttributes(), @@ -1644,10 +1644,6 @@ private final Genotype fullyDecodeGenotypes(final Genotype g, final VCFHeader he // tribble integration routines -- not for public consumption // // --------------------------------------------------------------------------------------------------------- - @Deprecated - public String getChr() { - return getContig(); - } @Override public String getContig() { diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContextComparator.java b/src/main/java/htsjdk/variant/variantcontext/VariantContextComparator.java index 575434974..d4e288f97 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContextComparator.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContextComparator.java @@ -84,7 +84,7 @@ public int compare(final VariantContext firstVariantContext, final VariantContex // present. This error checking should already have been done in the constructor but it's left // in as defence anyway. final int contigCompare = - this.contigIndexLookup.get(firstVariantContext.getChr()) - this.contigIndexLookup.get(secondVariantContext.getChr()); + this.contigIndexLookup.get(firstVariantContext.getContig()) - this.contigIndexLookup.get(secondVariantContext.getContig()); return contigCompare != 0 ? contigCompare : firstVariantContext.getStart() - secondVariantContext.getStart(); diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java index 493499e30..34cde3395 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java @@ -59,7 +59,7 @@ static { attributes.put("vc", (VariantContext vc) -> vc); - attributes.put("CHROM", VariantContext::getChr); + attributes.put("CHROM", VariantContext::getContig); attributes.put("POS", VariantContext::getStart); attributes.put("TYPE", (VariantContext vc) -> vc.getType().toString()); attributes.put("QUAL", (VariantContext vc) -> -10 * vc.getLog10PError()); diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java index 0776e4f75..f9dd458d0 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java @@ -255,7 +255,7 @@ public void start(final BCF2Encoder encoder, final VariantContext vc) throws IOE if ( vc.getNAlleles() > BCF2Utils.MAX_ALLELES_IN_GENOTYPES ) throw new IllegalStateException("Current BCF2 encoder cannot handle sites " + "with > " + BCF2Utils.MAX_ALLELES_IN_GENOTYPES + " alleles, but you have " - + vc.getNAlleles() + " at " + vc.getChr() + ":" + vc.getStart()); + + vc.getNAlleles() + " at " + vc.getContig() + ":" + vc.getStart()); encodingType = BCF2Type.INT8; buildAlleleMap(vc); diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2Writer.java b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2Writer.java index 74a629800..8c16aac97 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2Writer.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2Writer.java @@ -234,9 +234,9 @@ public void close() { // // -------------------------------------------------------------------------------- private byte[] buildSitesData( VariantContext vc ) throws IOException { - final int contigIndex = contigDictionary.get(vc.getChr()); + final int contigIndex = contigDictionary.get(vc.getContig()); if ( contigIndex == -1 ) - throw new IllegalStateException(String.format("Contig %s not found in sequence dictionary from reference", vc.getChr())); + throw new IllegalStateException(String.format("Contig %s not found in sequence dictionary from reference", vc.getContig())); // note use of encodeRawValue to not insert the typing byte encoder.encodeRawValue(contigIndex, BCF2Type.INT32); @@ -391,7 +391,7 @@ private void buildInfo( VariantContext vc ) throws IOException { */ private void errorUnexpectedFieldToWrite(final VariantContext vc, final String field, final String fieldType) { throw new IllegalStateException("Found field " + field + " in the " + fieldType + " fields of VariantContext at " + - vc.getChr() + ":" + vc.getStart() + " from " + vc.getSource() + " but this hasn't been defined in the VCFHeader"); + vc.getContig() + ":" + vc.getStart() + " from " + vc.getSource() + " but this hasn't been defined in the VCFHeader"); } // -------------------------------------------------------------------------------- diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java index 11d2f10e0..690a7813c 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java @@ -118,11 +118,11 @@ public synchronized void add(VariantContext vc) { since there is no implicit ordering of chromosomes: */ VCFRecord firstRec = queue.peek(); - if (firstRec != null && !vc.getChr().equals(firstRec.vc.getChr())) { // if we hit a new contig, flush the queue - if (finishedChromosomes.contains(vc.getChr())) - throw new IllegalArgumentException("Added a record at " + vc.getChr() + ":" + vc.getStart() + ", but already finished with chromosome" + vc.getChr()); + if (firstRec != null && !vc.getContig().equals(firstRec.vc.getContig())) { // if we hit a new contig, flush the queue + if (finishedChromosomes.contains(vc.getContig())) + throw new IllegalArgumentException("Added a record at " + vc.getContig() + ":" + vc.getStart() + ", but already finished with chromosome" + vc.getContig()); - finishedChromosomes.add(firstRec.vc.getChr()); + finishedChromosomes.add(firstRec.vc.getContig()); stopWaitingToSort(); } @@ -159,7 +159,7 @@ protected synchronized void emitSafeRecords() { protected void noteCurrentRecord(VariantContext vc) { // did the user break the contract by giving a record too late? if (mostUpstreamWritableLoc != null && vc.getStart() < mostUpstreamWritableLoc) // went too far back, since may have already written anything that is <= mostUpstreamWritableLoc - throw new IllegalArgumentException("Permitted to write any record upstream of position " + mostUpstreamWritableLoc + ", but a record at " + vc.getChr() + ":" + vc.getStart() + " was just added."); + throw new IllegalArgumentException("Permitted to write any record upstream of position " + mostUpstreamWritableLoc + ", but a record at " + vc.getContig() + ":" + vc.getStart() + " was just added."); } // -------------------------------------------------------------------------------- diff --git a/src/main/java/htsjdk/variant/vcf/VCFEncoder.java b/src/main/java/htsjdk/variant/vcf/VCFEncoder.java index f65a03888..a90906684 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFEncoder.java +++ b/src/main/java/htsjdk/variant/vcf/VCFEncoder.java @@ -72,7 +72,7 @@ public String encode(final VariantContext context) { final StringBuilder stringBuilder = new StringBuilder(); // CHROM - stringBuilder.append(context.getChr()).append(VCFConstants.FIELD_SEPARATOR) + stringBuilder.append(context.getContig()).append(VCFConstants.FIELD_SEPARATOR) // POS .append(String.valueOf(context.getStart())).append(VCFConstants.FIELD_SEPARATOR) // ID @@ -170,7 +170,7 @@ private String formatQualValue(final double qual) { private void fieldIsMissingFromHeaderError(final VariantContext vc, final String id, final String field) { if ( ! allowMissingFieldsInHeader) throw new IllegalStateException("Key " + id + " found in VariantContext field " + field - + " at " + vc.getChr() + ":" + vc.getStart() + + " at " + vc.getContig() + ":" + vc.getStart() + " but this key isn't defined in the VCFHeader. We require all VCFs to have" + " complete VCF headers by default."); } diff --git a/src/main/java/htsjdk/variant/vcf/VCFFileReader.java b/src/main/java/htsjdk/variant/vcf/VCFFileReader.java index 18a7b8089..9024f34fc 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFFileReader.java +++ b/src/main/java/htsjdk/variant/vcf/VCFFileReader.java @@ -116,7 +116,7 @@ public static IntervalList fromVcf(final VCFFileReader vcf, final boolean includ final Integer intervalEnd=vc.getCommonInfo().getAttributeAsInt("END",vc.getEnd()); if(".".equals(name) || name == null) name = "interval-" + (++intervals); - list.add(new Interval(vc.getChr(), vc.getStart(), intervalEnd, false, name)); + list.add(new Interval(vc.getContig(), vc.getStart(), intervalEnd, false, name)); } } diff --git a/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java b/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java index a0984d75e..297b892c5 100644 --- a/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java +++ b/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java @@ -18,7 +18,7 @@ @BeforeGroups(groups = "sra") public final void checkIfCanResolve() { - if (!SRAAccession.isSupported()) { + if (SRAAccession.checkIfInitialized() != null) { return; } canResolveNetworkAccession = SRAAccession.isValid(checkAccession); @@ -26,7 +26,7 @@ public final void checkIfCanResolve() { @BeforeMethod public final void assertSRAIsSupported() { - if(!SRAAccession.isSupported()){ + if(SRAAccession.checkIfInitialized() != null){ throw new SkipException("Skipping SRA Test because SRA native code is unavailable."); } } diff --git a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java index 474a8a89b..dbf23a0e5 100644 --- a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java +++ b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java @@ -52,17 +52,17 @@ public void testSimpleDecode() { BEDFeature feature; feature = codec.decode("chr1 1"); - Assert.assertEquals(feature.getChr(), "chr1"); + Assert.assertEquals(feature.getContig(), "chr1"); Assert.assertEquals(feature.getStart(), 2); Assert.assertEquals(feature.getEnd(), 2); feature = codec.decode("chr1 1 2"); - Assert.assertEquals(feature.getChr(), "chr1"); + Assert.assertEquals(feature.getContig(), "chr1"); Assert.assertEquals(feature.getStart(), 2); Assert.assertEquals(feature.getEnd(), 2); feature = codec.decode("chr1 1 3"); - Assert.assertEquals(feature.getChr(), "chr1"); + Assert.assertEquals(feature.getContig(), "chr1"); Assert.assertEquals(feature.getStart(), 2); Assert.assertEquals(feature.getEnd(), 3); } @@ -77,7 +77,7 @@ public void testFullDecode() { // Borrowed samples from Example: on http://genome.ucsc.edu/FAQ/FAQformat#format1 feature = (FullBEDFeature) codec.decode("chr22 1000 5000 cloneA 960 + 1000 5000 0 2 567,488, 0,3512"); - Assert.assertEquals(feature.getChr(), "chr22"); + Assert.assertEquals(feature.getContig(), "chr22"); Assert.assertEquals(feature.getStart(), 1001); Assert.assertEquals(feature.getEnd(), 5000); Assert.assertEquals(feature.getName(), "cloneA"); @@ -103,7 +103,7 @@ public void testFullDecode() { Assert.assertEquals(exons.get(1).getCodingLength(), 488); feature = (FullBEDFeature) codec.decode("chr22 2000 6000 cloneB 900 - 2000 6000 0 2 433,399, 0,3601"); - Assert.assertEquals(feature.getChr(), "chr22"); + Assert.assertEquals(feature.getContig(), "chr22"); Assert.assertEquals(feature.getStart(), 2001); Assert.assertEquals(feature.getEnd(), 6000); Assert.assertEquals(feature.getName(), "cloneB"); @@ -150,23 +150,23 @@ public void testDecodeBEDFile_good() throws Exception { Iterable iter = reader.iterator(); int count = 0; for (Feature feat : iter) { - Assert.assertTrue(feat.getChr().length() > 0); + Assert.assertTrue(feat.getContig().length() > 0); Assert.assertTrue(feat.getEnd() >= feat.getStart()); if (count == 0) { - Assert.assertEquals("1", feat.getChr()); + Assert.assertEquals("1", feat.getContig()); Assert.assertEquals(25592413 + 1, feat.getStart()); Assert.assertEquals(25657872, feat.getEnd()); } if (count == 3) { - Assert.assertEquals("1", feat.getChr()); + Assert.assertEquals("1", feat.getContig()); Assert.assertEquals(152555536 + 1, feat.getStart()); Assert.assertEquals(152587611, feat.getEnd()); } if (count == 28) { - Assert.assertEquals("14", feat.getChr()); + Assert.assertEquals("14", feat.getContig()); Assert.assertEquals(73996607 + 1, feat.getStart()); Assert.assertEquals(74025282, feat.getEnd()); } diff --git a/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java b/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java index 681cc6bab..c670bf182 100644 --- a/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java +++ b/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java @@ -77,7 +77,7 @@ public void testFirstRecord() { GeliTextFeature feat = iter.next(); // check the first records contents // 22 14438070 A 0 0 GG 33.2618 33.2618 0 0 0 0 0 0 0 33.2618 0 0 - Assert.assertTrue("22".equals(feat.getChr())); + Assert.assertTrue("22".equals(feat.getContig())); Assert.assertEquals(feat.getStart(), 14438070); Assert.assertEquals('A', feat.getRefBase()); Assert.assertEquals(feat.getDepthOfCoverage(), 0.0, 0.0001); diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java index 868aacc8a..613dec57a 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java @@ -831,7 +831,7 @@ public static void assertEquals(final Iterable actual, final Ite */ public static void assertEquals( final VariantContext actual, final VariantContext expected ) { Assert.assertNotNull(actual, "VariantContext expected not null"); - Assert.assertEquals(actual.getChr(), expected.getChr(), "chr"); + Assert.assertEquals(actual.getContig(), expected.getContig(), "chr"); Assert.assertEquals(actual.getStart(), expected.getStart(), "start"); Assert.assertEquals(actual.getEnd(), expected.getEnd(), "end"); Assert.assertEquals(actual.getID(), expected.getID(), "id"); diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 61b917cdc..2dc345fed 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -188,7 +188,7 @@ public void testCreatingSNPVariantContext() { List alleles = Arrays.asList(Aref, T); VariantContext vc = snpBuilder.alleles(alleles).make(); - Assert.assertEquals(vc.getChr(), snpLoc); + Assert.assertEquals(vc.getContig(), snpLoc); Assert.assertEquals(vc.getStart(), snpLocStart); Assert.assertEquals(vc.getEnd(), snpLocStop); Assert.assertEquals(vc.getType(), VariantContext.Type.SNP); @@ -216,7 +216,7 @@ public void testCreatingRefVariantContext() { List alleles = Arrays.asList(Aref); VariantContext vc = snpBuilder.alleles(alleles).make(); - Assert.assertEquals(vc.getChr(), snpLoc); + Assert.assertEquals(vc.getContig(), snpLoc); Assert.assertEquals(vc.getStart(), snpLocStart); Assert.assertEquals(vc.getEnd(), snpLocStop); Assert.assertEquals(VariantContext.Type.NO_VARIATION, vc.getType()); @@ -243,7 +243,7 @@ public void testCreatingDeletionVariantContext() { List alleles = Arrays.asList(ATCref, del); VariantContext vc = new VariantContextBuilder("test", delLoc, delLocStart, delLocStop, alleles).make(); - Assert.assertEquals(vc.getChr(), delLoc); + Assert.assertEquals(vc.getContig(), delLoc); Assert.assertEquals(vc.getStart(), delLocStart); Assert.assertEquals(vc.getEnd(), delLocStop); Assert.assertEquals(vc.getType(), VariantContext.Type.INDEL); @@ -271,7 +271,7 @@ public void testCreatingComplexSubstitutionVariantContext() { List alleles = Arrays.asList(Tref, ATC); VariantContext vc = new VariantContextBuilder("test", insLoc, insLocStart, insLocStop, alleles).make(); - Assert.assertEquals(vc.getChr(), insLoc); + Assert.assertEquals(vc.getContig(), insLoc); Assert.assertEquals(vc.getStart(), insLocStart); Assert.assertEquals(vc.getEnd(), insLocStop); Assert.assertEquals(vc.getType(), VariantContext.Type.INDEL); @@ -309,7 +309,7 @@ public void testCreatingInsertionVariantContext() { List alleles = Arrays.asList(delRef, ATC); VariantContext vc = insBuilder.alleles(alleles).make(); - Assert.assertEquals(vc.getChr(), insLoc); + Assert.assertEquals(vc.getContig(), insLoc); Assert.assertEquals(vc.getStart(), insLocStart); Assert.assertEquals(vc.getEnd(), insLocStop); Assert.assertEquals(vc.getType(), VariantContext.Type.INDEL); @@ -763,7 +763,7 @@ public String toString() { @Test(dataProvider = "SitesAndGenotypesVC") public void runModifyVCTests(SitesAndGenotypesVC cfg) { VariantContext modified = new VariantContextBuilder(cfg.vc).loc("chr2", 123, 123).make(); - Assert.assertEquals(modified.getChr(), "chr2"); + Assert.assertEquals(modified.getContig(), "chr2"); Assert.assertEquals(modified.getStart(), 123); Assert.assertEquals(modified.getEnd(), 123); @@ -783,7 +783,7 @@ public void runModifyVCTests(SitesAndGenotypesVC cfg) { Assert.assertEquals(modified.getAttribute("AC"), 1); // test the behavior when the builder's attribute object is not initialized - modified = new VariantContextBuilder(modified.getSource(), modified.getChr(), modified.getStart(), modified.getEnd(), modified.getAlleles()).attribute("AC", 1).make(); + modified = new VariantContextBuilder(modified.getSource(), modified.getContig(), modified.getStart(), modified.getEnd(), modified.getAlleles()).attribute("AC", 1).make(); // test normal attribute modification modified = new VariantContextBuilder(cfg.vc).attribute("AC", 1).make(); @@ -801,7 +801,7 @@ public void runModifyVCTests(SitesAndGenotypesVC cfg) { Assert.assertTrue(modified.getGenotypes().isEmpty()); // test that original hasn't changed - Assert.assertEquals(cfg.vc.getChr(), cfg.copy.getChr()); + Assert.assertEquals(cfg.vc.getContig(), cfg.copy.getContig()); Assert.assertEquals(cfg.vc.getStart(), cfg.copy.getStart()); Assert.assertEquals(cfg.vc.getEnd(), cfg.copy.getEnd()); Assert.assertEquals(cfg.vc.getAlleles(), cfg.copy.getAlleles()); @@ -863,7 +863,7 @@ public void runSubContextTest(SubContextTest cfg) { VariantContext sub = vc.subContextFromSamples(cfg.samples, cfg.updateAlleles); // unchanged attributes should be the same - Assert.assertEquals(sub.getChr(), vc.getChr()); + Assert.assertEquals(sub.getContig(), vc.getContig()); Assert.assertEquals(sub.getStart(), vc.getStart()); Assert.assertEquals(sub.getEnd(), vc.getEnd()); Assert.assertEquals(sub.getLog10PError(), vc.getLog10PError()); From b46ea315b8875a50a4aa6eeaf8710dc26f988ca0 Mon Sep 17 00:00:00 2001 From: Zaal Lyanov Date: Mon, 5 Dec 2016 20:00:25 +0300 Subject: [PATCH 048/137] new algorithm for locus iteration and Picard's CollectWgsMetrics (#637) * new algorithm for locus iteration and Picard's CollectWgsMetrics class SamLocusIterator was split into AbstractLocusIterator(common interface) and SamLocusIterator * implementation of a new algorithm - classes ReadEndsIterator and TypedRecordAndOffset added * AbstractLocusInfo class extracted from SamLocusIterator.LocusInfo * AbstractRecordAndOffset class extracted from SamLocusIterator.RecordAndOffset * Unit tests for all new classes * Tests for SamLocusIterator and ReadEndsIterator united by a common super class AbstractLocusIteratorTestTemplate --- .../htsjdk/samtools/util/AbstractLocusInfo.java | 146 +++++ .../samtools/util/AbstractLocusIterator.java | 572 +++++++++++++++++++ .../samtools/util/AbstractRecordAndOffset.java | 130 +++++ .../htsjdk/samtools/util/EdgeReadIterator.java | 228 ++++++++ .../samtools/util/EdgingRecordAndOffset.java | 219 ++++++++ .../htsjdk/samtools/util/SamLocusIterator.java | 613 ++++----------------- .../samtools/util/AbstractLocusInfoTest.java | 79 +++ .../util/AbstractLocusIteratorTestTemplate.java | 68 +++ .../samtools/util/AbstractRecordAndOffsetTest.java | 63 +++ .../htsjdk/samtools/util/EdgeReadIteratorTest.java | 402 ++++++++++++++ .../samtools/util/EdgingRecordAndOffsetTest.java | 94 ++++ .../htsjdk/samtools/util/SamLocusIteratorTest.java | 35 +- 12 files changed, 2120 insertions(+), 529 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java create mode 100644 src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java create mode 100644 src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java create mode 100644 src/main/java/htsjdk/samtools/util/EdgeReadIterator.java create mode 100644 src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java create mode 100644 src/test/java/htsjdk/samtools/util/AbstractLocusInfoTest.java create mode 100644 src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java create mode 100644 src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java create mode 100644 src/test/java/htsjdk/samtools/util/EdgeReadIteratorTest.java create mode 100644 src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java diff --git a/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java b/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java new file mode 100644 index 000000000..4e020071d --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java @@ -0,0 +1,146 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMSequenceRecord; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * The unit of iteration for AbstractLocusIterator. + * Holds information about the locus (the SAMSequenceRecord and 1-based position on the reference), + * plus list of AbstractRecordAndOffset objects, + * If RecordAndOffset class is used, one object represents one aligned read that overlaps the locus. + * If TypedRecordAndOffset class is used, one object represents one aligned read, + * that starts or ends at the locus. + * + * @author Darina_Nikolaeva@epam.com, EPAM Systems, Inc. + * + */ +public class AbstractLocusInfo implements Locus { + /** + * Reference sequence, to which the reads are aligned. + **/ + private final SAMSequenceRecord referenceSequence; + /** + * Position in the sequence, to which the reads are aligned. + **/ + private final int position; + + /** + * Initial size for the list of AbstractRecordAndOffset objects + **/ + private final static int INITIAL_LIST_SIZE = 100; + + /** + * List of aligned to current position reads + **/ + private final List recordAndOffsets = new ArrayList<>(INITIAL_LIST_SIZE); + + /** + * @param referenceSequence reference sequence to which the reads are aligned + * @param position position in the sequence to which the reads are aligned + */ + public AbstractLocusInfo(final SAMSequenceRecord referenceSequence, final int position) { + this.referenceSequence = referenceSequence; + this.position = position; + } + + /** + * Accumulates info for one read aligned to the locus. Method doesn't check, that recordAndOffset + * is really aligned to current reference position, so it must have valid reference sequence and + * position or further processing can go wrong. + * + * @param recordAndOffset object to add to current locus + */ + public void add(E recordAndOffset) { + recordAndOffsets.add(recordAndOffset); + } + + /** + * @return the index of reference sequence + */ + public int getSequenceIndex() { + return referenceSequence.getSequenceIndex(); + } + + /** + * @return 1-based reference position + */ + public int getPosition() { + return position; + } + + /** + * @deprecated since name of the method can be confusing, new implementation should be used + * {@code getRecordAndOffsets()} + * @return unmodifiable list of aligned to the reference position recordsAndOffsets + */ + @Deprecated + public List getRecordAndPositions() { + return Collections.unmodifiableList(recordAndOffsets); + } + + /** + * @return unmodifiable list of aligned to the reference position recordsAndOffsets + */ + public List getRecordAndOffsets() { + return Collections.unmodifiableList(recordAndOffsets); + } + + /** + * @return the name of reference sequence + */ + public String getSequenceName() { + return referenceSequence.getSequenceName(); + } + + @Override + public String toString() { + return referenceSequence.getSequenceName() + ":" + position; + } + + /** + * @return the length of reference sequence + */ + public int getSequenceLength() { + return referenceSequence.getSequenceLength(); + } + + /** + * @return the number of records overlapping the position + */ + public int size() { + return this.recordAndOffsets.size(); + } + + /** + * @return true if RecordAndOffset list is empty; + */ + public boolean isEmpty() { + return getRecordAndOffsets().isEmpty(); + } +} diff --git a/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java b/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java new file mode 100644 index 000000000..6ff8e835c --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java @@ -0,0 +1,572 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + + +import htsjdk.samtools.Cigar; +import htsjdk.samtools.CigarElement; +import htsjdk.samtools.CigarOperator; +import htsjdk.samtools.SAMException; +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.SamReader; +import htsjdk.samtools.filter.AggregateFilter; +import htsjdk.samtools.filter.DuplicateReadFilter; +import htsjdk.samtools.filter.FilteringSamIterator; +import htsjdk.samtools.filter.SamRecordFilter; +import htsjdk.samtools.filter.SecondaryOrSupplementaryFilter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * Iterator that traverses a SAM File, accumulating information on a per-locus basis. + * Optionally takes a target interval list, in which case the loci returned are the ones covered by + * the interval list. If no target interval list, whatever loci are covered by the input reads are returned. + * By default duplicate reads and non-primary alignments are filtered out. Filtering may be changed + * via setSamFilters(). + * + * @author Darina_Nikolaeva@epam.com, EPAM Systems, Inc. + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + */ + +public abstract class AbstractLocusIterator> implements Iterable, CloseableIterator { + + static final Log LOG = Log.getInstance(AbstractLocusIterator.class); + + private final SamReader samReader; + final private ReferenceSequenceMask referenceSequenceMask; + private PeekableIterator samIterator; + private List samFilters = Arrays.asList(new SecondaryOrSupplementaryFilter(), + new DuplicateReadFilter()); + final List intervals; + + /** + * If true, do indexed lookup to improve performance. Not relevant if intervalList == null. + * It is no longer the case the useIndex==true can make performance worse. It should always perform at least + * as well as useIndex==false, and generally will be much faster. + */ + private final boolean useIndex; + + /** + * LocusInfos on this list are ready to be returned by iterator. All reads that overlap + * the locus have been accumulated before the AbstractLocusInfo is moved into this list. + */ + private final ArrayList complete = new ArrayList<>(100); + + /** + * LocusInfos for which accumulation is in progress. When {@link #accumulateSamRecord(SAMRecord)} is called + * the state of this list is guaranteed to be either: + * a) Empty, or + * b) That the element at index 0 corresponds to the same genomic locus as the first aligned base + * in the read being accumulated + *

+ * Before each new read is accumulated the accumulator is examined and: + * i) any LocusInfos at positions earlier than the read start are moved to {@link #complete} + * ii) any uncovered positions between the last AbstractLocusInfo and the first aligned base of the new read + * have LocusInfos created and added to {@link #complete} if we are emitting uncovered loci + */ + final ArrayList accumulator = new ArrayList<>(100); + + private int qualityScoreCutoff = Integer.MIN_VALUE; + private int mappingQualityScoreCutoff = Integer.MIN_VALUE; + private boolean includeNonPfReads = true; + + /** + * If true, emit a AbstractLocusInfo for every locus in the target map, or if no target map, + * emit a AbstractLocusInfo for every locus in the reference sequence. + * If false, emit a AbstractLocusInfo only if a locus has coverage. + */ + private boolean emitUncoveredLoci = true; + + /** + * If set, this will cap the number of reads we accumulate for any given position. + * Note that if we hit the maximum threshold at the first position in the accumulation queue, + * then we throw further reads overlapping that position completely away (including for subsequent positions). + * This is a useful feature if one wants to minimize the memory footprint in files with a few massively large pileups, + * but it must be pointed out that it could cause major bias because of the non-random nature with which the cap is + * applied (the first maxReadsToAccumulatePerLocus reads are kept and all subsequent ones are dropped). + */ + private int maxReadsToAccumulatePerLocus = Integer.MAX_VALUE; + + /** + * Set to true when we have enforced the accumulation limit for the first time + */ + private boolean enforcedAccumulationLimit = false; + + /** + * If true, include indels in the LocusInfo + */ + protected boolean includeIndels = false; + + /** + * When there is a target mask, these members remember the last locus for which a AbstractLocusInfo has been + * returned, so that any uncovered locus in the target mask can be covered by a 0-coverage AbstractLocusInfo + */ + private int lastReferenceSequence = 0; + + /** + * Last processed locus position in the reference + */ + private int lastPosition = 0; + + /** + * Set to true when past all aligned reads in input SAM file + */ + private boolean finishedAlignedReads = false; + + private final LocusComparator locusComparator = new LocusComparator<>(); + + /** + * Last processed interval, relevant only if list of intervals is defined. + */ + private int lastInterval = 0; + + /** + * Prepare to iterate through the given SAM records, skipping non-primary alignments + * + * @param samReader must be coordinate sorted + * @param intervalList Either the list of desired intervals, or null. Note that if an intervalList is + * passed in that is not coordinate sorted, it will eventually be coordinated sorted by this class. + * @param useIndex If true, do indexed lookup to improve performance. Not relevant if intervalList == null. + * It is no longer the case the useIndex==true can make performance worse. It should always perform at least + * as well as useIndex==false, and generally will be much faster. + */ + + public AbstractLocusIterator(final SamReader samReader, final IntervalList intervalList, final boolean useIndex) { + final String className = this.getClass().getSimpleName(); + if (samReader.getFileHeader().getSortOrder() == null || samReader.getFileHeader().getSortOrder() == SAMFileHeader.SortOrder.unsorted) { + + LOG.warn(className + " constructed with samReader that has SortOrder == unsorted. ", "" + + "Assuming SAM is coordinate sorted, but exceptions may occur if it is not."); + } else if (samReader.getFileHeader().getSortOrder() != SAMFileHeader.SortOrder.coordinate) { + throw new SAMException(className + " cannot operate on a SAM file that is not coordinate sorted."); + } + this.samReader = samReader; + this.useIndex = useIndex; + if (intervalList != null) { + intervals = intervalList.uniqued().getIntervals(); + this.referenceSequenceMask = new IntervalListReferenceSequenceMask(intervalList); + } else { + intervals = null; + this.referenceSequenceMask = new WholeGenomeReferenceSequenceMask(samReader.getFileHeader()); + } + } + + /** + * @return iterator over all/all covered locus position in reference according to emitUncoveredLoci + * value. + */ + public Iterator iterator() { + if (samIterator != null) { + throw new IllegalStateException("Cannot call iterator() more than once on " + this.getClass().getSimpleName()); + } + CloseableIterator tempIterator; + if (intervals != null) { + tempIterator = new SamRecordIntervalIteratorFactory().makeSamRecordIntervalIterator(samReader, intervals, useIndex); + } else { + tempIterator = samReader.iterator(); + } + if (samFilters != null) { + tempIterator = new FilteringSamIterator(tempIterator, new AggregateFilter(samFilters)); + } + samIterator = new PeekableIterator<>(tempIterator); + return this; + } + + /** + * Closes inner SamIterator. + */ + public void close() { + this.samIterator.close(); + } + + private boolean samHasMore() { + return !finishedAlignedReads && (samIterator.peek() != null); + } + + /** + * Returns true if there are more AbstractLocusInfo objects that can be returned, due to any of the following reasons: + * 1) there are more aligned reads in the SAM file + * 2) there are AbstractLocusInfos in some stage of accumulation + * 3) there are loci in the target mask that have yet to be accumulated (even if there are no reads covering them) + */ + public boolean hasNext() { + if (this.samIterator == null) { + iterator(); + } + + while (complete.isEmpty() && ((!accumulator.isEmpty()) || samHasMore() || hasRemainingMaskBases())) { + final K locusInfo = next(); + if (locusInfo != null) { + complete.add(0, locusInfo); + } + } + return !complete.isEmpty(); + } + + /** + * @return true if there are more bases at which the locus iterator must emit AbstractLocusInfos because + * there are loci beyond the last emitted loci which are in the set of loci to be emitted and + * the iterator is setup to emit uncovered loci - so we can guarantee we'll emit those loci. + */ + private boolean hasRemainingMaskBases() { + // if there are more sequences in the mask, by definition some of them must have + // marked bases otherwise if we're in the last sequence, but we're not at the last marked position, + // there is also more in the mask + if (!emitUncoveredLoci) { + // If not emitting uncovered loci, this check is irrelevant + return false; + } + return (lastReferenceSequence < referenceSequenceMask.getMaxSequenceIndex() || + (lastReferenceSequence == referenceSequenceMask.getMaxSequenceIndex() && + lastPosition < referenceSequenceMask.nextPosition(lastReferenceSequence, lastPosition))); + } + + /** + * hasNext() has been fixed so that if it returns true, next() is now guaranteed not to return null. + * + * @return information about next locus position in reference sequence + */ + public K next() { + // if we don't have any completed entries to return, try and make some! + while (complete.isEmpty() && samHasMore()) { + final SAMRecord rec = samIterator.peek(); + + // There might be unmapped reads mixed in with the mapped ones, but when a read + // is encountered with no reference index it means that all the mapped reads have been seen. + if (rec.getReferenceIndex() == -1) { + this.finishedAlignedReads = true; + continue; + + } + // Skip over an unaligned read that has been forced to be sorted with the aligned reads + if (rec.getReadUnmappedFlag() + || rec.getMappingQuality() < this.mappingQualityScoreCutoff + || (!this.includeNonPfReads && rec.getReadFailsVendorQualityCheckFlag())) { + samIterator.next(); + continue; + } + + int start = rec.getAlignmentStart(); + // only if we are including indels and the record does not start in the first base of the reference + // the stop locus to populate the queue is not the same if the record starts with an insertion + if (includeIndels && start != 1 && startWithInsertion(rec.getCigar())) { + // the start to populate is one less + start--; + } + final Locus alignmentStart = new LocusImpl(rec.getReferenceIndex(), start); + // emit everything that is before the start of the current read, because we know no more + // coverage will be accumulated for those loci. + while (!accumulator.isEmpty() && locusComparator.compare(accumulator.get(0), alignmentStart) < 0) { + final K first = accumulator.get(0); + populateCompleteQueue(alignmentStart); + if (!complete.isEmpty()) { + return complete.remove(0); + } + if (!accumulator.isEmpty() && first == accumulator.get(0)) { + throw new SAMException("Stuck in infinite loop"); + } + } + + // at this point, either the accumulator list is empty or the head should + // be the same position as the first base of the read (or insertion if first) + if (!accumulator.isEmpty()) { + if (accumulator.get(0).getSequenceIndex() != rec.getReferenceIndex() || + accumulator.get(0).getPosition() != start) { + throw new IllegalStateException("accumulator should be empty or aligned with current SAMRecord"); + } + } + + // Store the loci for the read in the accumulator + if (!surpassedAccumulationThreshold()) { + accumulateSamRecord(rec); + // Store the indels if requested + if (includeIndels) { + accumulateIndels(rec); + } + } + samIterator.next(); + } + + final Locus endLocus = new LocusImpl(Integer.MAX_VALUE, Integer.MAX_VALUE); + // if we have nothing to return to the user, and we're at the end of the SAM iterator, + // push everything into the complete queue + if (complete.isEmpty() && !samHasMore()) { + while (!accumulator.isEmpty()) { + populateCompleteQueue(endLocus); + if (!complete.isEmpty()) { + return complete.remove(0); + } + } + } + + // if there are completed entries, return those + if (!complete.isEmpty()) { + return complete.remove(0); + } else if (emitUncoveredLoci) { + final Locus afterLastMaskPositionLocus = new LocusImpl(referenceSequenceMask.getMaxSequenceIndex(), + referenceSequenceMask.getMaxPosition() + 1); + // In this case... we're past the last read from SAM so see if we can + // fill out any more (zero coverage) entries from the mask + return createNextUncoveredLocusInfo(afterLastMaskPositionLocus); + } else { + return null; + } + } + + + /** + * @return true if we have surpassed the maximum accumulation threshold for the first locus in the accumulator, false otherwise + */ + + private boolean surpassedAccumulationThreshold() { + final boolean surpassesThreshold = !accumulator.isEmpty() && accumulator.get(0).getRecordAndOffsets().size() >= maxReadsToAccumulatePerLocus; + if (surpassesThreshold && !enforcedAccumulationLimit) { + LOG.warn("We have encountered greater than " + maxReadsToAccumulatePerLocus + " reads at position " + accumulator.get(0).toString() + " and will ignore the remaining reads at this position. Note that further warnings will be suppressed."); + enforcedAccumulationLimit = true; + } + return surpassesThreshold; + } + + /** + * Capture the loci covered by the given SAMRecord in the LocusInfos in the accumulator, + * creating new LocusInfos as needed. + * + * @param rec record to add to accumulator + */ + abstract void accumulateSamRecord(final SAMRecord rec); + + + /** + * Requires that the accumulator for the record is previously fill with + * {@link #accumulateSamRecord(htsjdk.samtools.SAMRecord)}. + * Include in the LocusInfo the indels; the quality threshold does not affect insertions/deletions + */ + abstract void accumulateIndels(final SAMRecord rec); + + /** + * @param rec aligned SamRecord + * @param readOffset offset from start of read + * @param length length of aligned block + * @param refPosition position in the reference sequence + * @return RecordAndOffset + */ + abstract T createRecordAndOffset(SAMRecord rec, int readOffset, int length, int refPosition); + + /** + * Create the next relevant zero-coverage AbstractLocusInfo + * + * @param stopBeforeLocus don't go up to this sequence and position + * @return a zero-coverage AbstractLocusInfo, or null if there is none before the stopBefore locus + */ + private K createNextUncoveredLocusInfo(final Locus stopBeforeLocus) { + while (lastReferenceSequence <= stopBeforeLocus.getSequenceIndex() && + lastReferenceSequence <= referenceSequenceMask.getMaxSequenceIndex()) { + + if (lastReferenceSequence == stopBeforeLocus.getSequenceIndex() && + lastPosition + 1 >= stopBeforeLocus.getPosition()) { + return null; + } + + final int nextbit = referenceSequenceMask.nextPosition(lastReferenceSequence, lastPosition); + + // try the next reference sequence + if (nextbit == -1) { + // No more in this reference sequence + if (lastReferenceSequence == stopBeforeLocus.getSequenceIndex()) { + lastPosition = stopBeforeLocus.getPosition(); + return null; + } + lastReferenceSequence++; + lastPosition = 0; + } else if (lastReferenceSequence < stopBeforeLocus.getSequenceIndex() || nextbit < stopBeforeLocus.getPosition()) { + lastPosition = nextbit; + return createLocusInfo(getReferenceSequence(lastReferenceSequence), lastPosition); + } else if (nextbit >= stopBeforeLocus.getPosition()) { + return null; + } + } + return null; + } + + /** + * @param referenceSequence processed reference sequence + * @param lastPosition last processed reference locus position + * @return AbstractLocusInfo for the lastPosition + */ + abstract K createLocusInfo(SAMSequenceRecord referenceSequence, int lastPosition); + + /** + * Pop the first entry from the AbstractLocusInfo accumulator into the complete queue. In addition, + * check the ReferenceSequenceMask and if there are intervening mask positions between the last popped base and the one + * about to be popped, put those on the complete queue as well. + * Note that a single call to this method may not empty the accumulator completely, or even + * empty it at all, because it may just put a zero-coverage AbstractLocusInfo into the complete queue. + * + * @param stopBeforeLocus don't go up to this sequence and position + */ + private void populateCompleteQueue(final Locus stopBeforeLocus) { + // Because of gapped alignments, it is possible to create LocusInfo's with no reads associated with them. + // Skip over these if not including indels + while (!accumulator.isEmpty() && accumulator.get(0).isEmpty() && + locusComparator.compare(accumulator.get(0), stopBeforeLocus) < 0) { + accumulator.remove(0); + } + if (accumulator.isEmpty()) { + return; + } + final K locusInfo = accumulator.get(0); + if (locusComparator.compare(stopBeforeLocus, locusInfo) <= 0) { + return; + } + + // If necessary, emit a zero-coverage LocusInfo + if (emitUncoveredLoci) { + final K zeroCoverage = createNextUncoveredLocusInfo(locusInfo); + if (zeroCoverage != null) { + complete.add(zeroCoverage); + return; + } + } + + // At this point we know we're going to process the LocusInfo, so remove it from the accumulator. + accumulator.remove(0); + + // fill in any gaps based on our genome mask + final int sequenceIndex = locusInfo.getSequenceIndex(); + + + // only add to the complete queue if it's in the mask (or we have no mask!) + if (referenceSequenceMask.get(locusInfo.getSequenceIndex(), locusInfo.getPosition())) { + complete.add(locusInfo); + } + + lastReferenceSequence = sequenceIndex; + lastPosition = locusInfo.getPosition(); + } + + protected SAMSequenceRecord getReferenceSequence(final int referenceSequenceIndex) { + return samReader.getFileHeader().getSequence(referenceSequenceIndex); + } + + public void remove() { + throw new UnsupportedOperationException("Can not remove records from a SAM file via an iterator!"); + } + + /** + * Check if cigar start with an insertion, ignoring other operators that do not consume references bases + * + * @param cigar the cigar + * @return true if the first operator to consume reference bases or be an insertion, is an insertion; false otherwise + */ + protected static boolean startWithInsertion(final Cigar cigar) { + for (final CigarElement element : cigar.getCigarElements()) { + if (element.getOperator() == CigarOperator.I) return true; + if (!element.getOperator().consumesReferenceBases()) continue; + break; + } + return false; + } + + // -------------------------------------------------------------------------------------------- + // Helper methods below this point... + // -------------------------------------------------------------------------------------------- + + /** + * Controls which, if any, SAMRecords are filtered. By default duplicate reads and non-primary alignments + * are filtered out. The list of filters passed here replaces any existing filters. + * + * @param samFilters list of filters, or null if no filtering is desired. + */ + public void setSamFilters(final List samFilters) { + this.samFilters = samFilters; + } + + public int getQualityScoreCutoff() { + return qualityScoreCutoff; + } + + public void setQualityScoreCutoff(final int qualityScoreCutoff) { + this.qualityScoreCutoff = qualityScoreCutoff; + } + + public int getMappingQualityScoreCutoff() { + return mappingQualityScoreCutoff; + } + + public void setMappingQualityScoreCutoff(final int mappingQualityScoreCutoff) { + this.mappingQualityScoreCutoff = mappingQualityScoreCutoff; + } + + public boolean isIncludeNonPfReads() { + return includeNonPfReads; + } + + public void setIncludeNonPfReads(final boolean includeNonPfReads) { + this.includeNonPfReads = includeNonPfReads; + } + + public boolean isEmitUncoveredLoci() { + return emitUncoveredLoci; + } + + public void setEmitUncoveredLoci(final boolean emitUncoveredLoci) { + this.emitUncoveredLoci = emitUncoveredLoci; + } + + public int getMaxReadsToAccumulatePerLocus() { + return maxReadsToAccumulatePerLocus; + } + + /** + * If set, this will cap the number of reads we accumulate for any given position. + * As is pointed out above, setting this could cause major bias because of the non-random nature with which the + * cap is applied (the first maxReadsToAccumulatePerLocus reads are kept and all subsequent ones are dropped). + */ + public void setMaxReadsToAccumulatePerLocus(final int maxReadsToAccumulatePerLocus) { + this.maxReadsToAccumulatePerLocus = maxReadsToAccumulatePerLocus; + } + + protected List getIntervals() { + return intervals; + } + + protected Interval getCurrentInterval() { + if (intervals == null) return null; + return intervals.get(lastInterval); + } + + public boolean isIncludeIndels() { + return includeIndels; + } + + public void setIncludeIndels(final boolean includeIndels) { + this.includeIndels = includeIndels; + } +} diff --git a/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java b/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java new file mode 100644 index 000000000..28b9d34b3 --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java @@ -0,0 +1,130 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMRecord; + +/** + * Holds a SAMRecord plus the zero-based offset into that SAMRecord's bases and quality scores that corresponds + * to the base and quality at the genomic position described the containing AbstractLocusInfo. One object represents + * one base for SamLocusIterator.RecordAndOffset implementation or one alignment block of + * SAMRecord for TypedRecordAndOffset implementation. + * + * @author Darina_Nikolaeva@epam.com, EPAM Systems, Inc. + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + */ +public class AbstractRecordAndOffset { + + /** + * A SAMRecord aligned to reference position + */ + protected final SAMRecord record; + /** + * Zero-based offset in the read corresponding to the current position in AbstractLocusInfo + */ + protected final int offset; + + /** + * @param record inner SAMRecord + * @param offset from the start of the read + * @param length of alignment block + * @param refPos corresponding to read offset reference position + */ + public AbstractRecordAndOffset(final SAMRecord record, final int offset, int length, int refPos) { + this(record, offset); + } + + /** + * @param record inner SAMRecord + * @param offset from the start of the read + */ + public AbstractRecordAndOffset(final SAMRecord record, final int offset) { + this.offset = offset; + this.record = record; + } + + /** + * @return offset of aligned read base from the start of the read. + */ + public int getOffset() { + return offset; + } + + /** + * @return inner SAMRecord object. + */ + public SAMRecord getRecord() { + return record; + } + + /** + * @return the read base according to offset. + */ + public byte getReadBase() { + return record.getReadBases()[offset]; + } + + /** + * @return the length of alignment block represented by the object. + */ + public int getLength() { + return 1; + } + + /** + * @return the position in reference sequence, to which the start of alignment block is aligned. + */ + public int getRefPos() { + return -1; + } + + /** + * @return read name of inner SAMRecord. + */ + public String getReadName() { + return record.getReadName(); + } + + /** + * @return array of base qualities of inner SAMRecord. + */ + public byte[] getBaseQualities() { + return record.getBaseQualities(); + } + + /** + * @return the base quality according to offset. + */ + public byte getBaseQuality() { + return record.getBaseQualities()[offset]; + } + + protected void validateOffset(int offset, final byte[] array) { + if (offset < 0 || offset >= array.length) { + throw new IllegalArgumentException("The requested position is not covered by this " + this.getClass().getSimpleName() + + " object."); + } + } +} diff --git a/src/main/java/htsjdk/samtools/util/EdgeReadIterator.java b/src/main/java/htsjdk/samtools/util/EdgeReadIterator.java new file mode 100644 index 000000000..0d779a87e --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/EdgeReadIterator.java @@ -0,0 +1,228 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.AlignmentBlock; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.SamReader; + +/** + * Iterator that traverses a SAM File, accumulating information on a per-locus basis. + * Optionally takes a target interval list, in which case the loci returned are the ones covered by + * the interval list. If no target interval list, whatever loci are covered by the input reads are returned. + * By default duplicate reads and non-primary alignments are filtered out. Filtering may be changed + * via setSamFilters(). Difference from SamLocusIterator is that this implementation accumulates data + * only about start and end of alignment blocks from reads, not about each aligned base. + * + * @author Darina_Nikolaeva@epam.com, EPAM Systems, Inc. + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + * + */ +public class EdgeReadIterator extends AbstractLocusIterator> { + + /** + * Prepare to iterate through the given SAM records, skipping non-primary alignments. Do not use + * BAM index even if available. + * + * @param samReader must be coordinate sorted + */ + public EdgeReadIterator(final SamReader samReader) { + this(samReader, null); + } + + /** + * Prepare to iterate through the given SAM records, skipping non-primary alignments. + * + * @param samReader must be coordinate sorted + * @param intervalList Either the list of desired intervals, or null. Note that if an intervalList is + * passed in that is not coordinate sorted, it will eventually be coordinated sorted by this class. + */ + public EdgeReadIterator(final SamReader samReader, final IntervalList intervalList) { + this(samReader, intervalList, samReader.hasIndex()); + } + + /** + * Prepare to iterate through the given SAM records, skipping non-primary alignments + * + * @param samReader must be coordinate sorted + * @param intervalList Either the list of desired intervals, or null. Note that if an intervalList is + * passed in that is not coordinate sorted, it will eventually be coordinated sorted by this class. + * @param useIndex If true, do indexed lookup to improve performance. Not relevant if intervalList == null. + * It is no longer the case the useIndex==true can make performance worse. It should always perform at least + * as well as useIndex==false, and generally will be much faster. + */ + public EdgeReadIterator(final SamReader samReader, final IntervalList intervalList, final boolean useIndex) { + super(samReader, intervalList, useIndex); + } + + /** + * Capture the loci covered by the given SAMRecord in the AbstractLocusInfos in the accumulator, + * creating new AbstractLocusInfos as needed. EdgingRecordAndOffset object are created only for start + * and end of each alignment block of SAMRecord. + * If list of intervals is defined, start or/and length of alignment block are shifted to match the interval, to + * prevent exceeding the interval. + * @param rec SAMRecord to process and add to AbstractLocusInfo + */ + @Override + void accumulateSamRecord(SAMRecord rec) { + // interpret the CIGAR string and add the base info + for (final AlignmentBlock alignmentBlock : rec.getAlignmentBlocks()) { + // 0-based offset into the read of the current base + final int readOffset = alignmentBlock.getReadStart() - 1; + // 1-based reference position that the current base aligns to + final int refPos = alignmentBlock.getReferenceStart(); + + // 0-based offset from the aligned position of the first base in the read to the aligned position + // of the current base. + final int refOffset = refPos - rec.getAlignmentStart(); + final int refOffsetEnd = refPos - rec.getAlignmentStart() + alignmentBlock.getLength(); + + + // Ensure there are AbstractLocusInfos up to and including this position + for (int j = accumulator.size(); j <= refOffsetEnd; ++j) { + accumulator.add(createLocusInfo(getReferenceSequence(rec.getReferenceIndex()), + rec.getAlignmentStart() + j)); + } + + /* Let's assume an alignment block starts in some locus. + * We put two records to the accumulator. The first one has the "begin" type which corresponds to the locus + * where the block starts. The second one has the "end" type which corresponds to the other locus where the block ends. + */ + int refOffsetInterval = refOffset; // corresponds to the beginning of the alignment block + int refOffsetEndInterval = refOffsetEnd; + int startShift = 0; + + // intersect intervals and alignment block + if (getIntervals() != null) { + // get the current interval we're processing + Interval interval = getCurrentInterval(); + if (interval != null) { + final int intervalEnd = interval.getEnd(); + final int intervalStart = interval.getStart(); + // check if an interval and the alignment block overlap + if (!CoordMath.overlaps(refPos, refPos + alignmentBlock.getLength(), intervalStart, intervalEnd)) { + continue; + } + // if the alignment block starts out of an interval, shift the starting position + if (refPos < intervalStart) { + startShift = intervalStart - refPos; + refOffsetInterval = refOffsetInterval + startShift; + } + // if the alignment block ends out of an interval, shift the ending position + final int readEnd = refPos + alignmentBlock.getLength(); + if (refPos + alignmentBlock.getLength() > intervalEnd) { + refOffsetEndInterval = refOffsetEndInterval - (readEnd - intervalEnd) + 1; + } + } + } + final int length = refOffsetEndInterval - refOffsetInterval; + // add the alignment block to the accumulator when it starts and when it ends + final EdgingRecordAndOffset recordAndOffset = createRecordAndOffset(rec, readOffset + startShift, length, refPos + startShift); + // accumulate start of the alignment block + accumulator.get(refOffsetInterval).add(recordAndOffset); + final EdgingRecordAndOffset recordAndOffsetEnd = createRecordAndOffset(recordAndOffset); + // accumulate end of the alignment block + accumulator.get(refOffsetEndInterval).add(recordAndOffsetEnd); + } + } + + @Override + void accumulateIndels(SAMRecord rec) { + throw new UnsupportedOperationException("Indels accumulation is not supported for " + getClass().getSimpleName() + "."); + } + + /** + * Creates a new EdgingRecordAndOffset for given input values + * + * @param rec aligned SamRecord + * @param readOffset offset from start of read + * @param length length of alignment block + * @param refPos position in the reference sequence + * @return created EdgingRecordAndOffset + */ + @Override + EdgingRecordAndOffset createRecordAndOffset(SAMRecord rec, int readOffset, int length, int refPos) { + return EdgingRecordAndOffset.createBeginRecord(rec, readOffset, length, refPos); + } + + EdgingRecordAndOffset createRecordAndOffset(EdgingRecordAndOffset startRecord) { + return EdgingRecordAndOffset.createEndRecord(startRecord); + } + + /** + * @param referenceSequence processed reference sequence + * @param lastPosition last processed reference locus position + * @return AbstractLocusInfo for the lastPosition + */ + @Override + AbstractLocusInfo createLocusInfo(SAMSequenceRecord referenceSequence, int lastPosition) { + return new AbstractLocusInfo<>(referenceSequence, lastPosition); + } + + /** + * This method isn't supported in current implementation. + * + * @param maxReadsToAccumulatePerLocus maximum number of RecordAndOffset objects to store for + * one loci in reference sequence. + */ + @Override + public void setMaxReadsToAccumulatePerLocus(int maxReadsToAccumulatePerLocus) { + if (getMaxReadsToAccumulatePerLocus() != 0) { + throw new UnsupportedOperationException("Locus cap is not supported for " + getClass().getSimpleName() + "."); + } + } + + /** + * This method isn't supported in current implementation. + * + * @param qualityScoreCutoff the minimum base quality to include in AbstractLocusInfo. + */ + @Override + public void setQualityScoreCutoff(int qualityScoreCutoff) { + throw new UnsupportedOperationException("Quality filtering is not supported for " + getClass().getSimpleName() + "."); + } + + /** + * For correct work of EdgeReadIterator value emitUncoveredLoci must be true. + * + * @param emitUncoveredLoci if false, iterator will skip uncovered loci in reference sequence, otherwise + * empty AbstractLocusInfo will be created for each loci. + */ + @Override + public void setEmitUncoveredLoci(boolean emitUncoveredLoci) { + if (isEmitUncoveredLoci() != emitUncoveredLoci) { + throw new UnsupportedOperationException(getClass().getSimpleName() + " doesn't support work with skipping " + + "uncovered bases."); + } + } + + @Override + public void setIncludeIndels(boolean includeIndels) { + if (isIncludeIndels() != includeIndels) { + throw new UnsupportedOperationException("Indels accumulation is not supported for " + getClass().getSimpleName() + "."); + } + } +} diff --git a/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java new file mode 100644 index 000000000..b83a169e6 --- /dev/null +++ b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java @@ -0,0 +1,219 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMRecord; + +import static htsjdk.samtools.util.EdgingRecordAndOffset.Type.BEGIN; +import static htsjdk.samtools.util.EdgingRecordAndOffset.Type.END; + +/** + * Holds a SAMRecord plus the zero-based offset into that SAMRecord's bases and quality scores that corresponds + * to the base and quality for the start of alignment block at the genomic position described by the AbstractLocusInfo. + * This is implementation for EdgeReadIterator, field type added to indicate whether object + * represents the start or the end of an alignment block. + *

+ * Subclasses StartEdgingRecordAndOffset and EndEdgingRecordAndOffset are used in EdgeReadIterator to + * distinguish starting and ending of the alignment block + * as for each alignment block two objects of EdgingRecordAndOffset are created with two different types. + * The main idea of using EdgeReadIterator is to process alignment block starting from locus where BEGIN type occurs, + * aggregate information per locus and keep it until END type occurs, then remove alignment block from consideration. + * + * @author Darina_Nikolaeva@epam.com, EPAM Systems, Inc. + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + */ +public abstract class EdgingRecordAndOffset extends AbstractRecordAndOffset { + + private EdgingRecordAndOffset(SAMRecord record, int offset) { + super(record, offset); + } + + public abstract EdgingRecordAndOffset getStart(); + + public abstract Type getType(); + + public abstract byte getBaseQuality(int position); + + public static EdgingRecordAndOffset createBeginRecord(SAMRecord record, int offset, int length, int refPos) { + return new StartEdgingRecordAndOffset(record, offset, length, refPos); + } + + public static EdgingRecordAndOffset createEndRecord(EdgingRecordAndOffset startRecord) { + return new EndEdgingRecordAndOffset(startRecord); + } + + /** + * Describes the type of TypedRecordAndOffset, whether it represents the start or the end of + * an alignment block. + */ + public enum Type { + BEGIN, END + } + + private static class StartEdgingRecordAndOffset extends EdgingRecordAndOffset { + /** + * Length of alignment block of the read + */ + private final int length; + /** + * A reference position to which read offset is aligned. + */ + private final int refPos; + + private int hash = 0; + + /** + * @param record inner SAMRecord + * @param offset from the start of the read + * @param length of alignment block + * @param refPos corresponding to read offset reference position + */ + protected StartEdgingRecordAndOffset(SAMRecord record, int offset, int length, int refPos) { + super(record, offset); + if (length > record.getReadLength()) { + throw new IllegalArgumentException("Block length cannot be larger than whole read length"); + } + this.length = length; + this.refPos = refPos; + } + + /** + * @param position in the reference + * @return base quality of a read base, corresponding to a given reference position + */ + public byte getBaseQuality(int position) { + int rOffset = getRelativeOffset(position); + byte[] baseQualities = record.getBaseQualities(); + validateOffset(rOffset, baseQualities); + return baseQualities[rOffset]; + } + + /** + * @return the length of alignment block represented by the object. + */ + @Override + public int getLength() { + return length; + } + + /** + * @return the position in reference sequence, to which the start of alignment block is aligned. + */ + @Override + public int getRefPos() { + return refPos; + } + + /** + * @return type of object + */ + @Override + public Type getType() { + return BEGIN; + } + + /** + * @return EdgingRecordAndOffset that represents the start of alignment block of the read + * for object with type END. For object with type BEGIN will return null. + */ + @Override + public EdgingRecordAndOffset getStart() { + return null; + } + + @Override + public int hashCode() { + if (hash != 0) return hash; + hash = record.hashCode(); + hash = 31 * hash + length; + hash = 31 * hash + offset; + hash = 31 * hash + refPos; + return hash; + } + + private int getRelativeOffset(int position) { + return position - refPos + offset; + } + } + + private static class EndEdgingRecordAndOffset extends EdgingRecordAndOffset { + + /** + * For object with type END this fields holds the reference to object with type BEGIN for the read. + */ + final private EdgingRecordAndOffset start; + + EndEdgingRecordAndOffset(EdgingRecordAndOffset record) { + super(record.getRecord(), record.getOffset()); + this.start = record; + } + + /** + * @param position in the reference + * @return base quality of a read base, corresponding to a given reference position + */ + public byte getBaseQuality(int position) { + return start.getBaseQuality(position); + } + + /** + * @return the length of alignment block represented by the object. + */ + @Override + public int getLength() { + return start.getLength(); + } + + /** + * @return the position in reference sequence, to which the start of alignment block is aligned. + */ + @Override + public int getRefPos() { + return start.getRefPos(); + } + + /** + * @return type of object + */ + @Override + public Type getType() { + return END; + } + + /** + * @return EdgingRecordAndOffset that represents the start of alignment block of the read + * for object with type END + */ + @Override + public EdgingRecordAndOffset getStart() { + return start; + } + + @Override + public int hashCode() { + return start.hashCode(); + } + } +} diff --git a/src/main/java/htsjdk/samtools/util/SamLocusIterator.java b/src/main/java/htsjdk/samtools/util/SamLocusIterator.java index 33bcfd308..7a60756d6 100644 --- a/src/main/java/htsjdk/samtools/util/SamLocusIterator.java +++ b/src/main/java/htsjdk/samtools/util/SamLocusIterator.java @@ -23,10 +23,16 @@ */ package htsjdk.samtools.util; -import htsjdk.samtools.*; -import htsjdk.samtools.filter.*; +import htsjdk.samtools.AlignmentBlock; +import htsjdk.samtools.CigarElement; +import htsjdk.samtools.CigarOperator; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.SamReader; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Iterator that traverses a SAM File, accumulating information on a per-locus basis. @@ -37,172 +43,14 @@ * * @author alecw@broadinstitute.org */ -public class SamLocusIterator implements Iterable, CloseableIterator { - private static final Log LOG = Log.getInstance(SamLocusIterator.class); - - /** - * Holds a SAMRecord plus the zero-based offset into that SAMRecord's bases and quality scores that corresponds - * to the base and quality at the genomic position described the containing LocusInfo. - */ - public static class RecordAndOffset { - private final SAMRecord record; - private final int offset; - - public RecordAndOffset(final SAMRecord record, final int offset) { - this.offset = offset; - this.record = record; - } - - /** Zero-based offset into the read corresponding to the current position in LocusInfo */ - public int getOffset() { return offset; } - public SAMRecord getRecord() { return record; } - public byte getReadBase() { return record.getReadBases()[offset]; } - public byte getBaseQuality() { return record.getBaseQualities()[offset]; } - } - - /** - * The unit of iteration. Holds information about the locus (the SAMSequenceRecord and 1-based position - * on the reference), plus List of ReadAndOffset objects, one for each read that overlaps the locus; - * two more List_s_ of ReadAndOffset objects include reads that overlap the locus with insertions and deletions - * respectively - */ - public static final class LocusInfo implements Locus { - private final SAMSequenceRecord referenceSequence; - private final int position; - private final List recordAndOffsets = new ArrayList(100); - private List deletedInRecord = null; - private List insertedInRecord = null; - - LocusInfo(final SAMSequenceRecord referenceSequence, final int position) { - this.referenceSequence = referenceSequence; - this.position = position; - } - - /** Accumulate info for one read at the locus. */ - public void add(final SAMRecord read, final int position) { - recordAndOffsets.add(new RecordAndOffset(read, position)); - } - - /** Accumulate info for one read with a deletion */ - public void addDeleted(final SAMRecord read, int previousPosition) { - if (deletedInRecord == null) { - deletedInRecord = new ArrayList<>(); - } - deletedInRecord.add(new RecordAndOffset(read, previousPosition)); - } - - /** - * Accumulate info for one read with an insertion. - * For this locus, the reads in the insertion are included also in recordAndOffsets - */ - public void addInserted(final SAMRecord read, int firstPosition) { - if (insertedInRecord == null) { - insertedInRecord = new ArrayList<>(); - } - insertedInRecord.add(new RecordAndOffset(read, firstPosition)); - } - - public int getSequenceIndex() { return referenceSequence.getSequenceIndex(); } - - /** @return 1-based reference position */ - public int getPosition() { return position; } - public List getRecordAndPositions() { return Collections.unmodifiableList(recordAndOffsets); } - public String getSequenceName() { return referenceSequence.getSequenceName(); } - @Override public String toString() { return referenceSequence.getSequenceName() + ":" + position; } - public int getSequenceLength() {return referenceSequence.getSequenceLength();} - public List getDeletedInRecord() { - return (deletedInRecord == null) ? Collections.emptyList() : Collections.unmodifiableList(deletedInRecord); - } - public List getInsertedInRecord() { - return (insertedInRecord == null) ? Collections.emptyList() : Collections.unmodifiableList(insertedInRecord); - } - /** @return the number of records overlapping the position, with deletions included if they are being tracked. */ - public int size() { return this.recordAndOffsets.size() + ((deletedInRecord == null) ? 0 : deletedInRecord.size()); } - - /** - * @return true if all the RecordAndOffset lists are empty; - * false if at least one have records - */ - public boolean isEmpty() { - return recordAndOffsets.isEmpty() && - (deletedInRecord == null || deletedInRecord.isEmpty()) && - (insertedInRecord == null || insertedInRecord.isEmpty()); - } - } - - - private final SamReader samReader; - private final ReferenceSequenceMask referenceSequenceMask; - private PeekableIterator samIterator; - private List samFilters = Arrays.asList(new SecondaryOrSupplementaryFilter(), - new DuplicateReadFilter()); - private final List intervals; - private final boolean useIndex; - - /** - * LocusInfos on this list are ready to be returned by iterator. All reads that overlap - * the locus have been accumulated before the LocusInfo is moved into this list. - */ - private final ArrayList complete = new ArrayList(100); - - /** - * LocusInfos for which accumulation is in progress. When {@link #accumulateSamRecord(SAMRecord)} is called - * the state of this list is guaranteed to be either: - * a) Empty, or - * b) That the element at index 0 corresponds to the same genomic locus as the first aligned base - * in the read being accumulated - * - * Before each new read is accumulated the accumulator is examined and: - * i) any LocusInfos at positions earlier than the read start are moved to {@link #complete} - * ii) any uncovered positions between the last LocusInfo and the first aligned base of the new read - * have LocusInfos created and added to {@link #complete} if we are emitting uncovered loci - */ - private final ArrayList accumulator = new ArrayList(100); - - private int qualityScoreCutoff = Integer.MIN_VALUE; - private int mappingQualityScoreCutoff = Integer.MIN_VALUE; - private boolean includeNonPfReads = true; - - /** - * If true, emit a LocusInfo for every locus in the target map, or if no target map, - * emit a LocusInfo for every locus in the reference sequence. - * If false, emit a LocusInfo only if a locus has coverage. - */ - private boolean emitUncoveredLoci = true; - - /** - * If set, this will cap the number of reads we accumulate for any given position. - * Note that if we hit the maximum threshold at the first position in the accumulation queue, - * then we throw further reads overlapping that position completely away (including for subsequent positions). - * This is a useful feature if one wants to minimize the memory footprint in files with a few massively large pileups, - * but it must be pointed out that it could cause major bias because of the non-random nature with which the cap is - * applied (the first maxReadsToAccumulatePerLocus reads are kept and all subsequent ones are dropped). - */ - private int maxReadsToAccumulatePerLocus = Integer.MAX_VALUE; - - // Set to true when we have enforced the accumulation limit for the first time - private boolean enforcedAccumulationLimit = false; - - /** - * If true, include indels in the LocusInfo - */ - private boolean includeIndels = false; - - - // When there is a target mask, these members remember the last locus for which a LocusInfo has been - // returned, so that any uncovered locus in the target mask can be covered by a 0-coverage LocusInfo - private int lastReferenceSequence = 0; - private int lastPosition = 0; - - // Set to true when past all aligned reads in input SAM file - private boolean finishedAlignedReads = false; - - private final LocusComparator locusComparator = new LocusComparator(); +public class SamLocusIterator extends AbstractLocusIterator { /** * Prepare to iterate through the given SAM records, skipping non-primary alignments. Do not use * BAM index even if available. + * + * @param samReader must be coordinate sorted */ public SamLocusIterator(final SamReader samReader) { this(samReader, null); @@ -212,6 +60,7 @@ public SamLocusIterator(final SamReader samReader) { * Prepare to iterate through the given SAM records, skipping non-primary alignments. Do not use * BAM index even if available. * + * @param samReader must be coordinate sorted * @param intervalList Either the list of desired intervals, or null. Note that if an intervalList is * passed in that is not coordinate sorted, it will eventually be coordinated sorted by this class. */ @@ -230,209 +79,18 @@ public SamLocusIterator(final SamReader samReader, final IntervalList intervalLi * as well as useIndex==false, and generally will be much faster. */ public SamLocusIterator(final SamReader samReader, final IntervalList intervalList, final boolean useIndex) { - if (samReader.getFileHeader().getSortOrder() == null || samReader.getFileHeader().getSortOrder() == SAMFileHeader.SortOrder.unsorted) { - LOG.warn("SamLocusIterator constructed with samReader that has SortOrder == unsorted. ", "" + - "Assuming SAM is coordinate sorted, but exceptions may occur if it is not."); - } else if (samReader.getFileHeader().getSortOrder() != SAMFileHeader.SortOrder.coordinate) { - throw new SAMException("SamLocusIterator cannot operate on a SAM file that is not coordinate sorted."); - } - this.samReader = samReader; - this.useIndex = useIndex; - if (intervalList != null) { - intervals = intervalList.uniqued().getIntervals(); - this.referenceSequenceMask = new IntervalListReferenceSequenceMask(intervalList); - } else { - intervals = null; - this.referenceSequenceMask = new WholeGenomeReferenceSequenceMask(samReader.getFileHeader()); - } - } - - public Iterator iterator() { - if (samIterator != null) { - throw new IllegalStateException("Cannot call iterator() more than once on SamLocusIterator"); - } - CloseableIterator tempIterator; - if (intervals != null) { - tempIterator = new SamRecordIntervalIteratorFactory().makeSamRecordIntervalIterator(samReader, intervals, useIndex); - } else { - tempIterator = samReader.iterator(); - } - if (samFilters != null) { - tempIterator = new FilteringSamIterator(tempIterator, new AggregateFilter(samFilters)); - } - samIterator = new PeekableIterator(tempIterator); - return this; - } - - public void close() { - this.samIterator.close(); - } - - private boolean samHasMore() { - return !finishedAlignedReads && (samIterator.peek() != null); - } - - /** - * Returns true if there are more LocusInfo objects that can be returned, due to any of the following reasons: - * 1) there are more aligned reads in the SAM file - * 2) there are LocusInfos in some stage of accumulation - * 3) there are loci in the target mask that have yet to be accumulated (even if there are no reads covering them) - */ - public boolean hasNext() { - if (this.samIterator == null) { - iterator(); - } - - while (complete.isEmpty() && ((!accumulator.isEmpty()) || samHasMore() || hasRemainingMaskBases())) { - final LocusInfo locusInfo = next(); - if (locusInfo != null) { - complete.add(0, locusInfo); - } - } - return !complete.isEmpty(); - } - - /** - * Returns true if there are more bases at which the locus iterator must emit LocusInfos because - * there are loci beyond the last emitted loci which are in the set of loci to be emitted and - * the iterator is setup to emit uncovered loci - so we can guarantee we'll emit those loci. - */ - private boolean hasRemainingMaskBases() { - // if there are more sequences in the mask, by definition some of them must have - // marked bases otherwise if we're in the last sequence, but we're not at the last marked position, - // there is also more in the mask - if (!emitUncoveredLoci) { - // If not emitting uncovered loci, this check is irrelevant - return false; - } - return (lastReferenceSequence < referenceSequenceMask.getMaxSequenceIndex() || - (lastReferenceSequence == referenceSequenceMask.getMaxSequenceIndex() && - lastPosition < referenceSequenceMask.nextPosition(lastReferenceSequence, lastPosition))); - } - - /** - * hasNext() has been fixed so that if it returns true, next() is now guaranteed not to return null. - */ - public LocusInfo next() { - - // if we don't have any completed entries to return, try and make some! - while (complete.isEmpty() && samHasMore()) { - final SAMRecord rec = samIterator.peek(); - - // There might be unmapped reads mixed in with the mapped ones, but when a read - // is encountered with no reference index it means that all the mapped reads have been seen. - if (rec.getReferenceIndex() == -1) { - this.finishedAlignedReads = true; - continue; - - } - // Skip over an unaligned read that has been forced to be sorted with the aligned reads - if (rec.getReadUnmappedFlag() - || rec.getMappingQuality() < this.mappingQualityScoreCutoff - || (!this.includeNonPfReads && rec.getReadFailsVendorQualityCheckFlag())) { - samIterator.next(); - continue; - } - - int start = rec.getAlignmentStart(); - // only if we are including indels and the record does not start in the first base of the reference - // the stop locus to populate the queue is not the same if the record starts with an insertion - if (includeIndels && start != 1 && startWithInsertion(rec.getCigar())) { - // the start to populate is one less - start--; - } - final Locus alignmentStart = new LocusImpl(rec.getReferenceIndex(), start); - // emit everything that is before the start of the current read, because we know no more - // coverage will be accumulated for those loci. - while (!accumulator.isEmpty() && locusComparator.compare(accumulator.get(0), alignmentStart) < 0) { - final LocusInfo first = accumulator.get(0); - populateCompleteQueue(alignmentStart); - if (!complete.isEmpty()) { - return complete.remove(0); - } - if (!accumulator.isEmpty() && first == accumulator.get(0)) { - throw new SAMException("Stuck in infinite loop"); - } - } - - // at this point, either the accumulator list is empty or the head should - // be the same position as the first base of the read (or insertion if first) - if (!accumulator.isEmpty()) { - if (accumulator.get(0).getSequenceIndex() != rec.getReferenceIndex() || - accumulator.get(0).position != start) { - throw new IllegalStateException("accumulator should be empty or aligned with current SAMRecord"); - } - } - - // Store the loci for the read in the accumulator - if (!surpassedAccumulationThreshold()) { - accumulateSamRecord(rec); - // Store the indels if requested - if (includeIndels) { - accumulateIndels(rec); - } - } - samIterator.next(); - } - - final Locus endLocus = new LocusImpl(Integer.MAX_VALUE, Integer.MAX_VALUE); - // if we have nothing to return to the user, and we're at the end of the SAM iterator, - // push everything into the complete queue - if (complete.isEmpty() && !samHasMore()) { - while (!accumulator.isEmpty()) { - populateCompleteQueue(endLocus); - if (!complete.isEmpty()) { - return complete.remove(0); - } - } - } - - // if there are completed entries, return those - if (!complete.isEmpty()) { - return complete.remove(0); - } else if (emitUncoveredLoci) { - final Locus afterLastMaskPositionLocus = new LocusImpl(referenceSequenceMask.getMaxSequenceIndex(), - referenceSequenceMask.getMaxPosition() + 1); - // In this case... we're past the last read from SAM so see if we can - // fill out any more (zero coverage) entries from the mask - return createNextUncoveredLocusInfo(afterLastMaskPositionLocus); - } else { - return null; - } - } - - /** - * @return true if we have surpassed the maximum accumulation threshold for the first locus in the accumulator, false otherwise - */ - private boolean surpassedAccumulationThreshold() { - final boolean surpassesThreshold = !accumulator.isEmpty() && accumulator.get(0).recordAndOffsets.size() >= maxReadsToAccumulatePerLocus; - if (surpassesThreshold && !enforcedAccumulationLimit) { - LOG.warn("We have encountered greater than " + maxReadsToAccumulatePerLocus + " reads at position " + accumulator.get(0).toString() + " and will ignore the remaining reads at this position. Note that further warnings will be suppressed."); - enforcedAccumulationLimit = true; - } - return surpassesThreshold; - } - - /** - * Check if cigar start with an insertion, ignoring other operators that do not consume references bases - * @param cigar the cigar - * @return true if the first operator to consume reference bases or be an insertion, is an insertion; false otherwise - */ - private static boolean startWithInsertion(final Cigar cigar) { - for (final CigarElement element : cigar.getCigarElements()) { - if (element.getOperator()==CigarOperator.I) return true; - if (!element.getOperator().consumesReferenceBases()) continue; - break; - } - return false; + super(samReader, intervalList, useIndex); } /** * Capture the loci covered by the given SAMRecord in the LocusInfos in the accumulator, - * creating new LocusInfos as needed. + * creating new LocusInfos as needed. RecordAndOffset object are created for each aligned base of + * SAMRecord. + * + * @param rec SAMRecord to process and add to LocusInfo */ - private void accumulateSamRecord(final SAMRecord rec) { - + @Override + void accumulateSamRecord(final SAMRecord rec) { // get the accumulator offset int accOffset = getAccumulatorOffset(rec); @@ -454,7 +112,7 @@ private void accumulateSamRecord(final SAMRecord rec) { if (dontCheckQualities || baseQualities.length == 0 || baseQualities[readOffset] >= minQuality) { // 0-based offset from the aligned position of the first base in the read to the aligned position of the current base. final int refOffset = refStart + i - accOffset; - accumulator.get(refOffset).add(rec, readOffset); + accumulator.get(refOffset).add(new RecordAndOffset(rec, readOffset)); } } } @@ -465,7 +123,8 @@ private void accumulateSamRecord(final SAMRecord rec) { * {@link #accumulateSamRecord(htsjdk.samtools.SAMRecord)}. * Include in the LocusInfo the indels; the quality threshold does not affect insertions/deletions */ - private void accumulateIndels(final SAMRecord rec) { + @Override + void accumulateIndels(SAMRecord rec) { // get the cigar elements final List cigar = rec.getCigar().getCigarElements(); // 0-based offset into the read of the current base @@ -497,90 +156,6 @@ private void accumulateIndels(final SAMRecord rec) { } /** - * Create the next relevant zero-coverage LocusInfo - * - * @param stopBeforeLocus don't go up to this sequence and position - * @return a zero-coverage LocusInfo, or null if there is none before the stopBefore locus - */ - private LocusInfo createNextUncoveredLocusInfo(final Locus stopBeforeLocus) { - while (lastReferenceSequence <= stopBeforeLocus.getSequenceIndex() && - lastReferenceSequence <= referenceSequenceMask.getMaxSequenceIndex()) { - - if (lastReferenceSequence == stopBeforeLocus.getSequenceIndex() && - lastPosition + 1 >= stopBeforeLocus.getPosition()) { - return null; - } - - final int nextbit = referenceSequenceMask.nextPosition(lastReferenceSequence, lastPosition); - - // try the next reference sequence - if (nextbit == -1) { - // No more in this reference sequence - if (lastReferenceSequence == stopBeforeLocus.getSequenceIndex()) { - lastPosition = stopBeforeLocus.getPosition(); - return null; - } - lastReferenceSequence++; - lastPosition = 0; - } else if (lastReferenceSequence < stopBeforeLocus.getSequenceIndex() || nextbit < stopBeforeLocus.getPosition()) { - lastPosition = nextbit; - return new LocusInfo(getReferenceSequence(lastReferenceSequence), lastPosition); - } else if (nextbit >= stopBeforeLocus.getPosition()) { - return null; - } - } - - return null; - } - - /** - * Pop the first entry from the LocusInfo accumulator into the complete queue. In addition, - * check the ReferenceSequenceMask and if there are intervening mask positions between the last popped base and the one - * about to be popped, put those on the complete queue as well. - * Note that a single call to this method may not empty the accumulator completely, or even - * empty it at all, because it may just put a zero-coverage LocusInfo into the complete queue. - */ - private void populateCompleteQueue(final Locus stopBeforeLocus) { - // Because of gapped alignments, it is possible to create LocusInfo's with no reads associated with them. - // Skip over these if not including indels - while (!accumulator.isEmpty() && accumulator.get(0).isEmpty() && - locusComparator.compare(accumulator.get(0), stopBeforeLocus) < 0) { - accumulator.remove(0); - } - if (accumulator.isEmpty()) { - return; - } - final LocusInfo locusInfo = accumulator.get(0); - if (locusComparator.compare(stopBeforeLocus, locusInfo) <= 0) { - return; - } - - // If necessary, emit a zero-coverage LocusInfo - if (emitUncoveredLoci) { - final LocusInfo zeroCoverage = createNextUncoveredLocusInfo(locusInfo); - if (zeroCoverage != null) { - complete.add(zeroCoverage); - return; - } - } - - // At this point we know we're going to process the LocusInfo, so remove it from the accumulator. - accumulator.remove(0); - - // fill in any gaps based on our genome mask - final int sequenceIndex = locusInfo.getSequenceIndex(); - - - // only add to the complete queue if it's in the mask (or we have no mask!) - if (referenceSequenceMask.get(locusInfo.getSequenceIndex(), locusInfo.getPosition())) { - complete.add(locusInfo); - } - - lastReferenceSequence = sequenceIndex; - lastPosition = locusInfo.getPosition(); - } - - /** * Ensure that the queue is populated and get the accumulator offset for the current record */ private int getAccumulatorOffset(SAMRecord rec) { @@ -594,7 +169,6 @@ private int getAccumulatorOffset(SAMRecord rec) { if (insOffset == 1 && accumulator.isEmpty()) { accumulator.add(new LocusInfo(ref, alignmentStart - 1)); } - // Ensure there are LocusInfos up to and including this position for (int i = accumulator.size(); i <= alignmentLength + insOffset; ++i) { accumulator.add(new LocusInfo(ref, alignmentStart + i - insOffset)); @@ -602,12 +176,28 @@ private int getAccumulatorOffset(SAMRecord rec) { return alignmentStart - insOffset; } - private SAMSequenceRecord getReferenceSequence(final int referenceSequenceIndex) { - return samReader.getFileHeader().getSequence(referenceSequenceIndex); + + /** + * @param rec aligned SamRecord + * @param readOffset offset from start of read + * @param length 1, as object represents only one aligned base + * @param refPos -1, as this filed isn't used for this implementation + * @param type null for this implementation + * @return created RecordAndOffset + */ + @Override + RecordAndOffset createRecordAndOffset(SAMRecord rec, int readOffset, int length, int refPos) { + return new RecordAndOffset(rec, readOffset); } - public void remove() { - throw new UnsupportedOperationException("Can not remove records from a SAM file via an iterator!"); + /** + * @param referenceSequence processed reference sequence + * @param lastPosition last processed reference locus position + * @return LocusInfo for the lastPosition + */ + @Override + LocusInfo createLocusInfo(SAMSequenceRecord referenceSequence, int lastPosition) { + return new LocusInfo(referenceSequence, lastPosition); } // -------------------------------------------------------------------------------------------- @@ -615,67 +205,88 @@ public void remove() { // -------------------------------------------------------------------------------------------- /** - * Controls which, if any, SAMRecords are filtered. By default duplicate reads and non-primary alignments - * are filtered out. The list of filters passed here replaces any existing filters. - * - * @param samFilters list of filters, or null if no filtering is desired. + * Implementation of AbstractRecordAndOffset class for SamLocusIterator. + * One object represents one aligned base of inner SAMRecord. */ - public void setSamFilters(final List samFilters) { - this.samFilters = samFilters; - } + public static class RecordAndOffset extends AbstractRecordAndOffset { - public int getQualityScoreCutoff() { - return qualityScoreCutoff; + /** + * @param record inner SAMRecord + * @param offset 0-based offset from the start of SAMRecord + */ + public RecordAndOffset(final SAMRecord record, final int offset) { + super(record, offset); + } } - public void setQualityScoreCutoff(final int qualityScoreCutoff) { - this.qualityScoreCutoff = qualityScoreCutoff; - } + /** + * The unit of iteration. Holds information about the locus (the SAMSequenceRecord and 1-based position + * on the reference), plus List of ReadAndOffset objects, one for each read that overlaps the locus; + * two more List_s_ of ReadAndOffset objects include reads that overlap the locus with insertions and deletions + * respectively + */ + public static final class LocusInfo extends AbstractLocusInfo { - public int getMappingQualityScoreCutoff() { - return mappingQualityScoreCutoff; - } + private List deletedInRecord = null; + private List insertedInRecord = null; - public void setMappingQualityScoreCutoff(final int mappingQualityScoreCutoff) { - this.mappingQualityScoreCutoff = mappingQualityScoreCutoff; - } + /** + * @param referenceSequence reference sequence at which the reads are aligned + * @param position position in the sequence at which the reads are aligned + */ + public LocusInfo(SAMSequenceRecord referenceSequence, int position) { + super(referenceSequence, position); + } - public boolean isIncludeNonPfReads() { - return includeNonPfReads; - } + /** + * Accumulate info for one read with a deletion + */ + public void addDeleted(final SAMRecord read, int previousPosition) { + if (deletedInRecord == null) { + deletedInRecord = new ArrayList<>(); + } + deletedInRecord.add(new RecordAndOffset(read, previousPosition)); + } - public void setIncludeNonPfReads(final boolean includeNonPfReads) { - this.includeNonPfReads = includeNonPfReads; - } + /** + * Accumulate info for one read with an insertion. + * For this locus, the reads in the insertion are included also in recordAndOffsets + */ - public boolean isEmitUncoveredLoci() { - return emitUncoveredLoci; - } + public void addInserted(final SAMRecord read, int firstPosition) { - public void setEmitUncoveredLoci(final boolean emitUncoveredLoci) { - this.emitUncoveredLoci = emitUncoveredLoci; - } + if (insertedInRecord == null) { + insertedInRecord = new ArrayList<>(); + } + insertedInRecord.add(new RecordAndOffset(read, firstPosition)); + } - public int getMaxReadsToAccumulatePerLocus() { - return maxReadsToAccumulatePerLocus; - } + public List getDeletedInRecord() { + return (deletedInRecord == null) ? Collections.emptyList() : Collections.unmodifiableList(deletedInRecord); + } - /** - * If set, this will cap the number of reads we accumulate for any given position. - * As is pointed out above, setting this could cause major bias because of the non-random nature with which the - * cap is applied (the first maxReadsToAccumulatePerLocus reads are kept and all subsequent ones are dropped). - */ - public void setMaxReadsToAccumulatePerLocus(final int maxReadsToAccumulatePerLocus) { - this.maxReadsToAccumulatePerLocus = maxReadsToAccumulatePerLocus; - } + public List getInsertedInRecord() { + return (insertedInRecord == null) ? Collections.emptyList() : Collections.unmodifiableList(insertedInRecord); + } + + /** + * @return the number of records overlapping the position, with deletions included if they are being tracked. + */ + @Override + public int size() { + return super.size() + ((deletedInRecord == null) ? 0 : deletedInRecord.size()); + } - public boolean isIncludeIndels() { - return includeIndels; - } - public void setIncludeIndels(final boolean includeIndels) { - this.includeIndels = includeIndels; + /** + * @return true if all the RecordAndOffset lists are empty; + * false if at least one have records + */ + @Override + public boolean isEmpty() { + return getRecordAndPositions().isEmpty() && + (deletedInRecord == null || deletedInRecord.isEmpty()) && + (insertedInRecord == null || insertedInRecord.isEmpty()); + } } - } - diff --git a/src/test/java/htsjdk/samtools/util/AbstractLocusInfoTest.java b/src/test/java/htsjdk/samtools/util/AbstractLocusInfoTest.java new file mode 100644 index 000000000..a21c74361 --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/AbstractLocusInfoTest.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMSequenceRecord; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + */ + +public class AbstractLocusInfoTest { + private final byte[] qualities = {30, 50, 50, 60, 60, 70, 70, 70, 80, 90, 30, 50, 50, 60, 60, 70, 70, 70, 80, 90}; + private byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C', 'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; + private EdgingRecordAndOffset typedRecordAndOffset; + private EdgingRecordAndOffset typedRecordAndOffsetEnd; + private SAMSequenceRecord sequence = new SAMSequenceRecord("chrM", 100); + + @BeforeTest + public void setUp() { + SAMRecord record = new SAMRecord(new SAMFileHeader()); + record.setReadName("testRecord"); + record.setReadBases(bases); + record.setBaseQualities(qualities); + typedRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 10, 10, 10); + typedRecordAndOffsetEnd = EdgingRecordAndOffset.createEndRecord(typedRecordAndOffset); + } + + @Test + public void testConstructor() { + AbstractLocusInfo info = new AbstractLocusInfo<>(sequence, 1); + assertEquals("chrM", info.getSequenceName()); + assertEquals(0, info.getRecordAndOffsets().size()); + assertEquals(100, info.getSequenceLength()); + assertEquals(1, info.getPosition()); + } + + @Test + public void testAdd() { + AbstractLocusInfo info = new AbstractLocusInfo<>(sequence, 10); + info.add(typedRecordAndOffset); + info.add(typedRecordAndOffsetEnd); + assertEquals(2, info.getRecordAndOffsets().size()); + assertEquals(typedRecordAndOffset, info.getRecordAndOffsets().get(0)); + assertEquals(typedRecordAndOffsetEnd, info.getRecordAndOffsets().get(1)); + assertEquals(10, info.getPosition()); + assertEquals('A', info.getRecordAndOffsets().get(0).getReadBase()); + assertEquals('A', info.getRecordAndOffsets().get(1).getReadBase()); + assertEquals(30, info.getRecordAndOffsets().get(0).getBaseQuality()); + assertEquals(30, info.getRecordAndOffsets().get(1).getBaseQuality()); + } +} diff --git a/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java b/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java new file mode 100644 index 000000000..0c08436e5 --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java @@ -0,0 +1,68 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + + +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecordSetBuilder; +import htsjdk.samtools.SAMSequenceDictionary; +import htsjdk.samtools.SAMSequenceRecord; + +/** + * Common template for testing classes, that extend AbstractLocusIterator. + * + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + * + */ +public abstract class AbstractLocusIteratorTestTemplate { + + /** Coverage for tests with the same reads */ + final static int coverage = 2; + + /** the read length for the tests */ + final static int readLength = 36; + + final static SAMFileHeader header = new SAMFileHeader(); + + static { + header.setSortOrder(SAMFileHeader.SortOrder.coordinate); + SAMSequenceDictionary dict = new SAMSequenceDictionary(); + dict.addSequence(new SAMSequenceRecord("chrM", 100000)); + header.setSequenceDictionary(dict); + } + + /** Get the record builder for the tests with the default parameters that are needed */ + static SAMRecordSetBuilder getRecordBuilder() { + final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); + builder.setHeader(header); + builder.setReadLength(readLength); + return builder; + } + + public abstract void testBasicIterator(); + public abstract void testEmitUncoveredLoci(); + public abstract void testSimpleGappedAlignment(); + public abstract void testOverlappingGappedAlignmentsWithoutIndels(); +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java new file mode 100644 index 000000000..568c84c7c --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecord; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + * + */ + +public class AbstractRecordAndOffsetTest { + + private final byte[] qualities = {30, 40, 50, 60, 70, 80 ,90, 70, 80, 90}; + private byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; + SAMRecord record; + + @BeforeTest + public void setUp(){ + record = new SAMRecord(new SAMFileHeader()); + record.setReadName("testRecord"); + record.setReadBases(bases); + record.setBaseQualities(qualities); + } + + @Test + public void testConstructor(){ + AbstractRecordAndOffset abstractRecordAndOffset = new AbstractRecordAndOffset(record, 0, 10, 3); + assertArrayEquals(qualities, abstractRecordAndOffset.getBaseQualities()); + assertArrayEquals(bases, abstractRecordAndOffset.getRecord().getReadBases()); + assertEquals('A', abstractRecordAndOffset.getReadBase()); + assertEquals(30, abstractRecordAndOffset.getBaseQuality()); + assertEquals(0, abstractRecordAndOffset.getOffset()); + assertEquals(-1, abstractRecordAndOffset.getRefPos()); + } +} diff --git a/src/test/java/htsjdk/samtools/util/EdgeReadIteratorTest.java b/src/test/java/htsjdk/samtools/util/EdgeReadIteratorTest.java new file mode 100644 index 000000000..a5459c65c --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/EdgeReadIteratorTest.java @@ -0,0 +1,402 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + +import htsjdk.samtools.SAMRecordSetBuilder; +import htsjdk.samtools.SamReader; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Tests check that for each alignment block of processed reads, iterator returns a EdgingRecordAndOffset + * with type BEGIN for the reference position of read start and a EdgingRecordAndOffset with + * type END for the reference position + 1 of read end. + */ +public class EdgeReadIteratorTest extends AbstractLocusIteratorTestTemplate { + + @Override + @Test + public void testBasicIterator() { + final EdgeReadIterator sli = new EdgeReadIterator(createSamFileReader()); + int pos = 1; + for (final AbstractLocusInfo li : sli) { + if (pos == 1 || pos == 37) { + assertEquals(pos++, li.getPosition()); + assertEquals(2, li.getRecordAndOffsets().size()); + } else { + assertEquals(pos++, li.getPosition()); + assertEquals(0, li.getRecordAndOffsets().size()); + } + } + + } + + /** + * Since EdgeReadIterator does not support emitting uncovered loci, this test just check that + * iterator return correctly aligned objects for start and end of a read. + */ + @Override + @Test + public void testEmitUncoveredLoci() { + final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 165; + for (int i = 0; i < coverage; i++) { + // add a negative-strand fragment mapped on chrM with base quality of 10 + builder.addFrag("record" + i, 0, startPosition, true, false, "36M", null, 10); + } + final int coveredEnd = CoordMath.getEnd(startPosition, readLength) +1; + final EdgeReadIterator sli = new EdgeReadIterator(builder.getSamReader()); + + int pos = 1; + final int coveredStart = 165; + for (final AbstractLocusInfo li : sli) { + Assert.assertEquals(li.getPosition(), pos++); + final int expectedReads; + if (li.getPosition() == coveredStart || li.getPosition() == coveredEnd) { + expectedReads = 2; + } else { + expectedReads = 0; + } + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReads); + } + Assert.assertEquals(pos, 100001); + } + + /** + * Try all CIGAR operands (except H and P) and confirm that loci produced by SamLocusIterator are as expected. + */ + @Override + @Test + public void testSimpleGappedAlignment() {final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 165; + for (int i = 0; i < coverage; i++) { + // add a negative-strand fragment mapped on chrM with base quality of 10 + builder.addFrag("record" + i, 0, startPosition, true, false, "3S3M3N3M3D3M3I1N18M3S", null, 10); + } + final EdgeReadIterator sli = new EdgeReadIterator(builder.getSamReader()); + while (sli.hasNext()) { + AbstractLocusInfo info = sli.next(); + int pos = info.getPosition(); + if (pos == startPosition || pos == startPosition + 6 || pos == startPosition + 12 || pos == startPosition + 16) { + assertEquals(EdgingRecordAndOffset.Type.BEGIN, info.getRecordAndOffsets().get(0).getType()); + assertEquals(EdgingRecordAndOffset.Type.BEGIN, info.getRecordAndOffsets().get(1).getType()); + } else if (pos == startPosition + 3 || pos == startPosition + 9 || pos == startPosition + 15 || pos == startPosition + 34) { + assertEquals(EdgingRecordAndOffset.Type.END, info.getRecordAndOffsets().get(0).getType()); + assertEquals(EdgingRecordAndOffset.Type.END, info.getRecordAndOffsets().get(1).getType()); + } + } + } + + /** + * Test two reads that overlap because one has a deletion in the middle of it. + */ + @Override + @Test + public void testOverlappingGappedAlignmentsWithoutIndels() { + final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 1; + // Were it not for the gap, these two reads would not overlap + + builder.addFrag("record1", 0, startPosition, true, false, "18M10D18M", null, 10); + builder.addFrag("record2", 0, 41, true, false, "36M", null, 10); + + final EdgeReadIterator sli = new EdgeReadIterator(builder.getSamReader()); + // 5 base overlap btw the two reads + final int numBasesCovered = 81; + final int[] expectedReferencePositions = new int[numBasesCovered]; + final int[] expectedDepths = new int[numBasesCovered]; + final int[][] expectedReadOffsets = new int[numBasesCovered][]; + List start = Arrays.asList(0, 28, 40); + List end = Arrays.asList(19, 47, 77); + + int i; + // First 18 bases are from the first read + expectedDepths[0] = 1; + expectedReferencePositions[0] = 1; + expectedReadOffsets[0] = new int[]{0}; + + for (i = 1; i < 18; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + expectedDepths[i] = 1; + expectedReferencePositions[i] = 19; + expectedReadOffsets[i++] = new int[]{0}; + + for (; i < 28; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + + // Gap of 10, then 13 bases from the first read + expectedDepths[i] = 1; + expectedReferencePositions[i] = 29; + expectedReadOffsets[i++] = new int[]{18}; + + for (; i < 40; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + + expectedDepths[i] = 1; + expectedReferencePositions[i] = 41; + expectedReadOffsets[i++] = new int[]{0}; + + for (; i < 46; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + + expectedDepths[i] = 1; + expectedReferencePositions[i] = 47; + expectedReadOffsets[i++] = new int[]{18}; + + // Last 5 bases of first read overlap first 5 bases of second read + for (; i < 76; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + + expectedDepths[i] = 1; + expectedReferencePositions[i] = 77; + expectedReadOffsets[i++] = new int[]{0}; + + // Last 31 bases of 2nd read + + for (; i <= 80; ++i) { + fillEmptyLocus(expectedReferencePositions, expectedDepths, expectedReadOffsets, i); + } + + i = 0; + for (final AbstractLocusInfo li : sli) { + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedDepths[i]); + Assert.assertEquals(li.getPosition(), expectedReferencePositions[i]); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReadOffsets[i].length); + for (int j = 0; j < expectedReadOffsets[i].length; ++j) { + Assert.assertEquals(li.getRecordAndOffsets().get(j).getOffset(), expectedReadOffsets[i][j]); + if (start.contains(li.getPosition() - 1)) { + Assert.assertEquals(li.getRecordAndOffsets().get(j).getType(), EdgingRecordAndOffset.Type.BEGIN); + } + if (end.contains(li.getPosition() - 1)) { + Assert.assertEquals(li.getRecordAndOffsets().get(j).getType(), EdgingRecordAndOffset.Type.END); + } + } + ++i; + if (i == 80) { + break; + } + } + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetQualityCutOff() { + final EdgeReadIterator sli = new EdgeReadIterator(createSamFileReader()); + + sli.setQualityScoreCutoff(10); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetMaxReadsToAccumulatePerLocus() { + final EdgeReadIterator sli = new EdgeReadIterator(createSamFileReader()); + + sli.setMaxReadsToAccumulatePerLocus(100); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetEmitUncoveredLoci() { + final EdgeReadIterator sli = new EdgeReadIterator(createSamFileReader()); + + sli.setEmitUncoveredLoci(false); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetIncludeIndels() { + final EdgeReadIterator sli = new EdgeReadIterator(createSamFileReader()); + + sli.setIncludeIndels(true); + } + + /** + * Tests that reads, that don't intersect given interval list, are excluded from iterator + */ + @Test + public void testNotIntersectingInterval() { + SamReader samReader = createSamFileReader(); + + IntervalList intervals = createIntervalList("@HD\tSO:coordinate\tVN:1.0\n" + + "@SQ\tSN:chrM\tLN:100\n" + + "chrM\t50\t60\t+\ttest"); + EdgeReadIterator iterator = new EdgeReadIterator(samReader, intervals); + int locusPosition = 50; + while (iterator.hasNext()) { + AbstractLocusInfo next = iterator.next(); + assertEquals(locusPosition++, next.getPosition()); + assertEquals(0, next.getRecordAndOffsets().size()); + } + assertEquals(61, locusPosition); + } + + /** + * Tests that for reads, that intersect given interval list read start is shifted to the start of the interval and + * length is adjusted to the end of the interval. + */ + @Test + public void testIntersectingInterval() { + SamReader samReader = createSamFileReader(); + IntervalList intervals = createIntervalList("@HD\tSO:coordinate\tVN:1.0\n" + + "@SQ\tSN:chrM\tLN:100\n" + + "chrM\t5\t15\t+\ttest"); + EdgeReadIterator iterator = new EdgeReadIterator(samReader, intervals); + int locusPosition = 5; + while (iterator.hasNext()) { + AbstractLocusInfo next = iterator.next(); + int position = next.getPosition(); + assertEquals(locusPosition++, position); + if (position == 5) { + assertEquals(2, next.getRecordAndOffsets().size()); + for (EdgingRecordAndOffset record : next.getRecordAndOffsets()) { + assertEquals(11, record.getLength()); + } + } else { + assertEquals(0, next.getRecordAndOffsets().size()); + } + } + assertEquals(16, locusPosition); + } + + /** + * Test for mixed reads: intersecting and not the interval + */ + @Test + public void testIntersectingAndNotInterval() { + + final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 40; + // Were it not for the gap, these two reads would not overlap + builder.addFrag("record2", 0, startPosition, true, false, "36M", null, 10); + + IntervalList intervals = createIntervalList("@HD\tSO:coordinate\tVN:1.0\n" + + "@SQ\tSN:chrM\tLN:100\n" + + "chrM\t40\t80\t+\ttest"); + + EdgeReadIterator iterator = new EdgeReadIterator(builder.getSamReader(), intervals); + int locusPosition = 40; + while (iterator.hasNext()) { + AbstractLocusInfo next = iterator.next(); + int position = next.getPosition(); + assertEquals(locusPosition++, position); + if (position == 40) { + assertEquals(1, next.getRecordAndOffsets().size()); + for (EdgingRecordAndOffset record : next.getRecordAndOffsets()) { + assertEquals(36, record.getLength()); + assertEquals(EdgingRecordAndOffset.Type.BEGIN, record.getType()); + } + } else if (position == 76) { + assertEquals(1, next.getRecordAndOffsets().size()); + for (EdgingRecordAndOffset record : next.getRecordAndOffsets()) { + assertEquals(36, record.getLength()); + assertEquals(EdgingRecordAndOffset.Type.END, record.getType()); + } + } else { + assertEquals(0, next.getRecordAndOffsets().size()); + } + } + assertEquals(81, locusPosition); + } + + + /** + * Test for intersecting interval for read with a deletion in the middle + */ + @Test + public void testIntersectingIntervalWithComplicatedCigar() { + + final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 1; + // Were it not for the gap, these two reads would not overlap + builder.addFrag("record", 0, startPosition, true, false, "10M3D26M", null, 10); + + IntervalList intervals = createIntervalList("@HD\tSO:coordinate\tVN:1.0\n" + + "@SQ\tSN:chrM\tLN:100\n" + + "chrM\t5\t20\t+\ttest"); + + EdgeReadIterator iterator = new EdgeReadIterator(builder.getSamReader(), intervals); + int locusPosition = 5; + int[] expectedLength = new int[]{6, 7}; + int i = 0; + while (iterator.hasNext()) { + AbstractLocusInfo next = iterator.next(); + int position = next.getPosition(); + assertEquals(locusPosition++, position); + if (position == 5 || position == 14) { + assertEquals(1, next.getRecordAndOffsets().size()); + for (EdgingRecordAndOffset record : next.getRecordAndOffsets()) { + assertEquals(expectedLength[i], record.getLength()); + assertEquals(EdgingRecordAndOffset.Type.BEGIN, record.getType()); + } + } else if (position == 11) { + assertEquals(1, next.getRecordAndOffsets().size()); + for (EdgingRecordAndOffset record : next.getRecordAndOffsets()) { + assertEquals(expectedLength[i], record.getLength()); + assertEquals(EdgingRecordAndOffset.Type.END, record.getType()); + } + i++; + } else { + assertEquals(0, next.getRecordAndOffsets().size()); + } + } + assertEquals(21, locusPosition); + } + + + private void fillEmptyLocus(int[] expectedReferencePositions, int[] expectedDepths, int[][] expectedReadOffsets, int i) { + expectedReferencePositions[i] = i + 1; + expectedDepths[i] = 0; + expectedReadOffsets[i] = new int[]{}; + } + + private SamReader createSamFileReader() { + final SAMRecordSetBuilder builder = getRecordBuilder(); + // add records up to coverage for the test in that position + final int startPosition = 1; + for (int i = 0; i < coverage; i++) { + // add a negative-strand fragment mapped on chrM with base quality of 10 + builder.addFrag("record" + i, 0, startPosition, true, false, "36M", null, 10); + } + return builder.getSamReader(); + } + + + private IntervalList createIntervalList(String s) { + return IntervalList.fromReader(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(s.getBytes())))); + } +} diff --git a/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java b/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java new file mode 100644 index 000000000..a4f6478b4 --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright (c) 2016 The Broad Institute + * + * 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 htsjdk.samtools.util; + + +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecord; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +/** + * + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. + * + */ + +public class EdgingRecordAndOffsetTest { + private final byte[] qualities = {30, 50, 50, 60, 60, 70 ,70, 70, 80, 90}; + private final byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; + private SAMRecord record; + + @BeforeTest + public void setUp(){ + record = new SAMRecord(new SAMFileHeader()); + record.setReadName("testRecord"); + record.setReadBases(bases); + record.setBaseQualities(qualities); + } + + @Test + public void testConstructor(){ + EdgingRecordAndOffset typedRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 0, 10, 3); + assertArrayEquals(qualities, typedRecordAndOffset.getBaseQualities()); + assertArrayEquals(bases, typedRecordAndOffset.getRecord().getReadBases()); + assertEquals('A', typedRecordAndOffset.getReadBase()); + assertEquals(0, typedRecordAndOffset.getOffset()); + assertEquals(3, typedRecordAndOffset.getRefPos()); + assertEquals(EdgingRecordAndOffset.Type.BEGIN, typedRecordAndOffset.getType()); + } + + @Test + public void testGetSetStart(){ + EdgingRecordAndOffset typedRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 0, 10, 3); + EdgingRecordAndOffset typedRecordAndOffsetEnd = EdgingRecordAndOffset.createEndRecord(typedRecordAndOffset); + assertEquals(typedRecordAndOffset, typedRecordAndOffsetEnd.getStart()); + assertEquals(EdgingRecordAndOffset.Type.END, typedRecordAndOffsetEnd.getType()); + } + + @Test + public void testNotEqualsTypedRecords(){ + EdgingRecordAndOffset typedRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 0, 10, 3); + EdgingRecordAndOffset secondEdgingRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 5, 10, 3); + assertNotSame(typedRecordAndOffset.getBaseQuality(), secondEdgingRecordAndOffset.getBaseQuality()); + assertArrayEquals(typedRecordAndOffset.getBaseQualities(), secondEdgingRecordAndOffset.getBaseQualities()); + } + + @Test + public void testGetOffset(){ + EdgingRecordAndOffset secondEdgingRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 5, 10, 3); + assertEquals(70, secondEdgingRecordAndOffset.getBaseQuality()); + assertEquals('C', secondEdgingRecordAndOffset.getReadBase()); + } + + @Test + public void testGetQualityAtPosition(){ + EdgingRecordAndOffset secondEdgingRecordAndOffset = EdgingRecordAndOffset.createBeginRecord(record, 0, 10, 1); + assertEquals(50, secondEdgingRecordAndOffset.getBaseQuality(2)); + } +} diff --git a/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java b/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java index 17e77b29a..262b7c93f 100644 --- a/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java @@ -23,40 +23,15 @@ */ package htsjdk.samtools.util; -import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecordSetBuilder; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SAMSequenceRecord; import org.testng.Assert; import org.testng.annotations.Test; /** * @author alecw@broadinstitute.org + * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. */ -public class SamLocusIteratorTest { - - /** Coverage for tests with the same reads */ - final static int coverage = 2; - - /** the read length for the testss */ - final static int readLength = 36; - - final static SAMFileHeader header = new SAMFileHeader(); - - static { - header.setSortOrder(SAMFileHeader.SortOrder.coordinate); - SAMSequenceDictionary dict = new SAMSequenceDictionary(); - dict.addSequence(new SAMSequenceRecord("chrM", 100000)); - header.setSequenceDictionary(dict); - } - - /** Get the record builder for the tests with the default parameters that are needed */ - private static SAMRecordSetBuilder getRecordBuilder() { - final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); - builder.setHeader(header); - builder.setReadLength(readLength); - return builder; - } +public class SamLocusIteratorTest extends AbstractLocusIteratorTestTemplate { /** Create the SamLocusIterator with the builder*/ private SamLocusIterator createSamLocusIterator(final SAMRecordSetBuilder builder) { @@ -68,6 +43,7 @@ private SamLocusIterator createSamLocusIterator(final SAMRecordSetBuilder builde /** * Test a simple with only matches, with both including or not indels */ + @Override @Test public void testBasicIterator() { final SAMRecordSetBuilder builder = getRecordBuilder(); @@ -118,6 +94,7 @@ public void testMissingQualityString() { /** * Test the emit uncovered loci, with both including or not indels */ + @Override @Test public void testEmitUncoveredLoci() { @@ -459,6 +436,7 @@ public void testNBeforeDeletion() { * Try all CIGAR operands (except H and P) and confirm that loci produced by SamLocusIterator are as expected, * with both including or not indels */ + @Override @Test public void testSimpleGappedAlignment() { final SAMRecordSetBuilder builder = getRecordBuilder(); @@ -551,6 +529,7 @@ public void testSimpleGappedAlignment() { /** * Test two reads that overlap because one has a deletion in the middle of it, without tracking indels */ + @Override @Test public void testOverlappingGappedAlignmentsWithoutIndels() { final SAMRecordSetBuilder builder = getRecordBuilder(); @@ -691,4 +670,4 @@ public void testOverlappingGappedAlignmentsWithIndels() { } } -} +} \ No newline at end of file From a5ee55b3d569fc2c94dbabebb560f11c9f414f80 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Mon, 5 Dec 2016 14:07:58 -0800 Subject: [PATCH 049/137] Path index (#762) * Support index as Path Includes passing test that accesses an indexed BAM from Google Cloud Storage. Sample output: [TestNG] Running: /usr/local/google/home/jpmartin/.IdeaIC2016.2/system/temp-testng-customsuite.xml Count is: 916 =============================================== Default Suite Total tests run: 1, Failures: 0, Skips: 0 =============================================== Process finished with exit code 0 * Remove temp test so I can remove its dependency * Bugfix handing of input type and null combinations * Simplify code Simplified "if" logic, removed unused code, set mIsSeekable in all constructors consistently. --- src/main/java/htsjdk/samtools/SamReaderFactory.java | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index bf890d569..2b57cbfb2 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -307,11 +307,27 @@ public SamReader open(final SamInputResource resource) { Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE) ); File sourceFile = data.asFile(); + // calling asFile is safe even if indexMaybe is a Google Cloud Storage bucket + // (in that case we just get null) final File indexFile = indexMaybe == null ? null : indexMaybe.asFile(); if (SamStreams.isBAMFile(bufferedStream)) { if (sourceFile == null || !sourceFile.isFile()) { - // Handle case in which file is a named pipe, e.g. /dev/stdin or created by mkfifo - primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, validationStringency, this.samRecordFactory); + // check whether we can seek + final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); + // do not close bufferedStream, it's the same stream we're getting here. + SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); + if (indexFile!=null || null == sourceSeekable || null == indexSeekable) { + // not seekable. + // it's OK that we consumed a bit of the stream already, this ctor expects it. + primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, validationStringency, this.samRecordFactory); + } else { + // seekable. + // need to return to the beginning because it's the same stream we used earlier + // and read a bit from, and that form of the ctor expects the stream to start at 0. + sourceSeekable.seek(0); + primitiveSamReader = new BAMFileReader( + sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, this.samRecordFactory); + } } else { bufferedStream.close(); primitiveSamReader = new BAMFileReader(sourceFile, indexFile, false, asynchronousIO, validationStringency, this.samRecordFactory); From 4d0070b854bc1f88ad8015a8274756cafff979cd Mon Sep 17 00:00:00 2001 From: kcibul Date: Tue, 6 Dec 2016 17:30:13 -0500 Subject: [PATCH 050/137] Fixed bug disallowing file-based index and seekable path based source (#769) * Added support for use case where index is a file, and the source stream is a seekable path (e.g. a GCS location) --- .../java/htsjdk/samtools/SamReaderFactory.java | 4 +-- .../java/htsjdk/samtools/SamReaderFactoryTest.java | 38 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index 2b57cbfb2..8769f4879 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -316,7 +316,7 @@ public SamReader open(final SamInputResource resource) { final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); // do not close bufferedStream, it's the same stream we're getting here. SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); - if (indexFile!=null || null == sourceSeekable || null == indexSeekable) { + if (null == sourceSeekable || null == indexSeekable) { // not seekable. // it's OK that we consumed a bit of the stream already, this ctor expects it. primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, validationStringency, this.samRecordFactory); @@ -326,7 +326,7 @@ public SamReader open(final SamInputResource resource) { // and read a bit from, and that form of the ctor expects the stream to start at 0. sourceSeekable.seek(0); primitiveSamReader = new BAMFileReader( - sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, this.samRecordFactory); + sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, this.samRecordFactory); } } else { bufferedStream.close(); diff --git a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java index ece91e220..31ad5c259 100644 --- a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java @@ -284,7 +284,43 @@ public void queryInputResourcePermutation(final SamInputResource resource) throw } reader.close(); } - + + + /** + * A path that pretends it's not based upon a file. This helps in cases where we want to test branches + * that apply to non-file based paths without actually having to use non-file based resources (like cloud urls) + */ + private static class NeverFilePathInputResource extends PathInputResource { + public NeverFilePathInputResource(Path pathResource) { + super(pathResource); + } + + @Override + public File asFile() { + return null; + } + } + + @Test + public void checkHasIndexForStreamingPathBamWithFileIndex() throws IOException { + InputResource bam = new NeverFilePathInputResource(localBam.toPath()); + InputResource index = new FileInputResource(localBamIndex); + + // ensure that the index is being used, not checked in queryInputResourcePermutation + try (final SamReader reader = SamReaderFactory.makeDefault().open(new SamInputResource(bam, index))) { + Assert.assertTrue(reader.hasIndex()); + } + } + + @Test + public void queryStreamingPathBamWithFileIndex() throws IOException { + InputResource bam = new NeverFilePathInputResource(localBam.toPath()); + InputResource index = new FileInputResource(localBamIndex); + + final SamInputResource resource = new SamInputResource(bam, index); + queryInputResourcePermutation(new SamInputResource(bam, index)); + } + @Test public void customReaderFactoryTest() throws IOException { try { From 7e9188621af8c2214071716e8b5bc69895ba2530 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Sat, 26 Nov 2016 21:32:13 -0500 Subject: [PATCH 051/137] Validate VariantContext AC and AF without genotypes --- .../variant/variantcontext/VariantContext.java | 79 ++++++++++++---------- .../variantcontext/VariantContextUnitTest.java | 46 ++++++++++++- 2 files changed, 86 insertions(+), 39 deletions(-) diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index a6b8a5fce..55825fb4d 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -25,7 +25,6 @@ package htsjdk.variant.variantcontext; -import htsjdk.samtools.util.Tuple; import htsjdk.tribble.Feature; import htsjdk.tribble.TribbleException; import htsjdk.tribble.util.ParsingUtils; @@ -263,7 +262,7 @@ * @return an ordered list of genotype fields in use in VC. If vc has genotypes this will always include GT first */ public List calcVCFGenotypeKeys(final VCFHeader header) { - final Set keys = new HashSet(); + final Set keys = new HashSet<>(); boolean sawGoodGT = false; boolean sawGoodQual = false; @@ -287,11 +286,11 @@ if ( sawPL ) keys.add(VCFConstants.GENOTYPE_PL_KEY); if ( sawGenotypeFilter ) keys.add(VCFConstants.GENOTYPE_FILTER_KEY); - List sortedList = ParsingUtils.sortList(new ArrayList(keys)); + List sortedList = ParsingUtils.sortList(new ArrayList<>(keys)); // make sure the GT is first if (sawGoodGT) { - final List newList = new ArrayList(sortedList.size()+1); + final List newList = new ArrayList<>(sortedList.size() + 1); newList.add(VCFConstants.GENOTYPE_KEY); newList.addAll(sortedList); sortedList = newList; @@ -434,7 +433,7 @@ public VariantContext subContextFromSamples(Set sampleNames, final boole Set allelesFromGenotypes = allelesOfGenotypes(newGenotypes); // ensure original order of genotypes - List rederivedAlleles = new ArrayList(allelesFromGenotypes.size()); + List rederivedAlleles = new ArrayList<>(allelesFromGenotypes.size()); for (Allele allele : alleles) if (allelesFromGenotypes.contains(allele)) rederivedAlleles.add(allele); @@ -469,7 +468,7 @@ public VariantContext subContextFromSample(String sampleName) { * @return allele set */ private final Set allelesOfGenotypes(Collection genotypes) { - final Set alleles = new HashSet(); + final Set alleles = new HashSet<>(); boolean addedref = false; for ( final Genotype g : genotypes ) { @@ -863,7 +862,7 @@ private boolean hasAllele(final Allele allele, final boolean ignoreRefState, fin return null; } - List lengths = new ArrayList(); + List lengths = new ArrayList<>(); for ( Allele a : getAlternateAlleles() ) { lengths.add(a.length() - getReference().length()); } @@ -973,7 +972,7 @@ public GenotypesContext getGenotypes(String sampleName) { * @throws IllegalArgumentException if sampleName isn't bound to a genotype */ protected GenotypesContext getGenotypes(Collection sampleNames) { - return getGenotypes().subsetToSamples(new HashSet(sampleNames)); + return getGenotypes().subsetToSamples(new HashSet<>(sampleNames)); } public GenotypesContext getGenotypes(Set sampleNames) { @@ -1049,7 +1048,7 @@ public int getCalledChrCount(Set sampleIds) { * @return chromosome count */ public int getCalledChrCount(Allele a) { - return getCalledChrCount(a,new HashSet(0)); + return getCalledChrCount(a, new HashSet<>(0)); } /** @@ -1162,7 +1161,7 @@ public int getMixedCount() { * Run all extra-strict validation tests on a Variant Context object * * @param reportedReference the reported reference allele - * @param observedReference the actual reference allele + * @param observedReference the observed reference allele * @param rsIDs the true dbSNP IDs */ public void extraStrictValidation(final Allele reportedReference, final Allele observedReference, final Set rsIDs) { @@ -1172,7 +1171,7 @@ public void extraStrictValidation(final Allele reportedReference, final Allele o // validate the RS IDs validateRSIDs(rsIDs); - // validate the altenate alleles + // validate the alternate alleles validateAlternateAlleles(); // validate the AN and AC fields @@ -1201,7 +1200,7 @@ public void validateAlternateAlleles() { if ( !hasGenotypes() ) return; - // maintain a list of non-symbolic alleles reported in the REF and ALT fields of the record + // maintain a list of non-symbolic alleles expected in the REF and ALT fields of the record // (we exclude symbolic alleles because it's commonly expected that they don't show up in the genotypes, e.g. with GATK gVCFs) final List reportedAlleles = new ArrayList(); for ( final Allele allele : getAlleles() ) { @@ -1210,7 +1209,7 @@ public void validateAlternateAlleles() { } // maintain a list of non-symbolic alleles observed in the genotypes - final Set observedAlleles = new HashSet(); + final Set observedAlleles = new HashSet<>(); observedAlleles.add(getReference()); for ( final Genotype g : getGenotypes() ) { if ( g.isCalled() ) { @@ -1233,24 +1232,39 @@ public void validateAlternateAlleles() { throw new TribbleException.InternalCodecException(String.format("one or more of the ALT allele(s) for the record at position %s:%d are not observed at all in the sample genotypes", getContig(), getStart())); } + private void validateAttributeIsExpectedSize(final String attributeKey, final int numAlternateAlleles ) { + final List actualValues = getAttributeAsList(attributeKey); + if (!actualValues.isEmpty()) { + // always have at least one actual value + final int expectedValuesSize = numAlternateAlleles > 0 ? numAlternateAlleles : 1; + if (actualValues.size() != expectedValuesSize) { + throw new TribbleException.InternalCodecException(String.format("the %s tag has the incorrect number of records at position %s:%d, %d vs. %d", attributeKey, getContig(), getStart(), actualValues.size(), expectedValuesSize)); + } + } + } + public void validateChromosomeCounts() { + final int numberOfAlternateAlleles = alleles.size() - 1; + validateAttributeIsExpectedSize(VCFConstants.ALLELE_COUNT_KEY, numberOfAlternateAlleles); + validateAttributeIsExpectedSize(VCFConstants.ALLELE_FREQUENCY_KEY, numberOfAlternateAlleles); + if ( !hasGenotypes() ) return; // AN if ( hasAttribute(VCFConstants.ALLELE_NUMBER_KEY) ) { - int reportedAN = Integer.valueOf(getAttribute(VCFConstants.ALLELE_NUMBER_KEY).toString()); - int observedAN = getCalledChrCount(); + final int reportedAN = Integer.valueOf(getAttribute(VCFConstants.ALLELE_NUMBER_KEY).toString()); + final int observedAN = getCalledChrCount(); if ( reportedAN != observedAN ) throw new TribbleException.InternalCodecException(String.format("the Allele Number (AN) tag is incorrect for the record at position %s:%d, %d vs. %d", getContig(), getStart(), reportedAN, observedAN)); } // AC if ( hasAttribute(VCFConstants.ALLELE_COUNT_KEY) ) { - ArrayList observedACs = new ArrayList(); + final ArrayList observedACs = new ArrayList<>(); // if there are alternate alleles, record the relevant tags - if (!getAlternateAlleles().isEmpty()) { + if ( numberOfAlternateAlleles > 0 ) { for ( Allele allele : getAlternateAlleles() ) { observedACs.add(getCalledChrCount(allele)); } @@ -1259,22 +1273,13 @@ public void validateChromosomeCounts() { observedACs.add(0); } - if ( getAttribute(VCFConstants.ALLELE_COUNT_KEY) instanceof List ) { - final List reportedACs = (List)getAttribute(VCFConstants.ALLELE_COUNT_KEY); - if ( observedACs.size() != reportedACs.size() ) - throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag doesn't have the correct number of values for the record at position %s:%d, %d vs. %d", getContig(), getStart(), reportedACs.size(), observedACs.size())); - for (int i = 0; i < observedACs.size(); i++) { - // need to cast to int to make sure we don't have an issue below with object equals (earlier bug) - EB - final int reportedAC = Integer.valueOf(reportedACs.get(i).toString()); - if ( reportedAC != observedACs.get(i) ) - throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag is incorrect for the record at position %s:%d, %s vs. %d", getContig(), getStart(), reportedAC, observedACs.get(i))); - } - } else { - if ( observedACs.size() != 1 ) - throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag doesn't have enough values for the record at position %s:%d", getContig(), getStart())); - int reportedAC = Integer.valueOf(getAttribute(VCFConstants.ALLELE_COUNT_KEY).toString()); - if ( reportedAC != observedACs.get(0) ) - throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag is incorrect for the record at position %s:%d, %d vs. %d", getContig(), getStart(), reportedAC, observedACs.get(0))); + final List reportedACs = getAttributeAsList(VCFConstants.ALLELE_COUNT_KEY); + + for (int i = 0; i < observedACs.size(); i++) { + // need to cast to int to make sure we don't have an issue below with object equals (earlier bug) - EB + final int reportedAC = Integer.valueOf(reportedACs.get(i).toString()); + if ( reportedAC != observedACs.get(i) ) + throw new TribbleException.InternalCodecException(String.format("the Allele Count (AC) tag is incorrect for the record at position %s:%d, %s vs. %d", getContig(), getStart(), reportedAC, observedACs.get(i))); } } } @@ -1473,7 +1478,7 @@ public String toStringWithoutGenotypes() { // protected basic manipulation routines private static List makeAlleles(Collection alleles) { - final List alleleList = new ArrayList(alleles.size()); + final List alleleList = new ArrayList<>(alleles.size()); boolean sawRef = false; for ( final Allele a : alleles ) { @@ -1544,7 +1549,7 @@ private final void fullyDecodeInfo(final VariantContextBuilder builder, final VC private final Map fullyDecodeAttributes(final Map attributes, final VCFHeader header, final boolean lenientDecoding) { - final Map newAttributes = new HashMap(10); + final Map newAttributes = new HashMap<>(10); for ( final Map.Entry attr : attributes.entrySet() ) { final String field = attr.getKey(); @@ -1582,7 +1587,7 @@ private final Object decodeValue(final String field, final Object value, final V final String string = (String)value; if ( string.indexOf(',') != -1 ) { final String[] splits = string.split(","); - final List values = new ArrayList(splits.length); + final List values = new ArrayList<>(splits.length); for ( int i = 0; i < splits.length; i++ ) values.add(decodeOne(field, splits[i], format)); return values; @@ -1591,7 +1596,7 @@ private final Object decodeValue(final String field, final Object value, final V } } else if ( value instanceof List && (((List) value).get(0)) instanceof String ) { final List asList = (List)value; - final List values = new ArrayList(asList.size()); + final List values = new ArrayList<>(asList.size()); for ( final String s : asList ) values.add(decodeOne(field, s, format)); return values; diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 2dc345fed..14056f833 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -1259,13 +1259,34 @@ private VariantContext createValidateAlternateAllelesContext(final List final VariantContext vcACSetTwoAlts = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAlts, hetVarTC); + // with AC set, and two different ALTs (T and C), with no GT, we expect a 2 count values. + final Map attributesACNoGtTwoAlts = new HashMap(); + attributesACNoGtTwoAlts.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1", "1")); + final VariantContext vcACNoGtSetTwoAlts = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAlts, null); + + // with AF set, and two different ALTs (T and C), with GT of 1/2, we expect two frequncy values. + // With two ALTs, a list is expected, so we set the attribute as a list of 0.5,0.5 + final Map attributesAFTwoAlts = new HashMap(); + attributesAFTwoAlts.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5", "0.5")); + final VariantContext vcAFSetTwoAlts = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFTwoAlts, hetVarTC); + + // with AF set, and two different ALTs (T and C), with no GT, we expect two frequency values. + final Map attributesAFNoGtTwoAlts = new HashMap(); + attributesAFNoGtTwoAlts.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5", "0.5")); + final VariantContext vcAFNoGtSetTwoAlts = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAlts, null); + return new Object[][]{ {vcNoGenotypes}, {vcANSet}, {vcANSetNoCall}, {vcACSet}, {vcACSetNoAlts}, - {vcACSetTwoAlts} + {vcACNoGtSetTwoAlts}, + {vcAFSetTwoAlts}, + {vcAFNoGtSetTwoAlts} }; } @Test(dataProvider = "testValidateChromosomeCountsDataProvider") @@ -1319,13 +1340,34 @@ public void testValidateChromosomeCounts(final VariantContext vc) { final VariantContext vcACSetTwoAltsOneAltCount = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAltsOneAltCount, hetVarTC); + // with AC set, no GT, two ALTs, but only count for one ALT (we expect two items in the list: 1,1) + final Map attributesACNoGtTwoAltsOneAltCount = new HashMap(); + attributesACNoGtTwoAltsOneAltCount.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1")); + final VariantContext vcACNoGtSetTwoAltsOneAltCount = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAltsOneAltCount, null); + + // with AF set, two ALTs, but only frequency for one ALT (we expect two items in the list + final Map attributesAFTwoAltsWrongFreq = new HashMap(); + attributesAFTwoAltsWrongFreq.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5")); + final VariantContext vcAFSetTwoAltsWrongFreq = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFTwoAltsWrongFreq, hetVarTC); + + // with AF set, no GT, two ALTs, but only frequency for one ALT (we expect two items in the list + final Map attributesAFNoGtTwoAltsWrongCount = new HashMap(); + attributesAFNoGtTwoAltsWrongCount.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5")); + final VariantContext vcAFNoGtSetTwoAltsWrongFreq = + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAltsWrongCount, null); + return new Object[][]{ {vcANSet}, {vcANSetNoCall}, {vcACWrongCount}, {vcACSetTwoAlts}, {vcACSetTwoAltsWrongCount}, - {vcACSetTwoAltsOneAltCount} + {vcACSetTwoAltsOneAltCount}, + {vcACNoGtSetTwoAltsOneAltCount}, + {vcAFSetTwoAltsWrongFreq}, + {vcAFNoGtSetTwoAltsWrongFreq} }; } @Test(dataProvider = "testValidateChromosomeCountsFailureDataProvider", expectedExceptions = TribbleException.class) From 5a2d7a75b824aab483d201055ea70296a6b9ac10 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Fri, 9 Dec 2016 16:21:11 -0500 Subject: [PATCH 052/137] adding fine control over jexl expression behavior with missing values (#772) * adding JexlMissingValueTreatment to control how values in a jexl expression that are missing in the evaluated context are treated * adding overloads to VariantContextUtils.match to allow control of missing value behavior * minor refactoring in JEXLMap * making enum method package private * responding to comments * updated tests * moving JexlMissingValueTreatment to top level * fixing typo --- .../htsjdk/variant/variantcontext/JEXLMap.java | 115 ++++++++++++++------- .../variantcontext/JexlMissingValueTreatment.java | 39 +++++++ .../variantcontext/VariantContextUtils.java | 35 ++++++- .../variantcontext/VariantJEXLContextUnitTest.java | 52 ++++++++-- 4 files changed, 193 insertions(+), 48 deletions(-) create mode 100644 src/main/java/htsjdk/variant/variantcontext/JexlMissingValueTreatment.java diff --git a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java index b8e13c75b..33ec59514 100644 --- a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java +++ b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java @@ -5,11 +5,7 @@ import org.apache.commons.jexl2.JexlException; import org.apache.commons.jexl2.MapContext; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * This is an implementation of a Map of {@link JexlVCMatchExp} to true or false values. @@ -17,49 +13,87 @@ */ class JEXLMap implements Map { + /** + * If a JEXL expression contains values that are not available in the given context, the default behavior is to + * treat that expression as a miss match. + */ + public static final JexlMissingValueTreatment DEFAULT_MISSING_VALUE_TREATMENT = JexlMissingValueTreatment.TREAT_AS_MISMATCH; + // our variant context and/or Genotype private final VariantContext vc; private final Genotype g; - // our context - private JexlContext jContext = null; + private final JexlMissingValueTreatment howToTreatMissingValues; /** * our mapping from {@link JexlVCMatchExp} to {@link Boolean}s, which will be set to {@code NULL} * for previously un-cached {@link JexlVCMatchExp}. */ - private Map jexl; + private final Map jexl; - public JEXLMap(final Collection jexlCollection, final VariantContext vc, final Genotype g) { - initialize(jexlCollection); + // our context + private JexlContext jContext = null; + + /** + * Construct a new JEXLMap which can evaluate expressions against a specific genotype and variant context + * @param jexlCollection collection of expressions to be evaluated + * @param vc VariantContext to evaluate expressions against + * @param g genotype to evaluate expressions against, may be null + * @param howToTreatMissingValues how missing values in vc and g should be treated + */ + public JEXLMap(final Collection jexlCollection, final VariantContext vc, final Genotype g, final JexlMissingValueTreatment howToTreatMissingValues) { + this.jexl = initializeMap(jexlCollection); this.vc = vc; this.g = g; + this.howToTreatMissingValues = howToTreatMissingValues; } + + /** + * Construct a new JEXLMap which can evaluate expressions against a specific genotype and variant context + * @param jexlCollection collection of expressions to be evaluated + * @param vc VariantContext to evaluate expressions against + * @param g genotype to evaluate expressions against, may be null + * + * missing values are treated as false + */ + public JEXLMap(final Collection jexlCollection, final VariantContext vc, final Genotype g) { + this(jexlCollection, vc, g, DEFAULT_MISSING_VALUE_TREATMENT); + } + + /** + * Construct a new JEXLMap which can evaluate expressions against a specific VariantContext + * @param jexlCollection collection of expressions to be evaluated + * @param vc VariantContext to evaluate expressions against + * + * missing values are treated as non matches (false) + */ public JEXLMap(final Collection jexlCollection, final VariantContext vc) { - this(jexlCollection, vc, null); + this(jexlCollection, vc, null, DEFAULT_MISSING_VALUE_TREATMENT); } /** * Note: due to laziness, this accessor actually modifies the instance by possibly forcing evaluation of an Jexl expression. * - * @throws IllegalArgumentException when {@code o} is {@code null} or + * @throws IllegalArgumentException when {@code key} is {@code null} or * when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ - public Boolean get(Object o) { - if (o == null) { + public Boolean get(Object key) { + if (key == null) { throw new IllegalArgumentException("Query key is null"); } // if we've already determined the value, return it - if (jexl.containsKey(o) && jexl.get(o) != null) { - return jexl.get(o); + final Boolean value = jexl.get(key); + if (jexl.containsKey(key) && value != null) { + return value; } // otherwise cast the expression and try again - final JexlVCMatchExp e = (JexlVCMatchExp) o; - evaluateExpression(e); - return jexl.get(e); + final JexlVCMatchExp exp = (JexlVCMatchExp) key; + final boolean matches = evaluateExpression(exp); + jexl.put(exp, matches); + return matches; } /** @@ -87,9 +121,7 @@ public Boolean get(Object o) { */ public Collection values() { for (final JexlVCMatchExp exp : jexl.keySet()) { - if (jexl.get(exp) == null) { - evaluateExpression(exp); - } + jexl.computeIfAbsent(exp, k -> evaluateExpression(exp)); } return jexl.values(); } @@ -112,38 +144,42 @@ public void putAll(Map map) { } /** - * Initializes all keys with null values indicating that they have not yet been evaluated. + * Initializes a map and give all keys with null values indicating that they have not yet been evaluated. * The actual value will be computed only when the key is requested via {@link #get(Object)} or {@link #values()}. + * + * @return an initialized map of jexlExpression -> null */ - private void initialize(Collection jexlCollection) { - jexl = new HashMap<>(); + private static Map initializeMap(final Collection jexlCollection) { + final Map jexlMap = new HashMap<>(jexlCollection.size()); for (final JexlVCMatchExp exp: jexlCollection) { - jexl.put(exp, null); + jexlMap.put(exp, null); } + + return jexlMap; } /** * Evaluates a {@link JexlVCMatchExp}'s expression, given the current context (and setup the context if it's {@code null}). * * @param exp the {@link JexlVCMatchExp} to evaluate - * + * @return true if the expression matched the context * @throws IllegalArgumentException when {@code exp} is {@code null}, or * when the Jexl expression in {@code exp} fails to evaluate the JexlContext * constructed with the input VC or genotype. */ - private void evaluateExpression(final JexlVCMatchExp exp) { + private boolean evaluateExpression(final JexlVCMatchExp exp) { // if the context is null, we need to create it to evaluate the JEXL expression if (this.jContext == null) { - createContext(); + jContext = createContext(); } try { + //TODO figure out of this can ever evaluate to null or if that isn't actually possible final Boolean value = (Boolean) exp.exp.evaluate(jContext); - // treat errors as no match - jexl.put(exp, value == null ? false : value); + return value == null ? howToTreatMissingValues.getMissingValueOrExplode() : value; } catch (final JexlException.Variable e) { - // if exception happens because variable is undefined (i.e. field in expression is not present), evaluate to FALSE - jexl.put(exp,false); + //this occurs when the jexl expression contained a literal that didn't match anything in the given context + return howToTreatMissingValues.getMissingValueOrExplode(); } catch (final JexlException e) { // todo - might be better if no exception is caught here but let's user decide how to deal with them; note this will propagate to get() and values() throw new IllegalArgumentException(String.format("Invalid JEXL expression detected for %s", exp.name), e); @@ -151,16 +187,17 @@ private void evaluateExpression(final JexlVCMatchExp exp) { } /** - * Create the internal JexlContext, only when required. + * Create a new JexlContext * This code is where new JEXL context variables should get added. + * @return a new jexl context initialized appropriately */ - private void createContext() { + private JexlContext createContext() { if (vc == null) { - jContext = new MapContext(Collections.emptyMap()); + return new MapContext(Collections.emptyMap()); } else if (g == null) { - jContext = new VariantJEXLContext(vc); + return new VariantJEXLContext(vc); } else { - jContext = new GenotypeJEXLContext(vc, g); + return new GenotypeJEXLContext(vc, g); } } @@ -181,7 +218,7 @@ public Boolean remove(Object o) { public Set> entrySet() { - throw new UnsupportedOperationException("clear() not supported on a JEXLMap"); + throw new UnsupportedOperationException("entrySet() not supported on a JEXLMap"); } // nope diff --git a/src/main/java/htsjdk/variant/variantcontext/JexlMissingValueTreatment.java b/src/main/java/htsjdk/variant/variantcontext/JexlMissingValueTreatment.java new file mode 100644 index 000000000..204cc3f2c --- /dev/null +++ b/src/main/java/htsjdk/variant/variantcontext/JexlMissingValueTreatment.java @@ -0,0 +1,39 @@ +package htsjdk.variant.variantcontext; + +import java.util.function.Supplier; + +/** + * How to treat values that appear in a jexl expression but are missing in the context it's applied to + */ +public enum JexlMissingValueTreatment { + /** + * Treat expressions with a missing value as a mismatch and evaluate to false + */ + TREAT_AS_MISMATCH(() -> false), + + /** + * Treat expressions with a missing value as a match and evaluate to true + */ + TREAT_AS_MATCH(() -> true), + + /** + * Treat expressions with a missing value as an error and throw an {@link IllegalArgumentException} + */ + THROW(() -> {throw new IllegalArgumentException("Jexl Expression couldn't be evaluated because there was a missing value.");}); + + private final Supplier resultSupplier; + + JexlMissingValueTreatment(final Supplier resultSupplier){ + this.resultSupplier = resultSupplier; + } + + /** + * get the missing value that corresponds to this option or throw an exception + * @return the value that should be used in case of a missing value + * @throws IllegalArgumentException if this should be treated as an error + */ + boolean getMissingValueOrExplode(){ + return resultSupplier.get(); + } + +} diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java index 96eaa64e3..face55bb9 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java @@ -307,6 +307,7 @@ public static boolean match(VariantContext vc, JexlVCMatchExp exp) { * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * + * Expressions that contain literals not available in the VariantContext or Genotype will be treated as not matching * @param vc variant context * @param exps expressions * @return true if there is a match @@ -324,7 +325,20 @@ public static boolean match(VariantContext vc, JexlVCMatchExp exp) { * @return true if there is a match */ public static boolean match(VariantContext vc, Genotype g, JexlVCMatchExp exp) { - return match(vc,g, Collections.singletonList(exp)).get(exp); + return match(vc, g, Collections.singletonList(exp), JEXLMap.DEFAULT_MISSING_VALUE_TREATMENT).get(exp); + } + + /** + * Returns true if {@code exp} match {@code vc}, {@code g}. + * See {@link #match(VariantContext, Genotype, Collection)} for full docs. + * @param vc variant context + * @param g genotype + * @param exp expression + * @param howToTreatMissingValues what to do if the jexl expression contains literals that aren't in the context + * @return true if there is a match + */ + public static boolean match(VariantContext vc, Genotype g, JexlVCMatchExp exp, JexlMissingValueTreatment howToTreatMissingValues) { + return match(vc, g, Collections.singletonList(exp), howToTreatMissingValues).get(exp); } /** @@ -333,13 +347,30 @@ public static boolean match(VariantContext vc, Genotype g, JexlVCMatchExp exp) { * This the best way to apply JEXL expressions to {@link VariantContext} records. * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. * + * Expressions that contain literals not available in the VariantContext or Genotype will be treated as not matching * @param vc variant context * @param g genotype * @param exps expressions * @return true if there is a match */ public static Map match(VariantContext vc, Genotype g, Collection exps) { - return new JEXLMap(exps,vc,g); + return match(vc, g, exps, JEXLMap.DEFAULT_MISSING_VALUE_TREATMENT); + } + + /** + * Matches each {@link JexlVCMatchExp} exp against the data contained in {@code vc}, {@code g}, + * and returns a map from these expressions to {@code true} (if they matched) or {@code false} (if they didn't). + * This the best way to apply JEXL expressions to {@link VariantContext} records. + * Use the various {@code initializeMatchExps()}'s to create the list of {@link JexlVCMatchExp} expressions. + * + * @param vc variant context + * @param g genotype + * @param exps expressions + * @param howToTreatMissingValues what to do if the jexl expression contains literals that aren't in the context + * @return true if there is a match + */ + public static Map match(VariantContext vc, Genotype g, Collection exps, JexlMissingValueTreatment howToTreatMissingValues) { + return new JEXLMap(exps, vc, g, howToTreatMissingValues); } /** diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java index bebd39384..78bf565a7 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantJEXLContextUnitTest.java @@ -31,14 +31,10 @@ import htsjdk.variant.vcf.VCFConstants; import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; /** @@ -55,6 +51,10 @@ private static final VariantContextUtils.JexlVCMatchExp exp = new VariantContextUtils.JexlVCMatchExp("name", VariantContextUtils.engine.get().createExpression("QUAL > 500.0")); + private static final JexlVCMatchExp missingValueExpression = new VariantContextUtils.JexlVCMatchExp( + "Zis10", VariantContextUtils.engine.get().createExpression("Z==10")); + + // SNP alleles: A[ref]/T[alt] at chr1:10. One (crappy) sample, one (bare minimum) VC. private static final SimpleFeature eventLoc = new SimpleFeature("chr1", 10, 10); private static final Allele Aref = Allele.create("A", true); @@ -87,7 +87,45 @@ public void testGetValue() { // eval our known expression Assert.assertTrue(!jexlMap.get(exp)); } - + + @Test(dataProvider = "getMissingValueTestData") + public void testMissingBehaviorThroughMatch(VariantContext vc, JexlMissingValueTreatment missingValueTreatment, boolean expected, Class expectedException){ + if(expectedException == null) { + Assert.assertEquals(VariantContextUtils.match(vc, null, missingValueExpression, missingValueTreatment), expected); + } else { + Assert.assertThrows(expectedException, () -> VariantContextUtils.match(vc, null, missingValueExpression, missingValueTreatment)); + } + } + + @Test(dataProvider = "getMissingValueTestData") + public void testMissingBehavior(VariantContext vc, JexlMissingValueTreatment missingValueTreatment, boolean expected, Class expectedException){ + final JEXLMap jexlMap = new JEXLMap(Collections.singletonList(missingValueExpression), vc, null, missingValueTreatment); + if(expectedException == null) { + Assert.assertEquals((boolean) jexlMap.get(missingValueExpression), expected); + } else { + Assert.assertThrows(expectedException, () -> jexlMap.get(missingValueExpression)); + } + } + + @DataProvider + public Object[][] getMissingValueTestData(){ + final List alleles = Arrays.asList(Aref, Talt); + VariantContextBuilder vcb = new VariantContextBuilder("test", "chr1", 10, 10, alleles); + VariantContext noZ = vcb.make(); + VariantContext hasZ = vcb.attribute("Z", 0).make(); + + return new Object[][]{ + {noZ, JEXLMap.DEFAULT_MISSING_VALUE_TREATMENT, false, null}, + {hasZ, JEXLMap.DEFAULT_MISSING_VALUE_TREATMENT, false, null}, //the value isn't missing but the expression is false + {noZ, JexlMissingValueTreatment.TREAT_AS_MATCH, true, null}, + {hasZ, JexlMissingValueTreatment.TREAT_AS_MATCH, false, null}, //the value isn't missing but the expression is false + {noZ, JexlMissingValueTreatment.TREAT_AS_MISMATCH, false, null}, + {hasZ, JexlMissingValueTreatment.TREAT_AS_MISMATCH, false, null}, + {noZ, JexlMissingValueTreatment.THROW, false, IllegalArgumentException.class}, + {hasZ, JexlMissingValueTreatment.THROW, false, null} + }; + } + // Testing the new 'FT' and 'isPassFT' expressions in the JEXL map @Test public void testJEXLGenotypeFilters() { From 2386a6eb830a2119c6e38e6154872d23a5e2d9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 13 Dec 2016 21:23:15 +0100 Subject: [PATCH 053/137] removed VariantContextWriterFactory (#756) --- .../writer/VariantContextWriterBuilder.java | 1 - .../writer/VariantContextWriterFactory.java | 282 --------------------- 2 files changed, 283 deletions(-) delete mode 100644 src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterFactory.java diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterBuilder.java b/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterBuilder.java index 56c8b8bf6..b1d7ca0eb 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterBuilder.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterBuilder.java @@ -53,7 +53,6 @@ * *

* Provides methods for creating VariantContextWriters using the Builder pattern. - * Replaces VariantContextWriterFactory. *

*

* The caller must choose an output file or an output stream for the VariantContextWriter to write to. diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterFactory.java b/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterFactory.java deleted file mode 100644 index e1e00265d..000000000 --- a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriterFactory.java +++ /dev/null @@ -1,282 +0,0 @@ -/* -* Copyright (c) 2012 The Broad Institute -* -* 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 htsjdk.variant.variantcontext.writer; - -import htsjdk.samtools.Defaults; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.util.BlockCompressedOutputStream; -import htsjdk.samtools.util.IOUtil; -import htsjdk.samtools.util.RuntimeIOException; -import htsjdk.tribble.AbstractFeatureReader; -import htsjdk.tribble.index.IndexCreator; -import htsjdk.tribble.index.tabix.TabixFormat; -import htsjdk.tribble.index.tabix.TabixIndexCreator; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.OutputStream; -import java.util.EnumSet; - -/** - * Factory methods to create VariantContext writers - * - * @author depristo - * @since 5/12 - * - * @deprecated Replaced by {@link VariantContextWriterBuilder} - */ -@Deprecated -public class VariantContextWriterFactory { - - public static final EnumSet DEFAULT_OPTIONS = EnumSet.of(Options.INDEX_ON_THE_FLY); - public static final EnumSet NO_OPTIONS = EnumSet.noneOf(Options.class); - - static { - if (Defaults.USE_ASYNC_IO_WRITE_FOR_TRIBBLE) { - DEFAULT_OPTIONS.add(Options.USE_ASYNC_IO); - } - } - - private VariantContextWriterFactory() {} - - public static VariantContextWriter create(final File location, final SAMSequenceDictionary refDict) { - return create(location, openOutputStream(location), refDict, DEFAULT_OPTIONS); - } - - public static VariantContextWriter create(final File location, final SAMSequenceDictionary refDict, final EnumSet options) { - return create(location, openOutputStream(location), refDict, options); - } - - /** - * @param output If buffered writing is desired, caller must provide some kind of buffered OutputStream. - */ - public static VariantContextWriter create(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict) { - return create(location, output, refDict, DEFAULT_OPTIONS); - } - - /** - * @param output If buffered writing is desired, caller must provide some kind of buffered OutputStream. - */ - public static VariantContextWriter create(final OutputStream output, - final SAMSequenceDictionary refDict, - final EnumSet options) { - return create(null, output, refDict, options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, and for naming the index, - * but does not control where the file is written - * @param output This is where the BCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createBcf2(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final EnumSet options) { - return maybeWrapWithAsyncWriter(new BCF2Writer(location, output, refDict, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES)), options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, and for naming the index, - * but does not control where the file is written - * @param output This is where the BCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createBcf2(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final IndexCreator indexCreator, - final EnumSet options) { - return maybeWrapWithAsyncWriter(new BCF2Writer(location, output, refDict, indexCreator, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES)), options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, and for naming the index, - * but does not control where the file is written - * @param output This is where the VCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createVcf(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final EnumSet options) { - return maybeWrapWithAsyncWriter(new VCFWriter(location, output, refDict, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES), - options.contains(Options.ALLOW_MISSING_FIELDS_IN_HEADER), - options.contains(Options.WRITE_FULL_FORMAT_FIELD)), options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, and for naming the index, - * but does not control where the file is written - * @param output This is where the VCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createVcf(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final IndexCreator indexCreator, - final EnumSet options) { - return maybeWrapWithAsyncWriter(new VCFWriter(location, output, refDict, indexCreator, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES), - options.contains(Options.ALLOW_MISSING_FIELDS_IN_HEADER), - options.contains(Options.WRITE_FULL_FORMAT_FIELD)), options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, - * but does not control where the file is written - * @param output This is where the VCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createBlockCompressedVcf(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final EnumSet options) { - final TabixIndexCreator indexCreator; - if (options.contains(Options.INDEX_ON_THE_FLY)) { - indexCreator = new TabixIndexCreator(refDict, TabixFormat.VCF); - } else { - indexCreator = null; - } - return maybeWrapWithAsyncWriter(new VCFWriter(location, BlockCompressedOutputStream.maybeBgzfWrapOutputStream(location, output), - refDict, indexCreator, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES), - options.contains(Options.ALLOW_MISSING_FIELDS_IN_HEADER), - options.contains(Options.WRITE_FULL_FORMAT_FIELD)), options); - } - - /** - * @param location Note that this parameter is used to producing intelligent log messages, - * but does not control where the file is written - * @param output This is where the VCF is actually written. If buffered writing is desired, caller must provide - * some kind of buffered OutputStream. - */ - public static VariantContextWriter createBlockCompressedVcf(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final IndexCreator indexCreator, - final EnumSet options) { - return maybeWrapWithAsyncWriter(new VCFWriter(location, BlockCompressedOutputStream.maybeBgzfWrapOutputStream(location, output), - refDict, indexCreator, - options.contains(Options.INDEX_ON_THE_FLY), - options.contains(Options.DO_NOT_WRITE_GENOTYPES), - options.contains(Options.ALLOW_MISSING_FIELDS_IN_HEADER), - options.contains(Options.WRITE_FULL_FORMAT_FIELD)), options); - } - - public static VariantContextWriter create(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final EnumSet options) { - - if (isBCFOutput(location, options)) { - return createBcf2(location, output, refDict, options); - } else if (isCompressedVcf(location)) { - return createBlockCompressedVcf(location, output, refDict, options); - } else { - return createVcf(location, output, refDict, options); - } - } - - /** - * @param output If buffered writing is desired, caller must provide some kind of buffered OutputStream. - */ - public static VariantContextWriter create(final File location, - final OutputStream output, - final SAMSequenceDictionary refDict, - final IndexCreator indexCreator, - final EnumSet options) { - - if (isBCFOutput(location, options)) { - return createBcf2(location, output, refDict, indexCreator, options); - } else if (isCompressedVcf(location)) { - return createBlockCompressedVcf(location, output, refDict, indexCreator, options); - } else { - return createVcf(location, output, refDict, indexCreator, options); - } - } - - private static VariantContextWriter maybeWrapWithAsyncWriter(final VariantContextWriter writer, - final EnumSet options) { - if (options.contains(Options.USE_ASYNC_IO)) { - return new AsyncVariantContextWriter(writer, AsyncVariantContextWriter.DEFAULT_QUEUE_SIZE); - } - else return writer; - } - - /** - * Should we output a BCF file based solely on the name of the file at location? - * - * @param location - * @return - */ - public static boolean isBCFOutput(final File location) { - return isBCFOutput(location, EnumSet.noneOf(Options.class)); - } - - public static boolean isBCFOutput(final File location, final EnumSet options) { - return options.contains(Options.FORCE_BCF) || (location != null && location.getName().contains(".bcf")); - } - - public static boolean isCompressedVcf(final File location) { - if (location == null) - return false; - - return AbstractFeatureReader.hasBlockCompressedExtension(location); - } - - public static VariantContextWriter sortOnTheFly(final VariantContextWriter innerWriter, final int maxCachingStartDistance) { - return sortOnTheFly(innerWriter, maxCachingStartDistance, false); - } - - public static VariantContextWriter sortOnTheFly(final VariantContextWriter innerWriter, final int maxCachingStartDistance, final boolean takeOwnershipOfInner) { - return new SortingVariantContextWriter(innerWriter, maxCachingStartDistance, takeOwnershipOfInner); - } - - /** - * Returns a output stream writing to location, or throws an exception if this fails - * @param location - * @return - */ - protected static OutputStream openOutputStream(final File location) { - try { - return IOUtil.maybeBufferOutputStream(new FileOutputStream(location)); - } catch (final FileNotFoundException e) { - throw new RuntimeIOException(location + ": Unable to create VCF writer", e); - } - } -} From c795101d3e10c0c6adc323dbcdd755b1a7d13cfa Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Wed, 14 Dec 2016 18:23:34 -0500 Subject: [PATCH 054/137] updating the gradle version from 2.13 to 3.2.1 (#727) updating the gradle version from 2.13 to 3.2.1 adding a settings.gradle file to define the project name these changes make it possible to use htsjdk in a composite gradle build with other projects --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 53556 -> 52928 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 5 +++++ settings.gradle | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle index 9e8f35154..25ca0c80a 100644 --- a/build.gradle +++ b/build.gradle @@ -136,7 +136,7 @@ task testSRA(type: Test) { task wrapper(type: Wrapper) { description = "Regenerate the gradle wrapper" - gradleVersion = '2.13' + gradleVersion = '3.2.1' } // This is a hack to disable the java 8 default javadoc lint until we fix the html formatting diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ca78035ef0501d802d4fc55381ef2d5c3ce0ec6e..6ffa237849ef3607e39c3b334a92a65367962071 100644 GIT binary patch delta 12254 zcmaKS1ymft((dArK#<_U-Q6{~TkzoSZi_A!+!qM$?ruSXySr;h2oMMa4e~aS|K30E zo%80LIo(_HeO1*m)jQqQkO%WQ6Xun&91QFW000pY@WY&4CjJ#A%Ac~SS|nB&005}P zzkP>&hq(^FCdA6snmFx2Ps_$(}dWtQ#FvAp@GoM-pF&m-OO8;l*bzF`3g!*k( zHWwqB#WAw^ihJU>&{!k-C8jsOezJ~|b1c5G_%1L@+d^&x5N%RtZ@L`!R-KT~&1CKP z93&4Ub+}*4h>8U%el_czDOvUM*6~mlnjjcf*)*Q6(eSDe8T=(j^o4)bYxX61$8NGS z7QWTa$mgh(T^Kf#PZBq#!wH?^pXWBGumyr|%#a4cdevz(CIVT45?AQXXYZh!8S{c; zE?Ov=g_BkqxvTpaWwi#Wq7J0BV1F&^f9HYA>+tmhH6Jr*yNh`7W%ax8WevV$ zNM&^Q(;AG7s6k>r0OA$9wdW;kI;DAb<(e1_t5ovtCVdvfYc|C}9=wQdr&ch)&o7E7 zR!OO%+-*6;E9;5c?KArZWaMy_?A0c$-bD%Ex7W~uHY4)&dF@~Y)EVRlY7XuKMSH#Z zHU<%()jPI91uSd3A3Ro zlvsTlU8F`-^CM;YzDK=ea>qU*u_i@2 zc6tV@*(NQTy7TtAXn5&S`Q_Dm97T=a^g~L%bkfxjh(+~1@@L{5^{l!Gp=xyvr-h{w z%B{p z>nUJh4!w6<4YxKtRqa&CJ+P-XNRzRH2pL!2#nibZd`hB689sWKt6#s@o{RlT%-b)- zq7$Rs9`q{49ypV1t}=cKcOJ|V6c8lo*T8-1kxPKI_Tdw0O?)$3pGt8N!lawEKP*9D zbZe^3ONaYC+OPVnKInO#7pOEg_SaBfHsN}7plcBqC94i|{DiG7sU#xMBesbia!Gh- z*~xfl#modYBYzJ`u$#f$MVG+$HOYwIax0MVg@d#nB`gIrl-@aShjhoR48yYocV~cqV3w`!6N|S zm@B65xWzy8lyc_!MddZT-F4QxkJE~gpBzEa;AL#+LJWD)jxK;4SB z8Wc4;9EP!$d#jS2b|N0iq*7YU*i}<2FjJOp371Xa0lJhb{4AmZH}s5|27?f{xn;@s zya{>msOOlZx0|&vu_Us@e&lWL8ncQKhwKS>vOD>FVq-k?mPcA2n>PF;b+&hi?Jo98Mz4`HB^6(Mb@zrD0wm_6v zV%4DXXUV7p+FlgAEKQZfV+Cz3I$72}kl~P`b^%UajhVZ#^7e?kL`XWFeZM1t4y)}b z=<8`MjSx{U_31FK&<7le+lGkAp}S>V#a$ORAN_tiEQ7F}NLGr|cbEnkUzkTvu~>hp z$9H1)FPc3_Vs1w5=&<^{*+l=!EcJ7prjwf5K;x$`mawv;ZCgVX4zlMug)wusMdh@a{trdxTOb`DfI zOWD?`_^`9?y7R{i<9=mFQ+vP5@Y3-9+fs5kok^T%%E6lIL@tS5Zj)rT*mQ{?DzezL zR zS*yEnUanzxY5T-FP{LJeH9K9q=eM|XY}xI2bzYlhYj#KrGH+NM7wun=@Nhk4?0D54 z?znC1Ta5+BINsn|Lef^mKM=(vE_u#z7)j_}+Q(uGDjXr!g_}{YuQ24g*#{kU1o7yY z>Q^rH)3?=&gcID(h%vS|2>1YJ9SJ%N<1}M=( zNGSP0wO#}#m414Bs#7tP#nXn6VwQ5!NGtulj%3a|L=O%DgiOsgynbLm!#;{mZ>+>u z>T6oOVaw~(3dx@?C|zg=A6xRb?)a-6Yk$&XbuF+2X>WXA+ass*%ijhyUuQ;a+FZjt zlx!Vq}4UCH}I7bKO+(yzw?NkR^N_GFlPSu#2UCON1s%o`5x~ccbQ={E~ ztud@%Tf_3YNWH2Bsf$Ch@3K59rDyZh1I6QKT1c6=kLH>i7x!Bf&NY*>poxJCAWNVg21|tALY67$!8oumi6v9K-)nv9kpwGbhvl8 zd*51MV{U$+1OHIAsp7l&7vAaw@|}?pdR#vp4?Y^&17&+F(v~2LbbR}lVxte4MzLqC z@GpsbwMI4CyDFQ{YMQ%kyh~LaZy$f*by1skT-j!7O%M{?1VopC>Th6?;j@vHBRhg(`F4lj=+rOv zn?^cH50e(xxp&!Q$(!r^P9h@44EZT4!OW*{c~?hB*xr^MXgA2o<*Wbo2&4Y1Ju$!( z&*EC$HOs*8_}c(Es09Fb^0f>_rb@WRB6BC!H_kcH+cuJbc=vlp2G+#38p=p{)_2Mn z)`_>oqChm_yn-9qYnB0?$bg@pKkv?AuBYHi7aEA^^p2qSKnoX&274mjXBC1w-3?_Z z0w_tSSn!tam<3BS9ExYu8y3+<3PY?BCksMmImMYm+;gjjKnBzJua3!@-iaY-zQ+pP zmEv&?j;4uYR}IxxucHt=ux5Xyj#)XkB7vUkYr^N%4G zH^{0FRlaM{P6Sd2CO=H;ejnU0_`dACVtqjLVhxQw6$c^Qqz*#YW)!xEQ8K5+^WCB| zcz;{nqeP7plCz_2u5!Jz#e#{w*bw3A0NbBb?yRHe@91+Vv8m2BWRUOHXFUdknttTI zB)$+RnbYc4uO7NNK)bFhWpwu%8Wd|KmFfOmD8xswg7^KUpk{wBN-Q-V9#1o)iZOoxh_LEtA+e2pSkuvMGB-QLsQ>>tBlC-wSrc76X zFO__S-*ykY4VgkbC>TXL`lPg|^`}jLX#V!zuoS)JIK05~@=fN|GIRUpU3F|qctEib zh+N_Q^?WQxjnF_+gYeVH>;CJNLUFdCE7igOehAYmHO z>Z8Q_7{4=`fJ~fhrrWLF+5Wok1*o_nsD)~~EtnQ>T`S2^Y~a(7bW1D3y%yM!KJ2Qc zb=Ai+o7p2!*{p(`o)iamcd$K5j7EA14UH!sFHMm8Gfs5n3pB8K4AM76D|j6yoW)c( z1-->^4B1p|bpv(A=uI2(>5VB_Xk*%0I*J#`X|vip0fqMVZ1heW9LEU?A3>Dr>@!W) z%VTL+Tg*T6{qv_A=kzW*)27t(fb9$0qs2#GED( zQCO~`{kC|tqu@QJ?*fNylNq&Jqnd20bqqS|ku~m^qOO6=d~J(!e4e{BwAY~zKgEx< z^N%SVM*FbH^GI3@)%6vbj-DV2-lde5V+Cf<`*HJ0`7x)_a~T-3$n0jt#U*<3s)J`)X2+75S!yl^?3vgK&HD{9i@Ca$aG8u(m8etYR@uFw*b@ zs=%?6y#B2p#|I@VDAldE2F{_U>4`I>Tk1o2!O%$n1=WC+=w#3hV+%rO@sf0Ewsz_W z;qmKkJ=emt-4%<-D$X+GG*jx9y4HF-$#gSvt5+5rtc2+kF8&i7zTuw~Gbb9mS+MOgWf=iBQ|;PLP0sle1v>UVXbo#j$!!_*@O6 z7W~E5Voe(8ssID%g_%TbRMHXj!qmLsmv?- zC|j-(GpZ8HJBhu?%pT+foh*Mus5C0+!2_QN5~mWEJ+45a3{n<9SM)1|Jhz;W9GgAb z&`U~5!cx@nq0QigQAM(Aex8$Sng9J=@e~=UhnPbL5CMSL*8l*`Q)D#Y!2yMPzlMA} zR5EIx)#1jm8k%50!3R^A{BZm*$5qsFk^7!&t|+=bdEpX6n_h$dv(-#+!}#>41}ibI z>;{F^6l?~3?p*4I1K-U>;hAc72OKL(=0v}TyNz`2b3d2%RL}Cohx4;?fcw@almPx` z6Ond3NfaW~EhId@jE|(-G8-UIDZmNxjvkZ%<*tD~eQ^3tDFt^Idw#LuHBE&YworQ}p{|gxczT-)&v z-S|QNqy-KU5(Tz>grJzhB~G3i}Is*>-TB2_Cv*Ks(Tjsp|99Pn@K4f zHe_;xlU@^DH`4-?RKHo<)|w0;-?Rt!eAR_f@7^CW-faSTEtm5&^77}fjR?iMctj>H zP|b_(Ys9XAG^I_lltone^>PvvRe7UiPP1!C(`m9%X1_0HI6yJ!OqtI7m=Fcoka;gsCXKaOFes+V~?7!05zA8OlGrOPx z!fcdm(dbui;d)^S^zFwO%Xg!mip_xCldM0cEn?mwa%1kjeONbDz`{V9Pt!OxNxnv6 zPhT6`YmjwNtYgXF;(U{zvsKJcB#`$t!4*BNa@ztlcJB$-3;zJe?l&AI87m|sa>Vr_ zwE+>6yZBT)HYIH!D(rwOX094Ds=m6*d~0*7`GwzL`DRdpgLYekT%1-) zCB$>H3=>MNqTl>}=STF-gv4gKqw*Ju@&lp`T{!izkK8m=n`D<+*e`vRd$WEAQXE`Q zyq`V?{pvs#MD`@srP<`wuJ}FAh+xK__hbnN_QG)3p8NCg9UE$O^bSbbs{&mlWov_8 zx1du4QDMjL(^0)Z|B^*?^`K*MkG5~~;vTKv8pS<6ee(_z+R5WaTi?y&ChhA;T_Cle zG%gBJIDa>@9_p7sKvMR`tpPd>ha{RQ$D9MdjIpN~@MwwdCILCESR;vU+A^oGR67y_ zg8d;|H%O-6OVW(H_E-BHx;^p)fadsSNP z>B!Bt;-{_BysZ_Acz;J#3|rWX#W4ZO&vb86c|VqK!lcnz85icqeeWy?{eZdjmTxIS zohvziHP+h+!+~_&fpSFZJgASD`9Lv3Fzj=P=<3(nU8+4xOK)_3B?iOA60ILh#!7B! zzrZt490?=NX%!xc$?C&*W;-Qb856a`huAe9iMk!t!r0k^LbsbBq%*+eu`54_GjSTl zlB}ERtgy78>4P-@Rzqppj)apm!tnKY<=)G5G;H%qdb96*=vLp$T(jw@5WggA)7}Q++bQhBJwYU1^K~hHst*saSV$RjFLM^66N>- z+Kw#DXkT#pMUZ5ZYIgmrmpx$I#yeZDQLcAg4w>?V@bi5#!6Ua^XxNT1wm#W~34X!G zKWRKqaTk=an|6?PgbyN2bcU0g5*Y1b7~A=R>d9glgRlL=7Hny~kgS6k=IPqT@BNBb zqi$P(4vlMNJOalNmNqgT<@q{f-r5g#k8uC)s5XZi9_B#~2JDL3Ldkr2@Of> zJV*!f=|5agI%YbquDd?fGsuPhtQOun*tqJv_dWUDIS79Ayk-TY_hDo4J2pp*1H*Oo z={|m><;l;J4vai?hN}jh?k4%Q%l^=>+~I5)jPh@Nz82GKmFsKcOTBb$c zx)vLX*}fTzT2j)Fpl-@S+C}x{jI>qYXfTmDw&ZkIr4=OK&5H$!-R-3l3XCng#ucjC zc?}fprpEG-?dK9G-I=|PBF;|Y$Ufgaf6+-I+(m<$oe*k}5S4YdJDM=9d3RkFSS_RT zBYAZ3zD4Z8uQ%4A>>9hXa*N;3M-v02)PMC8ru9y_KlaoFOSoGJ>rTEO;K!s~UtZnV zQNDX>2$I^|Uhe>{6NY+Up}1aNBEq)C98mvqlvj!h`CVzs!MZ}GykJiCE;*|vs}lp`94k$FBnSs)hz26^^{@~43Y+G{l7bJ`C zCKgz|KUUenjy)Vs?2hGZ^^!E6c1*{MJq$;;Iud#l8YN3+z5|OV6DW6%bgNX%K;Fqr zPZuh(%7NzzqS*U(9$d7=-v;{2InKBgeQHeQI_ zSbVUOA#Gepb9CGdM-dEtUY&+Bt;?HxxoXaHWhT@8!7c%sQ#bOEBWgzjrFqL7Wzdnm zeEI`Z0w}~_%c_lTGcyYRQJt^g&1~Nq&p^S{sob4xcQxl7d>7?}Q$XJCL5WDV-HmWn zPRQo=75;<2Fx@pcM0l53zAW`+qmELv+u<($LqwO=HUC2{=?8w!i~)8|trod%Va~gk zkJwuK+ntKqA5B>Gd18Cr&rQk9zmr!$O$^JROoIB4XCfa{kDII_eA_q|(q!?RH0BEW zelz2=0UC*hH0z5B8SG7cZW-6PG!Se3OOw-yI2Lnl(`X|TjzuM|IaDYjICEL@dX(nH zk?bw-T9daGtFUOsDbdSll3eje`+cn2$woL-nCLK7O2Z|{a>D2o)FS&MwDQ97YZDdI zcR`&qo9s6;9~HHxTjB*1tzofh{*THl?!zwtRMxZbl9qdLrU+42h-jRbZP4UdQlAh+%hk>DFO;aVp^Tq)e zh9T1WNFtr-5~DBf{e`oH<&MhAYrK+M-#s}Z2$(9O7%x1%dBqDWHMB3F-ELYq+{Zsi zA<>O~0+Mvq^Uqt`bW5{TFGIG#J?}x4Q->R3U-NvHgY|@|O{$An^sWV!Earha{>Doh zQ@_OYZ?XId*kUJX|w;k(K@+tekb% z4Mv+H2c3voImyb|Z6AUNJznmRU4)wv=MZM0sTVC183~&+=b5$1@v55co#To_iY~|2 zsaZ&Cf^rFFbqCmnBicZ#s(Sc=gngNtp85{(Xu&L!4#;$@W|L^(X9H1w7xvc#e#VBv z2NHDPmtDSH;0Gy|SDsTqIOkdrLZxr$5p3N+hT{bm@|O#J*RObUz7tbT4HM_Ovh z9mO|$g2E@uBbuJ4CKP0>s$8KVtuC(aBxj#=M6fIzm)48;GmBSo!N*x;24NmeI91ui zRN4BjDZjIE^z)CZv`?0pX4_c>CwS%vD~NlnZ3kWfgkV{NVF*I1x9y=ovMt0D%jIjt zW6K|%Kr|KxiZ4isS#+zZDUf-!U?fmpqoJ}OcIE09Ius; z+w!i5D7!DPYCwkJXn*H0Rdo6kHX_|u!~lLIAgB79%5^9XyzFhLFI1y&&kx>g!33M(8qkS_`zY+59>%6tVZmS|+LvO;1%9>Z`B)eyLF{2>RlOA+W? zU(ssXOTlEV5oFzqnT(1cS5y;dWB7$aLxF_j!+fvEBkY3w)(&k2Ww9GxMRu(>Dzxt6 z^!TBb72~>kEIx-J9DXgNYOG?FhK?=Xdv2la5ddyl-Rvi)2KtIWB2k~^nT`U`Y zxqn=T0;P_(1bZxYaqqPQk;HE(Vb0Kq<4m-qc~Q3{`@$NDsdl8NzH*<&$}sisIU)|m z2a9S~5(o`$6*pA+WE2h`BVX_ZfTIR!bz}K=xE?%Rwg82jqc)eNdDG>eNk8g7#a4m6PD*?B zk(*3wID_QaTWnA6X%5qRnzjd!be&?@(qI_zY`bveC|Mi}Pe3Y1xcW;|!r|pqr%^6d zY?ZHDZYvne6FOVh=1nQh0QbQt=zRXx$ZT$}Si-Bej|EdM^f|@c-(*SL%2JKj|0L+S zKT|*Ye#;@I)wfCd9p7>^$Je3vHBJ4+fn(y*@Z9?RytINlH#{Hv(rkwjzDaf8(o}DL zRkTI=7nJWWZYMy;jgwx$aqr3a-*gRA-7~{>m&}e>;I8yd?<0f3tnpve*N|SW)%p~;O;-Vj+ei?;4RKlSO+M8AcmswFX} z6+9K?m!w@gSm~Hl)Vr-ERZi&c^j1Empy&S0{tYaYE&eDfzn6K`KtmdBCjXdFSIYInD}?Qr?h7IM#k-``r&`^mc2E!~RE@fB(AxpqZ_9m9#d$9 z`(LLha}egXYcRhXgc%9|Ga3?uh6G>rupqzP7St3GHf8wSpW7fZmD_ludKXP75d2o$tJ`rK^OpF1ajuZ|Kx%2I2^cr3aehB zT?z)}PwP-0do?j3V~YXK?V_Ui&#d_JzvluJG$zb{^C-bYpV|LSqy+1K<^=tjVh}w5 zNQ0j)$Whzh3k`5Dk_M?eI9o6)%l%i`|IK*$KLo%ZM(1CKg|m^Vt=a#V2-&v={3ZWk z9{kzEX86A?Sq7S#=R<(=keeKG|IgJBKmY}1?Z*NuLZSUXr2dT$3z(1=IYL?^{yz*z z*$@DL{!2mvPjqt-|7pwDBQ2LQ$ir30P4*Ai9ZcH8`hp1WW*y?InH+31GWk zd}vejCk0u(w9m3ph)f*&Np{zZ`=s!nM52GJFXBFvabJUV`;ea%*!JN=!;n15GyCwK z6#R40`i}x#_9vh+J3jbu5c#P;{yBU22b>IM>F0h@_@B>oT*#;TKbSm`CxsdPJvB%7X@sfjtjOL zc<$*06WKR-kYf@#Q~-eVAA-C36G0IQ5nMNb@udAf!4?AiceYLFfwK)L!AOJ1B!Aw& z|Hf7T0MfqyTnzz?`e0T=e6Z3W^3&T7HWwi4?Guv$~AvglC{xCW5AE$+JEFT9EAPWNI%@h0w2n`k)CImZST5 zd^9TkWci=K{f}js2#7**NE~NHBjy)Sm7{`a!&3=Lkj}tvZ zjUY1Ryg&XV48wyf#xb5u0jYM>3?Z=q9}>U#|1rg02pL+zKQGENpMC-#`luA*H`9L_ z-t@V~B?Z!rWso`l?=YNIfO9A0{`NnF_@x?R7&3l!sDBhbSr(hd-v18iF&~I!fqyJZ z)IND0GWmC+)K20826uONPmmBGNN@-m0{J%N{oVKG z{`KuSd!|qIQ%_fQ*Hll>RBtwHV*xCNssbz=3KSF)5|k%rx@-al9s2Jp;+X*cODHHP zt%R5Agx`n(-(4?Up`rf8+Wp3sQNlf6CmH@+Z`q%(U?%7<&y5BQ7+07-q7b;fKO#r? zLKuwSTBO^CYeVo*P(M(>6iC7#1!r6f(q9f4^fv7dR4=Ji?V&;&RfB*83qdI#O|A9w z(2L>mTgLbd+c#n|l@$Ca#SS0b&kwQ-xoB8*5r>IoWJZF8b^r`cIOqAm|NroNY3RDIZx-< zMqH3CrJGW@a5F9tY8p*G^IBPvoD5vs@I*AT;Ant8udLqvGyH`#=|>zMsrVSHdjKwrux0Bf%~~HJKwO zsEWuVw}4O!8NZpYB15VJR7oi=FJCsD*`C8=zXZ^56TfP*4YQ<3Q6*QRC;36^g=?7- z$;&vU+7VS4T#>Ae;I4JY%^mr|^DlSMks|Ajv8N|4*tfy7hc0Md-AV7M7MR^#R0c`y|Z(g&7v|Os!9+^`_aoAu-OKIB0wnPMUt1$cl)$5?0K$GbAisj$8IJr?x z-p?k%OfCMxq(p|^n&GVFzr{wPkVH7 z12Pjkp$*rxAL7fBQ=l~fNr>NEs-pacgv(V_>01}psy5-$L6QWJVL{5z?3loacYPVY|{0j9qD^;G#>C$Vn_ zNh6U+om{S{Q!32Emh8<9=Il*l(9*TdtJ`Q!v!v{~xnAA6RCJw4_>aVST6BirKHj|? z4j+BENQjP0uma_zGKPkfJY=|F3~c8lsuJMy){K|mAWL1T*w_}JM}^O*Bsf|mOfr;@427<3n-~hnd>ln)J5jb}xCrxHqMu zQa(_6nJwK9;j0M7PhFWi*m103k7KCKSD!_YsA=i5_v48?WF$8(K+*E=iukwr{G}S{-HJ(RCKi$F#u1&Sr{u01v z&@D`J%rlkekq1ZFn2-}X#%PZc>P=rT)nMGbzW$cX9cLTgq+tD!h?ahYerw|`R`ne< zxxpKHFYY&jDz%sN-1mYdE>Y?2p_|!$6#o5W`%f(mse3s)-< zM%G&d-YsJf!JRM09-=yhzdlOll(}NUH2MbT+%yCsa!MM-B@W9^Yv+{0Ua<(4T8?|Y zzvClksEX$5p^7CK?!@fDS)fYV;Yj`}c+UVd_(jY3M*T2C`)UKbnY9P0m_T%5W2f=J zv&^2m5Jx8Q&W$5c&3FQ?MyR49h11 zISrs3?)lQIs+u26%B!t*b~H{*XYC!8gJG_?z*|2!n4|{;LVd=;Mri-9aWLJV3|}`h zE!-e>@t@gzp5*7=%xwAfH1o(0oi*IEEfCpHo;9RSir_fN*hML+7Pbpi{M2k-hcguA zG6+(OCwooHfS|LdPO7$^bn=?(gLECih`29pY}`1=>OvijB5Hfb#TO_?I zNi*=BTVnwHJ#|DTe804W;jLLJ9<8*aHGh-68aL`EMgRxuKK?OTqvFVtqNg@?yzLPe&8Vww)Z**`fWkbu;%TDpxM0S(pAtC z_Q2wZ`~+&dXca4-`vvrvfEAL(Cy8pl2`8( zpKf(cwC`BM`;7t*Vk`3obX>275*=FYKFytgG+uPp?x6^iRAT^DiFWcGw8M1>uahZX zV2`}*>tFp*@2)}d#@&74VxE+%>lip0mS{E|H2xrXgchIKo|&CKd4f}4^mZ7;=9u86 zkvXiR>jCcqATti_JoY@ziD7z>R97X>s+YehV9PZBfq`*7hKx_>tTyB=LOv9yiMBJs zlrzAK|JF-?NP}oc>a(NXkji;vBs|Nd{7L~4ka(!mvGT6`;3wJ(E9rVBf2vBDXG;n=jQ*(VJEV4&I`ZTIzZxsPlzKz}~=L(k{ zN7yiExsu9%3?whR3&%Tvd6IKP;hv_G>nhr!RoFu*ZEQGV)mDfrT@;DcibMenMl_= zY0r}|&Tm})l4!;4#dOm0%-&PQZO(^>0j~gKh@Yv;vwGeFlPgjA49_geP8HcdEwg&t zb;0pna0h<*{^Rh@ol_4{_ZBdh(*)7b7~`#G+)r@rve9_Ku*n{)k1sT1d{ zEfBtd53KR2px7qbM{}bbJZtcmuc!%uMsp>Al?)7IC}V7Q;f$Z|j18+u_6vchwb>Q8 z$y;b}`?!L^5t{eIr7i)AEpcx2$YQI0T9ns@}-s8YWUFBNU92RkZzG`b(7M}I!^;RQQ zKj)BYdmV)hsYwdrT(FjW0O9e%8}46Ayo zV^}izM#U*#Xpc%&MDCndl{?S{Q!BXBDsZzygn{7!$Km#T76@JzTV8lAJ%SKB75aWx z6rVOzCCSCt599`G_%;If!`v8R&h8G2$A0XG>$jvYaXERwLfI@HgcGcH(_Gz1tDF&x zIo76@C7hBhhDlZz(S%By1V6-lKT2*=8o@))+bt3lG@0XIrn($TJQ#*>AEgi9aH%hK z(so+sn8rw3dndJ#-~MEHYzJ!t9V#TYx+xI& zE7e<>iz`VEOMHfGaK0wrL==2*tvw}i`)X6voZ0KFJ&Ep%?>5i3Z0zdXPhan8Fm2<- z6iUQoYF5~EgT1pTV-G{gRwaZ&d{v=0gh6Cy-*rdAlbd|;#((|KVXXn1QVaL_;I+}K z7%>RhgOicLc^Jgt7;ZAq>osjG(f)e)3Y=bhPB>>6yv!kG4SAA09^}wkqD12qp`xf- zcIj6sWh}S{DQq?mBtKSDyZP9|YJ8P8AJ~5^g62G`&4c+bmoPok(}mwoPj;C--p@F( zLw5#JSlqejNV8kgcCwpjOEd48uMc!aL$aNTTo@uJ@r3Tyz@raHuYJkL>zS(UyY3XPYW<2`R8F! zF2jnP&@r4-#>{P#d=1Ns4%xI%Y3Z8R=u7i=-IRDIhOEq#q})|*JPzfI*;2XshfiV+ z1)fR^)2oeo+PpL%$I6tc4|33-S&UE)Fdc?&7GZfSDy4-0Ev!?rD(yH^(bG0UnQrH0 z+8NZ1iyQ=$9aQ^MXeMn2{<<%ND-AVSC31PtGs#xD+cTzy6?unbiJVo4CS$8>hUGPX zI)|wWQ$tVD)sbOiPF+t-e&%Hi&!$5lSew(Ek8KyKw`W%Yt0B z1#tp52A=}}NNT-8>I!CORMcvw4mi3MF#CNGw<_|O(JN9CT4{g#VeOqJGO`Sc*ucJ& zc_`0p=t`4A>^3UA(Ka*u%sKCc>p&dAK%8_KIj*-tKR-(Mkf-MHcc@Cifn~1a55_Q- zR+CvdD1Z{O#qwi5)YT4_19c<>_|gP1H>X7R60Q`cHdZW=%XpAAC#3Fao%+Gs7k;nDgk>Xjk=mXdU`7vrle8c(&6 zlS*w>i;| z_iD>FA)q^wPd&H@jMScEFhgPjG+0ugrS2-YL@>yCYM|fHWGv(+UMdNHNxo)Ve1na> z&dJJEL8(g<$BUFBRsGEucIDvLGUn6wmtR<8hCxS-QS&Ub$7Dib=RpMS2ZeL{NW!x! zh1&pQsG0@EI9#gw?eJgp!V!B@`3Yu=RayODEKqpbdGy}a)Bxd&z$BSRP5}KN?kF^8 z+zL#A(b&#>2>+LBBl@G^kWzT13y};4U+CPIIG}!R#fOwBLJWP3(h+3bJm>7jvwn_h zQxMw5)zqvc1ymQtq&J~FG`&$M=hv5;v=7)p#{-hl^N|mvk|C{8Jhxj2e#u^AI=GJ8 zO7#Hdcn^1{1jj5|YPrw!>{lwPqZ^!3S96_ik;{X%Jtssb-fk>$omn^0Q4XfJGY4cg zxK6GW-Ub(lmj^$Qy8~`l_6yD@dO+C)%%)pC`n0}5r)F0_U9 zT<^r?>?{=>q%)NSQeJKMl$8RO;FHO?@Xak7v zKeG?Wz}}_#QpBGOa7`d^FmEDScmV^#cZC@SMjpLr17wCGM?xNP#j|2nBD3Qrjy1R* z+@Nx?DPr#~Le4>?3E2DFk}cWKxq*-=OnJRlm1BPtBP z9kfuwS(T|Rp~9@YmzGn9GZve_E;Lo6|7A5pcK(SeoZG|=i525?VG_@eY*KfDn3-W* zI{&SYzRD1&$tAN+#IopymLBiJ5%*+7EV4!v!*#4*i8OtKS?TwaW^c%TO&5)axpd5_ z2yja$Z)m1fj1BXC**7$GuZ^eUEg~xVaIG8GU)I0kF!)W^-H5sv)4XKZ{q3t*1(BGv zl=b{{aefAMuSx< zZcFBXUODLFWdyyZOfbbhT-8*@ow+JWE1bThf8pO(rgD%%h#AzE+{vOjJO5KceYs{W z9=AgwlU=l(4iCJ@%u1ck(!OFrL~ycD^1yo`%MDNGGFzIC`>LqqJq94b`4yA3K#l}_ z(~i_vibCnAjQ(inkIlpyS8a*sZ3)JmD|{3Vp!Igsn+nHVL5{qLq_>e-uRj#EcFRoA z?mwKgMI|;?mX!sR8zq>mL^PmyG6L6*-hVJ7CN5dZl`S1>`C`vuyArMOs|o&kh3yjm z!_%*1dBRS{>b$mqO{z76dcSRSB9 zb`W1EVMzGxY?|{zhMR_fW4t1}_4HuGNlPSVGopX9!In@@l*wbq zf@!!`VW`-vE;98?F%!lYd3;79rl)`x>U!%MHHP~7Iu{Hgh1>3}aznXNhU6phmP!?O zQO-ZL_>MX==j38it$DbMx1D06GqpsR?Lmjh5#NA?`NDuFwvJz;YtC@LH29wANO+}u zcoL5JxW-yzQm*RHyQzCKhLh0kJ9@93@Kf%7P+ZcJ%^C!O; zD0byuPt!q7N+(+ha-vn?w{(eP=dI*?*NkTD_$BBxOXTG8hiLBvz#d9q$8ozVKXi>N zo1uN3akws5{1%j_iLUFzHd7+yp&Aeqba8(ToK~?q$~ivA&&UcUg(RjP^(rO?paQ-d z29Zfe+bpRamv5VYuUcl#vYDx476RT>miMu4FoO@6(kDRm^tQ-E=@fzV>O+ptvFD<1 z)UK4qDvMd0;jvBTda}+X2D4z+M3>jh^=EbEIN;~*qsR>+O`~kK&aza#{TCpmkjj!T zLaEyplJ;yB74(MDZK@$j_j8@ohmkO!4TQI}f26$@&sj}Q^H_vmc+>yMC8R?b16%Nk zmygQ^@ZhR4j1n0!Oz&R4L+48IeJ-^=Yl8N6`L$Qy%I?QP(P&Dck8b3xwdla(@2gFEi z=E9%u;5|K5!C}1;Z8l5OOwvSN29saRGRwyK93adKi(HKrdHNh^$n}hZBx|H(9X+Nc zUE9`-KA+vBu88$$l?8;kp2|08!C}e{b;7VPYXCZ>hk}MvNcMH7 zxnFX-#ZF{td^9_XPq#MFGNxt_hVGeKA{e7~Ce)tHH)X-Q5)B%!o0q*3FKf`fOgo6d z)6xl5kFCO+mnIStv0XC&t(fnXg?6@)HjR7a0C&di%?Y&d43*{>NQdWTk4!?SS;N(E zZG@v&2>|X4j&?SFSndgkt)fvDdMqua`S_P(1^xug4FYp|cFxP%pX?X}?HGjZOiVrG z2`aOPG_r@JGhJdDnwRzL>Q^!_^Gyn8!f3X8=ct_*A1voCKR&vF%+OE!MRoUK>hKi= z9RuQymITO}1cKFG;4*BnRA&VvP17}DcYS;%z&9}H-3q<0A?5n4S^}G7htMcwPQTUDoKv%XR_RIM-AVeJ>irY#k~$KH`+|psjrC@eUp9 zqh$WIp1nX&@mqmxy$*i@tI6$2VtsdQ6AW~enpqx zB%QW|$;L&_RzTOYM|0T5LU!;d|89TyQ{)gPwLM;vARBK^3WUA!Se4Iep?rf|agfH| zD3&2LqpaRkrDyMlM3`*6Po;UYc51)InA7xLqp>C=#nP3rE+rI!z#kcQX#nfGp>>DF z32YX&+m-spK*?!n6Y25Ds^EA??DEXQUG^OQhQt6({@nJsC36%8Abv0NrQkx|lxpiW zZ}?|U7+)65BTlee z1h4~qiy6YE{yPs*8g} zv3Z@J`4@aZa28eEm8Y+yLhid9r1fT7GU{J;1SRPz=}~p2ldtzjUGULQo~-u_1e=*n zrwnJY##FO%y~9;Ojb&yu`6zv~OzI)u^7! z&DNey}fE8`M0J}J{J_sL-%M6 zVRR~8`gRz6rb_j!XvI8JKX-&byV&tZ_Nj^7^~w#pt<(+0EYl5ZKp|IEqiI_Y$7H&T zr@*Wz8w%Gd;-u=K-@S}CAipsikBcii-@yj8fi(LB@hOJPn8-&)G<`{PRWIa3^1o=H zh*g<#=V)FK`!2`L$YsntT)@K{CFL3=hO3MXuq#MI9*^^wex4-sKzk7j2K+MMUOqTcP+@7hv{am92W`HFZ_)IDT6yzE)Wco^<2t(v zYnmD=3q=P?`=vf@Zho3e4P?uNpQ2$}2gJx!Il!PE9U{W{I_z;2C%a4S41Zqyh)pwB zN)bVd?C^~9 z4;uy2xrbjWH5VU6U|&|A3lOLpPIp{~yetpK<{c0&;0!J;-a!V3YcI;mzYj>{P^uJ_ zU+;GjP2jn!6M8)(%K~uZ9vQy6tL5>?jggd=!shl5il*(eXoKr21*dQ}v(tc>qQuvD zh`oV(t@su{MP9AChBX|#f0Hw5qChBkQ~bf2MFck0L#Xpd`7ztuXb*zn$pq~3W2rZ4 z2lbe8I(rgA%ESpt7C1e_s32#)6t{OH4y+n6?y zPkOg8I;+yFU)=c=R!fdZjyw51QI*^_(T}v=?P$mBi;JY)0J#b>IVrON#OU|fWG!6Q zd=K`l{ELPHN|%$HV(bNNt`a9rQ_iOoiJhoxEbz9i2rITkeXIb-VI@E;Xp3IE5Y>@8$kM=nUde6|=`?m&pU%Z%XXRSzn#W9y{-QER< zz1UVkdHS%$e#Z>9-IF+(2N!PqXbtPxIs-fff^22CG0;md?9mBMHP6}6e+`4+QhZu# zG?sUDqY0;XYBxlPs&Gk$w%9GmFOOrMh^U2xWU& zg?Op;!A35MggEz&LQ4$31dDNnMJeKxJ22B~yuiFe6vRPZ2N{r5{2zq&`bn0>5I$1b8wfqSAI$dl99*`Pc#kWSmQ|3oe zFgv2U3$u`=IYu!+T#99BP)glsQ!S%B!oswzx0z3roWE+eR@^h16I&u7Ek`kSp&`G} zlKN`}iRnHPnNpfrK2qj7wx~^r?Fy}3rd6@cOoEy8;QSy;ow_`ljcGC-A=(;$8{?M2 zuAZ1aE1RYDZ3Yzy9q4O8NJ%A}mh4bCWAS`tkzIlGhq*z0HRjp~k#&9C$h9H*BvO9r zxk5EF0lb*pa8LvFA&**|onDUUI4aGGWr-~Rn&g6*%Py~i842E8S%iB*#EM$HgD!6B z7e1*Eskb`>LR+0fuhgC5PpAmERc=O{-hT&NDe}i;wEwv0`3#a+Sh{tcxiH2c5M6M0 zi>+VW;bJFW0#`*OET&<%uodZt)np;ZO3SDtzxlxHZNa}u^yw1ixZJFJ*((-a&)j3q zj2XcB;ka5?bffMP5q>D!46Zsurf_t-f4sF=%!)m{ENO|U@=YEaVd`*o_c>79!kZBB zt#F4%mB%|}tY*-+s=+wS3GaoD%v}|hhLfppKKFI!pr?{0wF_+MF90A}Ur| z{;vZ}KWfiXg?H8uYlJMsh|F}hhqCT(fA0{UfJY~1dE0TuMz5-fA%#CViK-HI?`>h# znDfQKjVmA6kjlDPdvoY&zKApIrI=bMPdyeq<^CO|aD@Y;Bm6==!-x;o0emu8m$0m7 zkM#ldq~v>so~R!pINFffHXCDqTlF}yjg$1nXiF-0;>D+w5%;~MpA|En!zeP!hUw+} z-2B3cv0+<@gqf4Kr4@s2L7_#39aAeAr;x>OCEi>tnjox7tl?Jg+ z(|ZDrQho%3UKvvnEf9||iv+oh1`jjGM~PaW2%9s+^B#sAz5HDE=1j^n=VD_&+I7i2 zXJ1|aLQ!07D*XK3dq%svKbV3BJ0iW_f@dI>EY(pY#ah}XWHXG8e`m`7^FQ!XfwLS_mfP|i;TMlI+jN^8Y4EU`Z2 z_1NHux9P2_H?rySL`3N?=B;vsT1fU4FtY3ebU7oEagmfvmQ-h71TNvWdZF-sZK989 z;!Zh}4mbh``LH@v6%QP$g(9%X(69{&9ZPWEfEFBd%tX5yuo3c0Ymr+_xgBLt-vm_& z1T$X{={uao2$NHPRulG(yA%|Zk0aQkdUa1Hc}Yo;Qm88{c)Noz_`Zo^gfmQ_%U`t1 zF;Rg^&_KZ&2F0Su1xOZ}JI{A9mqOjby?6wt%tc*lF9@^obFXq5-7Tb(;JhWJJ4@n3 z6(pJG2y+e23$8R!H6ySdrYhD%vzCDuI!GjIR#PC=ZcUKyeG;-HDIO=RXFliRZ-^YL zp1b$tW9SiTxUBqmgF3pshZdzxSTn9I5aa&S4uuD?-^PF|@Vf1|z2T#l#m9^5)B29o zYOv2)b#E9CUx|wiAho#8X2uWrEu()gA2f_?;1bKF5A%BdqYq{U9fx!A8MZx%{>NG2 zh!b3|WkH?vWn=^QxxYHK5Gl zk2IXiC%bAo3!ezQ!`p+p-@ZF;=;}NW-5>R%Yo6_!A9J{2ugxCYoI2aMsWF>D_<{SA zsbG(e1zJn7C|Pj`Q1X_>kEA5E#(1%X!@-l=*8)Q1gychg&j+5K!hpyP2wm3({zhM7<)@{g8Tu1m* zX6U1V@Tc;yAw)|P3Fmk9$V&Exks747LTDNRgHu#8R4 z?5N*N&1g8Y0M$9Tv^Y2#xOf`5xU`);&@t3FxHx!rn7~+XNGNt;{_E?SF@zg}_hQi1j#Kwc^N4+u@8zEXQkLYh{G$tf&cvX^qpU}{;mZ1CTsCuE=EgB$GQgE< zlsDW7i)Cek%^%a|@noohT~HnaQIwQkUNgu_VT}KBYMsA!nhZyUy;%S4FL>__Gu-b+1HMr>%ySEzXL9gI6m9zSN7N8&aqv&e$2t-E zPcN1&ITH47RZ=^Gx+zGp&l&<06hGL+offP*iO^ucl;vbpl``LeF*pU_gu=PZj;{55g%{~7|_NHL- zpO$!W6WpKPALTHx&#n5ZGni*P8Q^5mh#~801+kS8VkcPxSuGdzbC={C!QLS(6qGM2 z6qLws7aBt9F#jP+ZJPPV2-_Boe}?I6ON2%L?b7USVTBANUZlXrUGzHte__a>`d?uP z9G{U_Vqr)| zNCrmg!TTel>;b?)!#vCMdUz>+)9)YazO8_eu0j6P|HlrwHU@gaL4f$+QaDVO|FsFO z+-Ji8;)WW;1=hcmOyHlDaC_;W+5fFIhJxb$3y6gD40P;e`D0p6F93!d<5@QIkMdtN zYkw*KzliRU%*xTXW;ujqG#ZL>ew)#py>YsIuJbrX9v*1+kH6CV1dug z8zi_;P{)i=P?Udxhbf-H0RxC&j(!}9-_!W-xBdT}5el$LzsNJ;zaPb*pn!i7vI#sB z&h#RJrv)&;2YqPI3jg&A0gCA_1sTa_1>gY9pV?Cx0KkYV|6>)&A6f1I0GbL+IY@V66fmKnsQ%(()`94T z(?8$H;G;nR46z9qafs|$DSq@SMFbKx8jzI2`L7Nn)MQP-D#gTL!y)=-AeWIvTnPkl z0QvttQ43~ZlR6sk@DT2w5jKVZFg@navMy5+u>ANWqYiME@G8&*~Wr zNqFGsVd`h_0S)@43?$7cKotHqtI8G(T}ui+9H#j*8ukbPM$-Nd!3gk=%oifFaQhFb zjVspg<@?jO1?da-eD0$hW%?si9tFS*`8@M>dZB^8_5QIq%idt76kzFvB6@ae-vJOIi@8+N-{^V|2LG=g1|@>)L`kcKWP_iFb05ejsz>b7kLh>-^<5k zuhT3KaTg9UKhl4V_A2^$G}!S!>l|9#NN9xv1vLx_zrSyo%UB4I8hjRw0QMdK$KU92 z08DoNGr`0-`5%8zAhLv_|G2<4LG;f&OaNfqN+HV*sk?!M`){kj*`4^E5UVI5RyI+_BmjoH<{#4kTL_4Zz8*5M%>N<$YnS+oFsuQhECyzs`iD?-3IH?S z0@j<7dA5WV-#Q2pVo3)izj6JgnBDd~YG%v7D Date: Wed, 28 Dec 2016 03:16:52 +0100 Subject: [PATCH 055/137] Methods for default sequence dictionary name (#774) * added new methdos to ReferenceSequenceFileFactory * using the methods to simplify code * address comments --- .../reference/AbstractFastaSequenceFile.java | 27 +++------- .../reference/ReferenceSequenceFileFactory.java | 62 ++++++++++++++++------ .../ReferenceSequenceFileFactoryTests.java | 18 +++++++ 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java b/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java index 86f11fead..badcf1987 100644 --- a/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java @@ -93,29 +93,16 @@ protected static Path findSequenceDictionary(final Path path) { if (path == null) { return null; } - // Try and locate the dictionary - Path dictionary = path.toAbsolutePath(); - Path dictionaryExt = path.toAbsolutePath(); - boolean fileTypeSupported = false; - for (final String extension : ReferenceSequenceFileFactory.FASTA_EXTENSIONS) { - String filename = dictionary.getFileName().toString(); - if (filename.endsWith(extension)) { - dictionaryExt = dictionary.resolveSibling(filename + IOUtil - .DICT_FILE_EXTENSION); - String filenameNoExt = filename.substring(0, filename.lastIndexOf(extension)); - dictionary = dictionary.resolveSibling(filenameNoExt+ IOUtil.DICT_FILE_EXTENSION); - fileTypeSupported = true; - break; - } - } - if (!fileTypeSupported) - throw new IllegalArgumentException("File is not a supported reference file type: " + path.toAbsolutePath()); - - if (Files.exists(dictionary)) + // Try and locate the dictionary with the default method + final Path dictionary = ReferenceSequenceFileFactory.getDefaultDictionaryForReferenceSequence(path); path.toAbsolutePath(); + if (Files.exists(dictionary)) { return dictionary; + } // try without removing the file extension - if (Files.exists(dictionaryExt)) + final Path dictionaryExt = path.resolveSibling(path.getFileName().toString() + IOUtil.DICT_FILE_EXTENSION); + if (Files.exists(dictionaryExt)) { return dictionaryExt; + } else return null; } diff --git a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java index 5978072d7..2b0b1e7fc 100644 --- a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java +++ b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java @@ -24,6 +24,8 @@ package htsjdk.samtools.reference; +import htsjdk.samtools.util.IOUtil; + import java.io.File; import java.io.FileNotFoundException; import java.nio.file.Path; @@ -113,24 +115,52 @@ public static ReferenceSequenceFile getReferenceSequenceFile(final Path path, fi * @param preferIndexed if true attempt to return an indexed reader that supports non-linear traversal, else return the non-indexed reader */ public static ReferenceSequenceFile getReferenceSequenceFile(final Path path, final boolean truncateNamesAtWhitespace, final boolean preferIndexed) { - final String name = path.getFileName().toString(); - for (final String ext : FASTA_EXTENSIONS) { - if (name.endsWith(ext)) { - // Using faidx requires truncateNamesAtWhitespace - if (truncateNamesAtWhitespace && preferIndexed && IndexedFastaSequenceFile.canCreateIndexedFastaReader(path)) { - try { - return new IndexedFastaSequenceFile(path); - } - catch (final FileNotFoundException e) { - throw new IllegalStateException("Should never happen, because existence of files has been checked.", e); - } - } - else { - return new FastaSequenceFile(path, truncateNamesAtWhitespace); - } + // this should thrown an exception if the fasta file is not supported + getFastaExtension(path); + // Using faidx requires truncateNamesAtWhitespace + if (truncateNamesAtWhitespace && preferIndexed && IndexedFastaSequenceFile.canCreateIndexedFastaReader(path)) { + try { + return new IndexedFastaSequenceFile(path); + } + catch (final FileNotFoundException e) { + throw new IllegalStateException("Should never happen, because existence of files has been checked.", e); } + } else { + return new FastaSequenceFile(path, truncateNamesAtWhitespace); } + } - throw new IllegalArgumentException("File is not a supported reference file type: " + path.toAbsolutePath()); + /** + * Returns the default dictionary name for a FASTA file. + * + * @param file the reference sequence file on disk. + */ + public static File getDefaultDictionaryForReferenceSequence(final File file) { + return getDefaultDictionaryForReferenceSequence(file.toPath()).toFile(); } + + /** + * Returns the default dictionary name for a FASTA file. + * + * @param path the reference sequence file path. + */ + public static Path getDefaultDictionaryForReferenceSequence(final Path path) { + final String name = path.getFileName().toString(); + final int extensionIndex = name.length() - getFastaExtension(path).length(); + return path.resolveSibling(name.substring(0, extensionIndex) + IOUtil.DICT_FILE_EXTENSION); + } + + /** + * Returns the FASTA extension for the path. + * + * @param path the reference sequence file path. + * + * @throws IllegalArgumentException if the file is not a supported reference file. + */ + public static String getFastaExtension(final Path path) { + final String name = path.getFileName().toString(); + return FASTA_EXTENSIONS.stream().filter(name::endsWith).findFirst() + .orElseGet(() -> {throw new IllegalArgumentException("File is not a supported reference file type: " + path.toAbsolutePath());}); + } + } diff --git a/src/test/java/htsjdk/samtools/reference/ReferenceSequenceFileFactoryTests.java b/src/test/java/htsjdk/samtools/reference/ReferenceSequenceFileFactoryTests.java index 6eeae7b99..9ac2ea343 100644 --- a/src/test/java/htsjdk/samtools/reference/ReferenceSequenceFileFactoryTests.java +++ b/src/test/java/htsjdk/samtools/reference/ReferenceSequenceFileFactoryTests.java @@ -1,6 +1,7 @@ package htsjdk.samtools.reference; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; @@ -36,4 +37,21 @@ Assert.assertTrue(f instanceof IndexedFastaSequenceFile, "Got non-indexed reader by default."); } + + @DataProvider + public Object[][] fastaNames() { + return new Object[][] { + {"break.fa", "break.dict"}, + {"break.txt.txt", "break.txt.dict"}, + {"break.fasta.fasta", "break.fasta.dict"}, + {"break.fa.gz", "break.dict"}, + {"break.txt.gz.txt.gz", "break.txt.gz.dict"}, + {"break.fasta.gz.fasta.gz", "break.fasta.gz.dict"} + }; + } + + @Test(dataProvider = "fastaNames") + public void testGetDefaultDictionaryForReferenceSequence(final String fastaFile, final String expectedDict) throws Exception { + Assert.assertEquals(ReferenceSequenceFileFactory.getDefaultDictionaryForReferenceSequence(new File(fastaFile)), new File(expectedDict)); + } } From 1dd11be8b07c027c6c2864b29bff523ebc9d6729 Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Wed, 4 Jan 2017 22:50:50 +0100 Subject: [PATCH 056/137] replacing uses of getRecordAndPositions with getRecordAndOffsets (#715) AbstractLocusInfo.getRecordAndPositions was deprecated in #637 but still had uses in the code base replacing those instances with getRecordAndOffsets --- .../htsjdk/samtools/util/SamLocusIterator.java | 12 +++--- .../htsjdk/samtools/util/SamLocusIteratorTest.java | 44 +++++++++++----------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/SamLocusIterator.java b/src/main/java/htsjdk/samtools/util/SamLocusIterator.java index 7a60756d6..dc6745f5d 100644 --- a/src/main/java/htsjdk/samtools/util/SamLocusIterator.java +++ b/src/main/java/htsjdk/samtools/util/SamLocusIterator.java @@ -268,13 +268,13 @@ public void addInserted(final SAMRecord read, int firstPosition) { public List getInsertedInRecord() { return (insertedInRecord == null) ? Collections.emptyList() : Collections.unmodifiableList(insertedInRecord); } - - /** - * @return the number of records overlapping the position, with deletions included if they are being tracked. + + /** + * @return the number of records overlapping the position, with deletions included if they are being tracked. */ @Override - public int size() { - return super.size() + ((deletedInRecord == null) ? 0 : deletedInRecord.size()); + public int size() { + return super.size() + ((deletedInRecord == null) ? 0 : deletedInRecord.size()); } @@ -284,7 +284,7 @@ public int size() { */ @Override public boolean isEmpty() { - return getRecordAndPositions().isEmpty() && + return getRecordAndOffsets().isEmpty() && (deletedInRecord == null || deletedInRecord.isEmpty()) && (insertedInRecord == null || insertedInRecord.isEmpty()); } diff --git a/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java b/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java index 262b7c93f..5bcea40df 100644 --- a/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/SamLocusIteratorTest.java @@ -61,7 +61,7 @@ public void testBasicIterator() { int pos = startPosition; for (final SamLocusIterator.LocusInfo li : sli) { Assert.assertEquals(li.getPosition(), pos++); - Assert.assertEquals(li.getRecordAndPositions().size(), coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), coverage); Assert.assertEquals(li.size(), coverage); // make sure that we are not accumulating indels Assert.assertEquals(li.getDeletedInRecord().size(), 0); @@ -86,7 +86,7 @@ public void testMissingQualityString() { int pos = 165; for (final SamLocusIterator.LocusInfo li : sli) { Assert.assertEquals(li.getPosition(), pos++); - Assert.assertEquals(li.getRecordAndPositions().size(), 2); + Assert.assertEquals(li.getRecordAndOffsets().size(), 2); Assert.assertEquals(li.size(), 2); } } @@ -123,7 +123,7 @@ public void testEmitUncoveredLoci() { } else { expectedReads = 0; } - Assert.assertEquals(li.getRecordAndPositions().size(), expectedReads); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReads); Assert.assertEquals(li.size(), expectedReads); // make sure that we are not accumulating indels Assert.assertEquals(li.getDeletedInRecord().size(), 0); @@ -161,7 +161,7 @@ public void testQualityFilter() { // make sure we accumulated depth coverage for even positions, coverage/2 for odd positions int pos = startPosition; for (final SamLocusIterator.LocusInfo li : sli) { - Assert.assertEquals(li.getRecordAndPositions().size(), (pos % 2 == 0) ? coverage / 2 : coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), (pos % 2 == 0) ? coverage / 2 : coverage); Assert.assertEquals(li.size(), (pos % 2 == 0) ? coverage / 2 : coverage); Assert.assertEquals(li.getPosition(), pos++); // make sure that we are not accumulating indels @@ -200,7 +200,7 @@ public void testSimpleDeletion() { Assert.assertEquals(li.getPosition(), pos++); if (isDeletedPosition) { // make sure there are no reads without indels - Assert.assertEquals(li.getRecordAndPositions().size(), 0); + Assert.assertEquals(li.getRecordAndOffsets().size(), 0); Assert.assertEquals(li.size(), coverage); // should include deletions // make sure that we are accumulating indels @@ -208,7 +208,7 @@ public void testSimpleDeletion() { Assert.assertEquals(li.getInsertedInRecord().size(), 0); } else { // make sure we are accumulating normal coverage - Assert.assertEquals(li.getRecordAndPositions().size(), coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), coverage); Assert.assertEquals(li.size(), coverage); // make sure that we are not accumulating indels @@ -241,7 +241,7 @@ public void testSimpleInsertion() { for (final SamLocusIterator.LocusInfo li : sli) { Assert.assertEquals(li.getPosition(), pos++); // make sure we are accumulating normal coverage - Assert.assertEquals(li.getRecordAndPositions().size(), coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), coverage); Assert.assertEquals(li.size(), coverage); // make sure that we are not accumulating deletions @@ -278,7 +278,7 @@ public void testStartWithInsertion() { for (final SamLocusIterator.LocusInfo li : sli) { Assert.assertEquals(li.getPosition(), pos); // accumulation of coverage - Assert.assertEquals(li.getRecordAndPositions().size(), (indelPosition) ? 0 : coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), (indelPosition) ? 0 : coverage); Assert.assertEquals(li.size(), (indelPosition) ? 0 : coverage); // no accumulation of deletions @@ -319,7 +319,7 @@ public void testStartWithSoftClipAndInsertion() { for (final SamLocusIterator.LocusInfo li : sli) { Assert.assertEquals(li.getPosition(), pos); // accumulation of coverage - Assert.assertEquals(li.getRecordAndPositions().size(), (indelPosition) ? 0 : coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), (indelPosition) ? 0 : coverage); Assert.assertEquals(li.size(), (indelPosition) ? 0 : coverage); // no accumulation of deletions Assert.assertEquals(li.getDeletedInRecord().size(), 0); @@ -364,7 +364,7 @@ public void testNBeforeInsertion() { } Assert.assertEquals(li.getPosition(), pos); // accumulation of coverage - Assert.assertEquals(li.getRecordAndPositions().size(), (pos == endN) ? 0 : coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), (pos == endN) ? 0 : coverage); Assert.assertEquals(li.size(), (pos == endN) ? 0 : coverage); // no accumulation of deletions Assert.assertEquals(li.getDeletedInRecord().size(), 0); @@ -416,7 +416,7 @@ public void testNBeforeDeletion() { final boolean insideDeletion = incIndels && (pos >= startDel && pos <= endDel); Assert.assertEquals(li.getPosition(), pos); // accumulation of coverage - Assert.assertEquals(li.getRecordAndPositions().size(), (insideDeletion) ? 0 : coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), (insideDeletion) ? 0 : coverage); Assert.assertEquals(li.size(), coverage); // either will be all deletions, or all non-deletions, but always of size `coverage`. // accumulation of deletions Assert.assertEquals(li.getDeletedInRecord().size(), (insideDeletion) ? coverage : 0); @@ -506,18 +506,18 @@ public void testSimpleGappedAlignment() { if (inDelRange) { // check the coverage for insertion and normal records Assert.assertEquals(li.getDeletedInRecord().size(), coverage); - Assert.assertEquals(li.getRecordAndPositions().size(), 0); + Assert.assertEquals(li.getRecordAndOffsets().size(), 0); Assert.assertEquals(li.size(), coverage); // includes deletions // check the offset for the deletion Assert.assertEquals(li.getDeletedInRecord().get(0).getOffset(), expectedReadOffsets[i]); Assert.assertEquals(li.getDeletedInRecord().get(1).getOffset(), expectedReadOffsets[i]); } else { // if it is not a deletion, perform the same test as before - Assert.assertEquals(li.getRecordAndPositions().size(), coverage); + Assert.assertEquals(li.getRecordAndOffsets().size(), coverage); Assert.assertEquals(li.size(), coverage); // Assert.assertEquals(li.getDeletedInRecord().size(), 0); - Assert.assertEquals(li.getRecordAndPositions().get(0).getOffset(), expectedReadOffsets[i]); - Assert.assertEquals(li.getRecordAndPositions().get(1).getOffset(), expectedReadOffsets[i]); + Assert.assertEquals(li.getRecordAndOffsets().get(0).getOffset(), expectedReadOffsets[i]); + Assert.assertEquals(li.getRecordAndOffsets().get(1).getOffset(), expectedReadOffsets[i]); } ++i; } @@ -576,12 +576,12 @@ public void testOverlappingGappedAlignmentsWithoutIndels() { i = 0; for (final SamLocusIterator.LocusInfo li : sli) { - Assert.assertEquals(li.getRecordAndPositions().size(), expectedDepths[i]); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedDepths[i]); Assert.assertEquals(li.size(), expectedDepths[i]); Assert.assertEquals(li.getPosition(), expectedReferencePositions[i]); - Assert.assertEquals(li.getRecordAndPositions().size(), expectedReadOffsets[i].length); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReadOffsets[i].length); for (int j = 0; j < expectedReadOffsets[i].length; ++j) { - Assert.assertEquals(li.getRecordAndPositions().get(j).getOffset(), expectedReadOffsets[i][j]); + Assert.assertEquals(li.getRecordAndOffsets().get(j).getOffset(), expectedReadOffsets[i][j]); } // make sure that we are not accumulating indels Assert.assertEquals(li.getDeletedInRecord().size(), 0); @@ -652,12 +652,12 @@ public void testOverlappingGappedAlignmentsWithIndels() { i = 0; for (final SamLocusIterator.LocusInfo li : sli) { // checking the same as without indels - Assert.assertEquals(li.getRecordAndPositions().size(), expectedDepths[i]); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedDepths[i]); Assert.assertEquals(li.size(), expectedDepths[i] + expectedDelDepths[i]); // include deletions Assert.assertEquals(li.getPosition(), expectedReferencePositions[i]); - Assert.assertEquals(li.getRecordAndPositions().size(), expectedReadOffsets[i].length); + Assert.assertEquals(li.getRecordAndOffsets().size(), expectedReadOffsets[i].length); for (int j = 0; j < expectedReadOffsets[i].length; ++j) { - Assert.assertEquals(li.getRecordAndPositions().get(j).getOffset(), expectedReadOffsets[i][j]); + Assert.assertEquals(li.getRecordAndOffsets().get(j).getOffset(), expectedReadOffsets[i][j]); } // check the deletions Assert.assertEquals(li.getDeletedInRecord().size(), expectedDelDepths[i]); @@ -670,4 +670,4 @@ public void testOverlappingGappedAlignmentsWithIndels() { } } -} \ No newline at end of file +} From ffca2591b4cf268b3dc591f9f8697ee51ae08512 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Thu, 5 Jan 2017 17:47:20 -0500 Subject: [PATCH 057/137] switching coverage reporting from coveralls to codecov (#764) * switching from coveralls.io to codecov.io for code coverage reporting on * updating README.md and adding codecov.yml * running SRA tests before regular tests so that they end up included in the coverage report Codecov understands partially covered lines, which means that our our total coverage will appear lower than under coveralls which counted any coverage on a line as full coverage. This looks worse but gives us more accurate information. --- .travis.yml | 4 ++-- README.md | 2 +- build.gradle | 3 +-- codecov.yml | 29 +++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 codecov.yml diff --git a/.travis.yml b/.travis.yml index 1e9259942..9c046e353 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,11 @@ cache: - $HOME/.m2 jdk: - oraclejdk8 -script: ./gradlew jacocoTestReport testSRA; +script: ./gradlew testSRA jacocoTestReport; after_success: + - bash <(curl -s https://codecov.io/bash) - echo "TRAVIS_BRANCH='$TRAVIS_BRANCH'"; echo "JAVA_HOME='$JAVA_HOME'"; - ./gradlew coveralls; if [ "$TRAVIS_BRANCH" == "master" ]; then ./gradlew uploadArchives; fi diff --git a/README.md b/README.md index 0e468d334..1981fd182 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Coverage Status](https://coveralls.io/repos/github/samtools/htsjdk/badge.svg?branch=master)](https://coveralls.io/github/samtools/htsjdk?branch=master) +[![Coverage Status](https://codecov.io/gh/samtools/htsjdk/branch/master/graph/badge.svg)](https://codecov.io/gh/samtools/htsjdk) [![Build Status](https://travis-ci.org/samtools/htsjdk.svg?branch=master)](https://travis-ci.org/samtools/htsjdk) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.samtools/htsjdk/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.samtools%22%20AND%20a%3A%22htsjdk%22) [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/samtools/htsjdk) diff --git a/build.gradle b/build.gradle index 25ca0c80a..b29f8e3d8 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,6 @@ plugins { id 'jacoco' id 'com.palantir.git-version' version '0.5.1' id 'com.github.johnrengelman.shadow' version '1.2.3' - id "com.github.kt3k.coveralls" version "2.6.3" } repositories { @@ -25,7 +24,7 @@ jacocoTestReport { additionalSourceDirs = files(sourceSets.main.allJava.srcDirs) reports { - xml.enabled = true // coveralls plugin depends on xml format report + xml.enabled = true // codecov depends on xml format report html.enabled = true } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..98fb05f4c --- /dev/null +++ b/codecov.yml @@ -0,0 +1,29 @@ +codecov: + branch: master + +coverage: + precision: 3 + round: nearest + range: "50...100" + + status: + project: + default: + target: auto + threshold: null + branches: null + + patch: + default: + target: auto + branches: null + + changes: + default: + branches: null + + +comment: + layout: "header, diff, changes, sunburst, uncovered, tree" + branches: null + behavior: default From 2acb88b8c1a2b75ec253af7b9b6a575952b6303d Mon Sep 17 00:00:00 2001 From: JP Martin Date: Fri, 6 Jan 2017 08:15:25 -0800 Subject: [PATCH 058/137] adding new method SamReaderFactory.setPathWrapper(#775) java.nio.Paths from plugin FileSystem providers may have different performance characteristics than local file system paths (like high latency for a file remote filesystem). SamReaderFactory.setPathWrapper allows adding a wrapper function that will be used by SamReaders created from Paths. This can be used to provide customizable caching or prefetching for specific inputs. Also added new overload of SamInputResource.of() that takes a wrapper. --- .../java/htsjdk/samtools/SamInputResource.java | 23 +++++- .../java/htsjdk/samtools/SamReaderFactory.java | 39 ++++++++-- .../seekablestream/SeekablePathStream.java | 11 ++- .../java/htsjdk/samtools/SamReaderFactoryTest.java | 41 ++++++++++- .../samtools/SeekableByteChannelFromBuffer.java | 85 ++++++++++++++++++++++ src/test/resources/htsjdk/samtools/noheader.sam | 10 +++ 6 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/SeekableByteChannelFromBuffer.java create mode 100755 src/test/resources/htsjdk/samtools/noheader.sam diff --git a/src/main/java/htsjdk/samtools/SamInputResource.java b/src/main/java/htsjdk/samtools/SamInputResource.java index f25d97bb6..13ecf5dfd 100644 --- a/src/main/java/htsjdk/samtools/SamInputResource.java +++ b/src/main/java/htsjdk/samtools/SamInputResource.java @@ -39,9 +39,11 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.nio.channels.SeekableByteChannel; import java.nio.file.FileSystemNotFoundException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.function.Function; /** * Describes a SAM-like resource, including its data (where the records are), and optionally an index. @@ -89,7 +91,15 @@ public String toString() { public static SamInputResource of(final File file) { return new SamInputResource(new FileInputResource(file)); } /** Creates a {@link SamInputResource} reading from the provided resource, with no index. */ - public static SamInputResource of(final Path path) { return new SamInputResource(new PathInputResource(path)); } + public static SamInputResource of(final Path path) { + return new SamInputResource(new PathInputResource(path)); + } + + /** Creates a {@link SamInputResource} reading from the provided resource, with no index, + * and with a wrapper to apply to the SeekableByteChannel for custom prefetching/buffering. */ + public static SamInputResource of(final Path path, Function wrapper) { + return new SamInputResource(new PathInputResource(path, wrapper)); + } /** Creates a {@link SamInputResource} reading from the provided resource, with no index. */ public static SamInputResource of(final InputStream inputStream) { return new SamInputResource(new InputStreamInputResource(inputStream)); } @@ -121,7 +131,7 @@ public SamInputResource index(final File file) { /** Updates the index to point at the provided resource, then returns itself. */ public SamInputResource index(final Path path) { - this.index = new PathInputResource(path); + this.index = new PathInputResource(path, Function.identity()); return this; } @@ -268,11 +278,12 @@ public SRAAccession asSRAAccession() { class PathInputResource extends InputResource { final Path pathResource; + final Function wrapper; final Lazy lazySeekableStream = new Lazy(new Lazy.LazyInitializer() { @Override public SeekableStream make() { try { - return new SeekablePathStream(pathResource); + return new SeekablePathStream(pathResource, wrapper); } catch (final IOException e) { throw new RuntimeIOException(e); } @@ -281,8 +292,14 @@ public SeekableStream make() { PathInputResource(final Path pathResource) { + this(pathResource, Function.identity()); + } + + // wrapper applies to the SeekableByteChannel for custom prefetching/buffering. + PathInputResource(final Path pathResource, Function wrapper) { super(Type.PATH); this.pathResource = pathResource; + this.wrapper = wrapper; } @Override diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index 8769f4879..4c3035433 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -33,9 +33,11 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.channels.SeekableByteChannel; import java.nio.file.Path; import java.util.Collections; import java.util.EnumSet; +import java.util.function.Function; import java.util.zip.GZIPInputStream; /** @@ -74,11 +76,13 @@ public abstract class SamReaderFactory { private static ValidationStringency defaultValidationStringency = ValidationStringency.DEFAULT_STRINGENCY; - + + private Function pathWrapper = Function.identity(); + abstract public SamReader open(final File file); public SamReader open(final Path path) { - final SamInputResource r = SamInputResource.of(path); + final SamInputResource r = SamInputResource.of(path, getPathWrapper()); final Path indexMaybe = SamFiles.findIndex(path); if (indexMaybe != null) r.index(indexMaybe); return open(r); @@ -102,6 +106,25 @@ public SamReader open(final Path path) { /** Sets a specific Option to a boolean value. * */ abstract public SamReaderFactory setOption(final Option option, boolean value); + /** Sets a wrapper to modify the SeekableByteChannel from an opened Path, e.g. to add + * buffering or prefetching. This only works on Path inputs since we need a SeekableByteChannel. + * + * @param wrapper how to modify the SeekableByteChannel (Function.identity to unset) + * @return this + */ + public SamReaderFactory setPathWrapper(Function wrapper) { + this.pathWrapper = wrapper; + return this; + } + + /** Gets the wrapper previously set via setPathWrapper. + * + * @return the wrapper. + */ + public Function getPathWrapper() { + return pathWrapper; + } + /** Sets the specified reference sequence * */ abstract public SamReaderFactory referenceSequence(File referenceSequence); @@ -138,8 +161,8 @@ public static SamReaderFactory makeDefault() { } /** - * Creates an "empty" factory with no enabled {@link Option}s, {@link ValidationStringency#DEFAULT_STRINGENCY}, and - * {@link htsjdk.samtools.DefaultSAMRecordFactory}. + * Creates an "empty" factory with no enabled {@link Option}s, {@link ValidationStringency#DEFAULT_STRINGENCY}, + * no path wrapper, and {@link htsjdk.samtools.DefaultSAMRecordFactory}. */ public static SamReaderFactory make() { return new SamReaderFactoryImpl(EnumSet.noneOf(Option.class), ValidationStringency.DEFAULT_STRINGENCY, DefaultSAMRecordFactory.getInstance()); @@ -155,10 +178,15 @@ public static SamReaderFactory make() { private CRAMReferenceSource referenceSource; private SamReaderFactoryImpl(final EnumSet

c94RrI+R(w9ALvXGoD~7V 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/htsjdk/samtools/util/random.bin.gz b/src/test/resources/htsjdk/samtools/util/random.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..cd764e0b3f30f1a97aa45c09ac1b86ad063c0403 GIT binary patch literal 131254 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 literal 0 HcmV?d00001 From fd71c45c80747428858bf1101ba5c5d600d68377 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Tue, 10 Jan 2017 14:10:38 -0500 Subject: [PATCH 060/137] Discounts reads that are non-filter-passing in DuplicateScoringStrategy (#745) * Discounting the score for records that do not pass filters. This will mean that failing records are very un-likely to be chosen as the representative read in a set. * caping score to guarantee that discount works --- .../htsjdk/samtools/DuplicateScoringStrategy.java | 36 ++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java index 1abd51473..292ab0654 100644 --- a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java +++ b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java @@ -46,8 +46,8 @@ private static enum Attr { DuplicateScore } /** Calculates a score for the read which is the sum of scores over Q15. */ - private static short getSumOfBaseQualities(final SAMRecord rec) { - short score = 0; + private static int getSumOfBaseQualities(final SAMRecord rec) { + int score = 0; for (final byte b : rec.getBaseQualities()) { if (b >= 15) score += b; } @@ -64,6 +64,8 @@ public static short computeDuplicateScore(final SAMRecord record, final ScoringS /** * Returns the duplicate score computed from the given fragment. + * value should be capped by Short.MAX_VALUE/2 since the score from two reads will be + * added and an overflow will be * * If true is given to assumeMateCigar, then any score that can use the mate cigar to compute the mate's score will return the score * computed on both ends. @@ -72,24 +74,40 @@ public static short computeDuplicateScore(final SAMRecord record, final ScoringS Short storedScore = (Short) record.getTransientAttribute(Attr.DuplicateScore); if (storedScore == null) { - short score = 0; - + short score=0; switch (scoringStrategy) { case SUM_OF_BASE_QUALITIES: - score += getSumOfBaseQualities(record); + // two (very) long reads worth of high-quality bases can go over Short.MAX_VALUE/2 + // and risk overflow. + score += (short) Math.min(getSumOfBaseQualities(record), Short.MAX_VALUE / 2); break; case TOTAL_MAPPED_REFERENCE_LENGTH: if (!record.getReadUnmappedFlag()) { - score += record.getCigar().getReferenceLength(); + // no need to remember the score since this scoring mechanism is symmetric + score = (short) Math.min(record.getCigar().getReferenceLength(), Short.MAX_VALUE / 2); } if (assumeMateCigar && record.getReadPairedFlag() && !record.getMateUnmappedFlag()) { - score += SAMUtils.getMateCigar(record).getReferenceLength(); + score += (short) Math.min(SAMUtils.getMateCigar(record).getReferenceLength(), Short.MAX_VALUE / 2); } break; + // The RANDOM score gives the same score to both reads so that they get filtered together. + // it's not critical do use the readName since the scores from both ends get added, but it seem + // to be clearer this way. case RANDOM: - score += (short) (hasher.hashUnencodedChars(record.getReadName()) >> 16); + // start with a random number between Short.MIN_VALUE/4 and Short.MAX_VALUE/4 + score += (short) (hasher.hashUnencodedChars(record.getReadName()) & 0b11_1111_1111_1111); + // subtract Short.MIN_VALUE/4 from it to end up with a number between + // 0 and Short.MAX_VALUE/2. This number can be then discounted in case the read is + // not passing filters. We need to stay far from overflow so that when we add the two + // scores from the two read mates we do not overflow since that could cause us to chose a + // failing read-pair instead of a passing one. + score -= Short.MIN_VALUE / 4; } + // make sure that filter-failing records are heavily discounted. (the discount can happen twice, once + // for each mate, so need to make sure we do not subtract more than Short.MIN_VALUE overall.) + score += record.getReadFailsVendorQualityCheckFlag() ? (short) (Short.MIN_VALUE / 2) : 0; + storedScore = score; record.setTransientAttribute(Attr.DuplicateScore, storedScore); } @@ -125,7 +143,7 @@ public static int compare(final SAMRecord rec1, final SAMRecord rec2, final Scor } /** - * Compare two records based on their duplicate scores. The duplicate scores for each record is assume to be + * Compare two records based on their duplicate scores. The duplicate scores for each record is assumed to be * pre-computed by computeDuplicateScore and stored in the "DS" tag. If the scores are equal, we break * ties based on mapping quality (added to the mate's mapping quality if paired and mapped), then library/read name. * From ed15dc09d6d1b2002f31efa52456cef4865b0417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 10 Jan 2017 20:18:03 +0100 Subject: [PATCH 061/137] removing unused package-specific (visible for testing) deprecated method (#752) --- .../tribble/TribbleIndexedFeatureReader.java | 25 ------------ .../tribble/TribbleIndexFeatureReaderTest.java | 44 ---------------------- 2 files changed, 69 deletions(-) diff --git a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java index 514782d1e..f991936e1 100644 --- a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java +++ b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java @@ -276,31 +276,6 @@ private void readHeader() throws IOException { } /** - * @deprecated use {@link #hasBlockCompressedExtension(String)} instead - */ - //Visible for testing - @Deprecated - static boolean isGZIPPath(final String path) { - if (path.toLowerCase().endsWith(".gz")) { - return true; - } - else { - String uriPath = null; - try { - URI uri = new URI(path); - if (uri != null) { - uriPath = uri.getPath(); - return uriPath != null && uriPath.toLowerCase().endsWith(".gz"); - } - return false; - } - catch (URISyntaxException e) { - return false; - } - } - } - - /** * Class to iterator over an entire file. */ class WFIterator implements CloseableTribbleIterator { diff --git a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java index afdd827e6..2c48c6301 100644 --- a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java @@ -16,50 +16,6 @@ public class TribbleIndexFeatureReaderTest { - @DataProvider(name = "extensionURIStrings") - public Object[][] createBlockCompressedExtensionURIs() { - return new Object[][]{ - {"testzip.gz", true}, - {"testzip.GZ", true}, - {"testzip.gZ", true}, - {"testzip.Gz", true}, - - {"test", false}, - {"test.gzip", false}, - {"test.bgz", false}, - {"test.bgzf", false}, - {"test.bzip2", false}, - - {"file://testzip.gz", true}, - {"file://apath/testzip.gz", true}, - - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.gz", true}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.GZ", true}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.gzip", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bgz", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bgzf", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bzip2", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877", false}, - - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.gz?alt=media", true}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.GZ?alt=media", true}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.gzip?alt=media", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bgz?alt=media", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bgzf?alt=media", false}, - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.bzip2?alt=media", false}, - - {"ftp://ftp.broadinstitute.org/distribution/igv/TEST/cpgIslands.hg18.gz", true}, - {"ftp://ftp.broadinstitute.org/distribution/igv/TEST/cpgIslands.hg18.bed", false}, - - {"https://www.googleapis.com/download/storage/v1/b/deflaux-public-test/o/NA12877.vcf.gz", true}, - }; - } - - @Test(enabled = true, dataProvider = "extensionURIStrings") - public void testGZExtension(final String testString, final boolean expected) throws URISyntaxException { - Assert.assertEquals(TribbleIndexedFeatureReader.isGZIPPath(testString), expected); - } - @DataProvider(name = "featureFileStrings") public Object[][] createFeatureFileStrings() { return new Object[][]{ From 9c5cbf6edbac5ff2507c8ec2f0e586c9c79b748f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 10 Jan 2017 20:26:05 +0100 Subject: [PATCH 062/137] removed gelitext package and usages (#753) --- .../java/htsjdk/tribble/example/CountRecords.java | 3 - .../htsjdk/tribble/gelitext/DiploidGenotype.java | 66 --------- .../htsjdk/tribble/gelitext/GeliTextCodec.java | 117 ---------------- .../htsjdk/tribble/gelitext/GeliTextFeature.java | 148 --------------------- .../java/htsjdk/tribble/gelitext/GeliTextTest.java | 100 -------------- 5 files changed, 434 deletions(-) delete mode 100644 src/main/java/htsjdk/tribble/gelitext/DiploidGenotype.java delete mode 100644 src/main/java/htsjdk/tribble/gelitext/GeliTextCodec.java delete mode 100644 src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java delete mode 100644 src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java diff --git a/src/main/java/htsjdk/tribble/example/CountRecords.java b/src/main/java/htsjdk/tribble/example/CountRecords.java index 230c1bf3d..3bb8e4160 100644 --- a/src/main/java/htsjdk/tribble/example/CountRecords.java +++ b/src/main/java/htsjdk/tribble/example/CountRecords.java @@ -29,7 +29,6 @@ import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.Tribble; import htsjdk.tribble.bed.BEDCodec; -import htsjdk.tribble.gelitext.GeliTextCodec; import htsjdk.tribble.index.Index; import htsjdk.tribble.index.IndexFactory; import htsjdk.tribble.index.linear.LinearIndex; @@ -193,8 +192,6 @@ public static FeatureCodec getFeatureCodec(File featureFile) { // return new VCFCodec(); if (featureFile.getName().endsWith(".bed") || featureFile.getName().endsWith(".BED") ) return new BEDCodec(); - if (featureFile.getName().endsWith(".geli.calls") || featureFile.getName().endsWith(".geli") ) - return new GeliTextCodec(); throw new IllegalArgumentException("Unable to determine correct file type based on the file name, for file -> " + featureFile); } } diff --git a/src/main/java/htsjdk/tribble/gelitext/DiploidGenotype.java b/src/main/java/htsjdk/tribble/gelitext/DiploidGenotype.java deleted file mode 100644 index f53343270..000000000 --- a/src/main/java/htsjdk/tribble/gelitext/DiploidGenotype.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2013 The Broad Institute - * - * 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 htsjdk.tribble.gelitext; - - -/** - * Class DiploidGenotype - * - * Enum describing all possible combinations of diploid genotype variations; - * AA, AC, etc. - * - * @author aaron - */ -@Deprecated -public enum DiploidGenotype { - AA, AC, AG, AT, CC, CG, CT, GG, GT, TT; - - public static DiploidGenotype toDiploidGenotype(String genotype) { - if (genotype.length() != 2) - throw new DiploidGenotypeException("Genotype string for conversion should be of length 2, we were passed = " + genotype); - genotype = genotype.toUpperCase(); - for (DiploidGenotype g: DiploidGenotype.values()) - if (g.toString().equals(genotype)) return g; - throw new DiploidGenotypeException("Unable to find genotype matching " + genotype); - } - - public boolean isHet() { - return toString().toCharArray()[0] != toString().toCharArray()[1]; - } - - public boolean containsBase(char base) { - return (toString().charAt(0) == base || toString().charAt(1) == base); - } -} - -@Deprecated -class DiploidGenotypeException extends RuntimeException { - DiploidGenotypeException(String s) { - super(s); - } - - DiploidGenotypeException(String s, Throwable throwable) { - super(s, throwable); - } -} \ No newline at end of file diff --git a/src/main/java/htsjdk/tribble/gelitext/GeliTextCodec.java b/src/main/java/htsjdk/tribble/gelitext/GeliTextCodec.java deleted file mode 100644 index 394b5dc78..000000000 --- a/src/main/java/htsjdk/tribble/gelitext/GeliTextCodec.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2013 The Broad Institute - * - * 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 htsjdk.tribble.gelitext; - -import htsjdk.samtools.util.CollectionUtil; -import htsjdk.tribble.AsciiFeatureCodec; -import htsjdk.tribble.Feature; -import htsjdk.tribble.exception.CodecLineParsingException; -import htsjdk.tribble.readers.LineIterator; - -import java.util.Arrays; - - -/** - *

- * A codec for parsing geli text files, which is the text version of the geli binary format. - *

- *

- * GELI text has the following tab-seperated fields: - * contig the contig (string) - * position the position on the contig (long) - * refBase the reference base (char) - * depthOfCoverage the depth of coverage at this position (int) - * maximumMappingQual the maximum mapping quality of a read at this position (int) - * genotype the called genotype (string) - * LODBestToReference the LOD score of the best to the reference (double) - * LODBestToNext the LOD score of the best to the next best genotype (double) - * likelihoods the array of all genotype likelihoods, in ordinal ordering (array of 10 doubles, in ordinal order) - * - * @author aaron - * @deprecated This is deprecated and unsupported. - */ -@Deprecated -public class GeliTextCodec extends AsciiFeatureCodec { - public GeliTextCodec() { - super(GeliTextFeature.class); - } - - public Feature decodeLoc(final String line) { - return decode(line); - } - - @Override - public GeliTextFeature decode(final String line) { - // clean out header lines and comments - if (line.startsWith("#") || line.startsWith("@")) - return null; - - // parse into tokens - final String[] parts = line.trim().split("\\s+"); - return decode(parts); - } - - @Override - public boolean canDecode(String path){ - return path.toLowerCase().endsWith(".geli.calls") || path.toLowerCase().endsWith(".geli"); - } - - @Override - public Object readActualHeader(LineIterator reader) { - return null; - } - - public GeliTextFeature decode(final String[] tokens) { - try { - // check that we got the correct number of tokens in the split - if (tokens.length != 18) - throw new CodecLineParsingException("Invalid GeliTextFeature row found -- incorrect element count. Expected 18, got " + tokens.length + " line = " + CollectionUtil.join(Arrays.asList(tokens), " ")); - - // UPPER case and sort - final char[] x = tokens[5].toUpperCase().toCharArray(); - Arrays.sort(x); - final String bestGenotype = new String(x); - - final double[] genotypeLikelihoods = new double[10]; - for (int pieceIndex = 8, offset = 0; pieceIndex < 18; pieceIndex++, offset++) { - genotypeLikelihoods[offset] = Double.valueOf(tokens[pieceIndex]); - } - return new GeliTextFeature(tokens[0], - Long.valueOf(tokens[1]), - Character.toUpperCase(tokens[2].charAt(0)), - Integer.valueOf(tokens[3]), - Integer.valueOf(tokens[4]), - DiploidGenotype.toDiploidGenotype(bestGenotype), - Double.valueOf(tokens[6]), - Double.valueOf(tokens[7]), - genotypeLikelihoods); - } catch (CodecLineParsingException e) { - e.printStackTrace(); - throw new RuntimeException("Unable to parse line " + CollectionUtil.join(Arrays.asList(tokens), " "), e); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new RuntimeException("Unable to parse line " + CollectionUtil.join(Arrays.asList(tokens), " "), e); - } - } -} diff --git a/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java b/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java deleted file mode 100644 index baad1caab..000000000 --- a/src/main/java/htsjdk/tribble/gelitext/GeliTextFeature.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2013 The Broad Institute - * - * 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 htsjdk.tribble.gelitext; - -import htsjdk.tribble.Feature; - -import java.util.Arrays; - - -/** - *

- * Class GeliTextFeature - *

- * This is a feature for the Geli text object, which is the text version of the Geli binary genotyping format. - * - * @author aaron - * @deprecated this is deprecated and no longer supported - */ -@Deprecated -public class GeliTextFeature implements Feature { - - private final String contig; // the contig name - private final long position; // the position on the contig - private final char refBase; // the reference base - private final int depthOfCoverage; // the depth of coverage at this position - private final int maximumMappingQual; // the maximum mapping quality of a read at this position - private final DiploidGenotype genotype; // the called genotype - private final double LODBestToReference; // the LOD score of the best to the reference - private final double LODBestToNext; // the LOD score of the best to the next best genotype - private final double likelihoods[]; // the array of all genotype likelihoods, in ordinal order - - /** - * Create a geli text feature, given: - * - * @param contig the contig - * @param position the position on the contig - * @param refBase the reference base - * @param depthOfCoverage the depth of coverage at this position - * @param maximumMappingQual the maximum mapping quality of a read at this position - * @param genotype the called genotype - * @param LODBestToReference the LOD score of the best to the reference - * @param LODBestToNext the LOD score of the best to the next best genotype - * @param likelihoods the array of all genotype likelihoods, in ordinal ordering - */ - public GeliTextFeature(String contig, - long position, - char refBase, - int depthOfCoverage, - int maximumMappingQual, - DiploidGenotype genotype, - double LODBestToReference, - double LODBestToNext, - double[] likelihoods) { - this.contig = contig; - this.position = position; - this.refBase = refBase; - this.depthOfCoverage = depthOfCoverage; - this.maximumMappingQual = maximumMappingQual; - this.genotype = genotype; - this.LODBestToReference = LODBestToReference; - this.LODBestToNext = LODBestToNext; - this.likelihoods = likelihoods; - } - - @Override - public String getContig() { - return this.contig; - } - - /** Return the start position in 1-based coordinates (first base is 1) */ - public int getStart() { - return (int) this.position; - } - - /** - * Return the end position following 1-based fully closed conventions. The length of a feature is - * end - start + 1; - */ - public int getEnd() { - return (int) this.position; - } - - public char getRefBase() { - return refBase; - } - - public int getDepthOfCoverage() { - return depthOfCoverage; - } - - public int getMaximumMappingQual() { - return maximumMappingQual; - } - - public DiploidGenotype getGenotype() { - return genotype; - } - - public double getLODBestToNext() { - return LODBestToNext; - } - - public double getLODBestToReference() { - return LODBestToReference; - } - - public double[] getLikelihoods() { - return likelihoods; - } - - private static double Epsilon = 0.0001; - public boolean equals(Object o) { - if (!(o instanceof GeliTextFeature)) return false; - GeliTextFeature other = (GeliTextFeature)o; - if (!Arrays.equals(likelihoods,other.likelihoods)) return false; - if (!contig.equals(other.contig)) return false; - if (!(position == other.position)) return false; - if (!(refBase == other.refBase)) return false; - if (!(depthOfCoverage == other.depthOfCoverage)) return false; - if (!(maximumMappingQual == other.maximumMappingQual)) return false; - if (!(genotype == other.genotype)) return false; - if (!(Math.abs(LODBestToReference - other.LODBestToReference) < Epsilon)) return false; - if (!(Math.abs(LODBestToNext - other.LODBestToNext) < Epsilon)) return false; - return true; - } - -} diff --git a/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java b/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java deleted file mode 100644 index c670bf182..000000000 --- a/src/test/java/htsjdk/tribble/gelitext/GeliTextTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package htsjdk.tribble.gelitext; - -import htsjdk.tribble.AbstractFeatureReader; -import htsjdk.tribble.FeatureReader; -import htsjdk.tribble.TestUtils; -import htsjdk.tribble.index.Index; -import htsjdk.tribble.index.IndexFactory; -import org.testng.Assert; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.IOException; -import java.util.Iterator; - - -/** - * @author aaron - *

- * Class GeliTextTest - *

- * test out the geli text source codec and feature - */ -@Deprecated -public class GeliTextTest { - public static final File testFile = new File(TestUtils.DATA_DIR + "testGeliText.txt"); - public static Index index; - private FeatureReader source; - - // setup a new source before each class - - @BeforeSuite - public void beforeTest() { - index = IndexFactory.createLinearIndex(testFile, new GeliTextCodec()); - source = AbstractFeatureReader.getFeatureReader(testFile.getAbsolutePath(), new GeliTextCodec(), index); - } - - @Test - public void testReadAllLines() { - // Query - try { - Iterator iter = source.query("22", 14438070, 14592250); - int count = 0; - while (iter.hasNext()) { - GeliTextFeature feat = iter.next(); - count++; - } - Assert.assertEquals(count, 50); - } catch (IOException e) { - Assert.fail("failed to generate iterator from feature source"); - } - } - - @Test - public void testGetSubRegion() { - // Query - try { - Iterator iter = source.query("22", 14438070, 14539060); // should be the first 41 records - int count = 0; - while (iter.hasNext()) { - GeliTextFeature feat = iter.next(); - count++; - } - Assert.assertEquals(count, 41); - } catch (IOException e) { - Assert.fail("failed to generate iterator from feature source"); - } - } - - @Test - public void testFirstRecord() { - // Query - try { - Iterator iter = source.query("22", 14438070, 14592250); - int count = 0; - - GeliTextFeature feat = iter.next(); - // check the first records contents - // 22 14438070 A 0 0 GG 33.2618 33.2618 0 0 0 0 0 0 0 33.2618 0 0 - Assert.assertTrue("22".equals(feat.getContig())); - Assert.assertEquals(feat.getStart(), 14438070); - Assert.assertEquals('A', feat.getRefBase()); - Assert.assertEquals(feat.getDepthOfCoverage(), 0.0, 0.0001); - Assert.assertEquals(feat.getMaximumMappingQual(), 0.0, 0.0001); - Assert.assertTrue(DiploidGenotype.GG.equals(feat.getGenotype())); - Assert.assertEquals(feat.getDepthOfCoverage(), 0.0, 0.0001); - Assert.assertEquals(feat.getLODBestToReference(), 33.2618, 0.0001); - Assert.assertEquals(feat.getLODBestToNext(), 33.2618, 0.0001); - for (int x = 0; x < feat.getLikelihoods().length; x++) { - if (x == DiploidGenotype.GG.ordinal()) - Assert.assertEquals(feat.getLikelihoods()[x], 33.2618, 0.0001); - else - Assert.assertEquals(feat.getLikelihoods()[x], 0, 0.0001); - } - - } catch (IOException e) { - Assert.fail("failed to generate iterator from feature source"); - } - } -} From 00079377e0955c2d78c7947df0ef9f8e98aa062e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 10 Jan 2017 20:35:14 +0100 Subject: [PATCH 063/137] removed deprecated LineReaderUtil (#754) --- .../htsjdk/tribble/readers/LineReaderUtil.java | 56 ---------------------- 1 file changed, 56 deletions(-) delete mode 100644 src/main/java/htsjdk/tribble/readers/LineReaderUtil.java diff --git a/src/main/java/htsjdk/tribble/readers/LineReaderUtil.java b/src/main/java/htsjdk/tribble/readers/LineReaderUtil.java deleted file mode 100644 index 83a0545e5..000000000 --- a/src/main/java/htsjdk/tribble/readers/LineReaderUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -package htsjdk.tribble.readers; - -import java.io.InputStream; -import java.io.StringReader; - -/** - * A collection of factories for generating {@link LineReader}s. - * - * @Deprecated use {@link SynchronousLineReader} directly. - * @author mccowan - */ -@Deprecated -public class LineReaderUtil { - @Deprecated - public enum LineReaderOption { - ASYNCHRONOUS, //Note: the asynchronous option has no effect - this class does not provide asynchronous reading anymore - SYNCHRONOUS - } - - /** - * Creates a line reader from the given stream. - * @Deprecated use new SynchronousLineReader(stream); - */ - @Deprecated - public static LineReader fromBufferedStream(final InputStream stream) { - return new SynchronousLineReader(stream); - } - - /** - * Creates a line reader from the given string reader. - * @Deprecated use new SynchronousLineReader(stringReader); - */ - @Deprecated - public static LineReader fromStringReader(final StringReader stringReader) { - return new SynchronousLineReader(stringReader); - } - - /** - * Creates a line reader from the given string reader. - * @Deprecated Asynchronous mode is not going to be supported. Use new SynchronousLineReader(stringReader); - */ - @Deprecated - public static LineReader fromStringReader(final StringReader stringReader, final Object ignored) { - return new SynchronousLineReader(stringReader); - } - - /** - * Convenience factory for composing a LineReader from an InputStream. - * @Deprecated Asynchronous mode is not going to be supported. Use new SynchronousLineReader(bufferedStream); - */ - @Deprecated - public static LineReader fromBufferedStream(final InputStream bufferedStream, final Object ignored) { - return new SynchronousLineReader(bufferedStream); - } - -} From dc179c10b0c3bcce9f14cb05fa43f14a8affcbe7 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Tue, 10 Jan 2017 17:14:52 -0500 Subject: [PATCH 064/137] refactoring Lazy to take a Supplier() (#780) it previously accepted a custom interface called LazyInitializer which is redundant with Supplier since java 8 made LazyInitializer extend Supplier changed the constructor to take generic Supplier instead of only LazyInitializers --- src/main/java/htsjdk/samtools/SamInputResource.java | 13 +++++++------ src/main/java/htsjdk/samtools/util/Lazy.java | 21 +++++++++++++++------ .../variant/variantcontext/VariantContextUtils.java | 15 ++++++--------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamInputResource.java b/src/main/java/htsjdk/samtools/SamInputResource.java index 13ecf5dfd..d76694eeb 100644 --- a/src/main/java/htsjdk/samtools/SamInputResource.java +++ b/src/main/java/htsjdk/samtools/SamInputResource.java @@ -44,6 +44,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.function.Function; +import java.util.function.Supplier; /** * Describes a SAM-like resource, including its data (where the records are), and optionally an index. @@ -223,9 +224,9 @@ public String toString() { class FileInputResource extends InputResource { final File fileResource; - final Lazy lazySeekableStream = new Lazy(new Lazy.LazyInitializer() { + final Lazy lazySeekableStream = new Lazy<>(new Supplier() { @Override - public SeekableStream make() { + public SeekableStream get() { try { return new SeekableFileStream(fileResource); } catch (final FileNotFoundException e) { @@ -279,9 +280,9 @@ public SRAAccession asSRAAccession() { final Path pathResource; final Function wrapper; - final Lazy lazySeekableStream = new Lazy(new Lazy.LazyInitializer() { + final Lazy lazySeekableStream = new Lazy<>(new Supplier() { @Override - public SeekableStream make() { + public SeekableStream get() { try { return new SeekablePathStream(pathResource, wrapper); } catch (final IOException e) { @@ -344,9 +345,9 @@ public SRAAccession asSRAAccession() { class UrlInputResource extends InputResource { final URL urlResource; - final Lazy lazySeekableStream = new Lazy(new Lazy.LazyInitializer() { + final Lazy lazySeekableStream = new Lazy<>(new Supplier() { @Override - public SeekableStream make() { + public SeekableStream get() { try { return SeekableStreamFactory.getInstance().getStreamFor(urlResource); } catch (final IOException ioe) { throw new RuntimeIOException(ioe); } } diff --git a/src/main/java/htsjdk/samtools/util/Lazy.java b/src/main/java/htsjdk/samtools/util/Lazy.java index 13726b878..fca53a6a2 100644 --- a/src/main/java/htsjdk/samtools/util/Lazy.java +++ b/src/main/java/htsjdk/samtools/util/Lazy.java @@ -1,5 +1,7 @@ package htsjdk.samtools.util; +import java.util.function.Supplier; + /** * Simple utility for building an on-demand (lazy) object-initializer. * @@ -9,29 +11,36 @@ * @author mccowan */ public class Lazy { - private final LazyInitializer initializer; + private final Supplier initializer; private boolean isInitialized = false; private T instance; - /** Simple cons */ - public Lazy(final LazyInitializer initializer) { + public Lazy(final Supplier initializer) { this.initializer = initializer; } /** Returns the instance associated with this {@link Lazy}, initializing it if necessary. */ public synchronized T get() { if (!isInitialized) { - this.instance = initializer.make(); + this.instance = initializer.get(); isInitialized = true; } return instance; } - /** Describes how to build the instance of the lazy object. */ + /** Describes how to build the instance of the lazy object. + * @deprecated since 1/2017 use a {@link Supplier} instead + * */ @FunctionalInterface - public interface LazyInitializer { + @Deprecated + public interface LazyInitializer extends Supplier { /** Returns the desired object instance. */ T make(); + + @Override + default T get(){ + return make(); + } } public boolean isInitialized() { diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java index face55bb9..6988b4c8e 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContextUtils.java @@ -52,15 +52,12 @@ /** Use a {@link Lazy} {@link JexlEngine} instance to avoid class-loading issues. (Applications that access this class are otherwise * forced to build a {@link JexlEngine} instance, which depends on some apache logging libraries that mightn't be packaged.) */ - final public static Lazy engine = new Lazy(new Lazy.LazyInitializer() { - @Override - public JexlEngine make() { - final JexlEngine jexl = new JexlEngine(); - jexl.setSilent(false); // will throw errors now for selects that don't evaluate properly - jexl.setLenient(false); - jexl.setDebug(false); - return jexl; - } + final public static Lazy engine = new Lazy<>(() -> { + final JexlEngine jexl = new JexlEngine(); + jexl.setSilent(false); // will throw errors now for selects that don't evaluate properly + jexl.setLenient(false); + jexl.setDebug(false); + return jexl; }); private final static boolean ASSUME_MISSING_FIELDS_ARE_STRINGS = false; From 5a9d819edceb563b99b900049b2dab4a9061a68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Wed, 11 Jan 2017 16:15:35 +0100 Subject: [PATCH 065/137] fixed unmappable character for encoding ASCII (#784) --- src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java index 49de11d9c..af2acf59e 100644 --- a/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015 Daniel Gómez-Sánchez + * Copyright (c) 2015 Daniel Gomez-Sanchez * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From 6738286eb032c98f0228ff7a790a339274d07970 Mon Sep 17 00:00:00 2001 From: Tom White Date: Fri, 20 Jan 2017 20:29:59 +0000 Subject: [PATCH 066/137] Support use of Path in ParsingUtils, SeekableStreamFactory, AbstractVCFCodec. (#724) Added support for java.util.nio.Path for some more code pathways. --- build.gradle | 1 + .../java/htsjdk/samtools/SamInputResource.java | 5 +- .../seekablestream/SeekableStreamFactory.java | 3 ++ src/main/java/htsjdk/samtools/util/IOUtil.java | 44 ++++++++++++++++ .../java/htsjdk/tribble/util/ParsingUtils.java | 6 +++ .../java/htsjdk/variant/vcf/AbstractVCFCodec.java | 10 ++-- .../java/htsjdk/tribble/util/ParsingUtilsTest.java | 61 ++++++++++++++++++++++ 7 files changed, 125 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index b29f8e3d8..ddd12a34d 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,7 @@ dependencies { compile "gov.nih.nlm.ncbi:ngs-java:1.2.4" testCompile "org.testng:testng:6.9.9" + testCompile "com.google.jimfs:jimfs:1.1" } sourceCompatibility = 1.8 diff --git a/src/main/java/htsjdk/samtools/SamInputResource.java b/src/main/java/htsjdk/samtools/SamInputResource.java index d76694eeb..c1ffb2dd4 100644 --- a/src/main/java/htsjdk/samtools/SamInputResource.java +++ b/src/main/java/htsjdk/samtools/SamInputResource.java @@ -29,6 +29,7 @@ import htsjdk.samtools.seekablestream.SeekableStream; import htsjdk.samtools.seekablestream.SeekableStreamFactory; import htsjdk.samtools.sra.SRAAccession; +import htsjdk.samtools.util.IOUtil; import htsjdk.samtools.util.Lazy; import htsjdk.samtools.util.RuntimeIOException; @@ -366,8 +367,8 @@ public File asFile() { @Override public Path asPath() { try { - return Paths.get(urlResource.toURI()); - } catch (URISyntaxException | IllegalArgumentException | + return IOUtil.getPath(urlResource.toExternalForm()); + } catch (IOException | IllegalArgumentException | FileSystemNotFoundException | SecurityException e) { return null; } diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableStreamFactory.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableStreamFactory.java index fe8f42a56..ec8b9526e 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableStreamFactory.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableStreamFactory.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.seekablestream; +import htsjdk.samtools.util.IOUtil; import java.io.File; import java.io.IOException; import java.net.URL; @@ -78,6 +79,8 @@ public SeekableStream getStreamFor(final String path) throws IOException { return new SeekableFTPStream(new URL(path)); } else if (path.startsWith("file:")) { return new SeekableFileStream(new File(new URL(path).getPath())); + } else if (IOUtil.hasScheme(path)) { + return new SeekablePathStream(IOUtil.getPath(path)); } else { return new SeekableFileStream(new File(path)); } diff --git a/src/main/java/htsjdk/samtools/util/IOUtil.java b/src/main/java/htsjdk/samtools/util/IOUtil.java index 07ae9006a..97d4d9cdc 100644 --- a/src/main/java/htsjdk/samtools/util/IOUtil.java +++ b/src/main/java/htsjdk/samtools/util/IOUtil.java @@ -48,14 +48,20 @@ import java.io.Reader; import java.io.Writer; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Scanner; @@ -943,4 +949,42 @@ public static String slurp(final InputStream is, final Charset charSet) { return output; } + + /** + * Check if the given URI has a scheme. + * + * @param uriString the URI to check + * @return true if the given URI has a scheme, false if + * not, or if the URI is malformed. + */ + public static boolean hasScheme(String uriString) { + try { + return new URI(uriString).getScheme() != null; + } catch (URISyntaxException e) { + return false; + } + } + + /** + * Converts the given URI to a {@link Path} object. If the filesystem cannot be found in the usual way, then attempt + * to load the filesystem provider using the thread context classloader. This is needed when the filesystem + * provider is loaded using a URL classloader (e.g. in spark-submit). + * + * @param uriString the URI to convert + * @return the resulting {@code Path} + * @throws IOException an I/O error occurs creating the file system + */ + public static Path getPath(String uriString) throws IOException { + URI uri = URI.create(uriString); + try { + // if the URI has no scheme, then treat as a local file, otherwise use the scheme to determine the filesystem to use + return uri.getScheme() == null ? Paths.get(uriString) : Paths.get(uri); + } catch (FileSystemNotFoundException e) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + throw e; + } + return FileSystems.newFileSystem(uri, new HashMap<>(), cl).provider().getPath(uri); + } + } } diff --git a/src/main/java/htsjdk/tribble/util/ParsingUtils.java b/src/main/java/htsjdk/tribble/util/ParsingUtils.java index 2a88ed735..38cf8ab7f 100644 --- a/src/main/java/htsjdk/tribble/util/ParsingUtils.java +++ b/src/main/java/htsjdk/tribble/util/ParsingUtils.java @@ -23,6 +23,7 @@ */ package htsjdk.tribble.util; +import htsjdk.samtools.util.IOUtil; import java.awt.Color; import java.io.File; import java.io.FileInputStream; @@ -31,6 +32,7 @@ import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -83,6 +85,8 @@ public static InputStream openInputStream(String path) if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("ftp:")) { inputStream = getURLHelper(new URL(path)).openInputStream(); + } else if (IOUtil.hasScheme(path)) { + inputStream = Files.newInputStream(IOUtil.getPath(path)); } else { File file = new File(path); inputStream = new FileInputStream(file); @@ -400,6 +404,8 @@ public static boolean resourceExists(String resource) throws IOException{ } URLHelper helper = getURLHelper(url); return helper.exists(); + } else if (IOUtil.hasScheme(resource)) { + return Files.exists(IOUtil.getPath(resource)); } else { return (new File(resource)).exists(); } diff --git a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java index 16857b4e6..04887aeea 100644 --- a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java +++ b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java @@ -26,6 +26,7 @@ package htsjdk.variant.vcf; import htsjdk.samtools.util.BlockCompressedInputStream; +import htsjdk.samtools.util.IOUtil; import htsjdk.tribble.AsciiFeatureCodec; import htsjdk.tribble.Feature; import htsjdk.tribble.NameAwareCodec; @@ -45,6 +46,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -616,10 +619,11 @@ private static void parseSingleAltAllele(List alleles, String alt, int l public static boolean canDecodeFile(final String potentialInput, final String MAGIC_HEADER_LINE) { try { + Path path = IOUtil.getPath(potentialInput); //isVCFStream closes the stream that's passed in - return isVCFStream(new FileInputStream(potentialInput), MAGIC_HEADER_LINE) || - isVCFStream(new GZIPInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE) || - isVCFStream(new BlockCompressedInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE); + return isVCFStream(Files.newInputStream(path), MAGIC_HEADER_LINE) || + isVCFStream(new GZIPInputStream(Files.newInputStream(path)), MAGIC_HEADER_LINE) || + isVCFStream(new BlockCompressedInputStream(Files.newInputStream(path)), MAGIC_HEADER_LINE); } catch ( FileNotFoundException e ) { return false; } catch ( IOException e ) { diff --git a/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java b/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java index 1c3ad1f54..85f414e87 100644 --- a/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java +++ b/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java @@ -1,6 +1,16 @@ package htsjdk.tribble.util; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import htsjdk.samtools.util.IOUtil; +import java.io.BufferedWriter; +import java.io.File; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; import org.testng.Assert; import org.testng.annotations.Test; @@ -118,6 +128,37 @@ public void testSplitJoinEmptyFirst() { } @Test + public void testFileDoesExist() throws IOException{ + File tempFile = File.createTempFile(getClass().getSimpleName(), ".tmp"); + tempFile.deleteOnExit(); + tstExists(tempFile.getAbsolutePath(), true); + tstExists(tempFile.toURI().toString(), true); + } + + @Test + public void testFileDoesNotExist() throws IOException{ + File tempFile = File.createTempFile(getClass().getSimpleName(), ".tmp"); + tempFile.delete(); + tstExists(tempFile.getAbsolutePath(), false); + tstExists(tempFile.toURI().toString(), false); + } + + @Test + public void testInMemoryNioFileDoesExist() throws IOException{ + FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); + Path file = fs.getPath("/file"); + Files.createFile(file); + tstExists(file.toUri().toString(), true); + } + + @Test + public void testInMemoryNioFileDoesNotExist() throws IOException{ + FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); + Path file = fs.getPath("/file"); + tstExists(file.toUri().toString(), false); + } + + @Test public void testFTPDoesExist() throws IOException{ tstExists(AVAILABLE_FTP_URL, true); } @@ -143,6 +184,26 @@ private void tstExists(String path, boolean expectExists) throws IOException{ } @Test + public void testFileOpenInputStream() throws IOException{ + File tempFile = File.createTempFile(getClass().getSimpleName(), ".tmp"); + tempFile.deleteOnExit(); + OutputStream os = IOUtil.openFileForWriting(tempFile); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os)); + writer.write("hello"); + writer.close(); + tstStream(tempFile.getAbsolutePath()); + tstStream(tempFile.toURI().toString()); + } + + @Test + public void testInMemoryNioFileOpenInputStream() throws IOException{ + FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); + Path file = fs.getPath("/file"); + Files.write(file, "hello".getBytes("UTF-8")); + tstStream(file.toUri().toString()); + } + + @Test public void testFTPOpenInputStream() throws IOException{ tstStream(AVAILABLE_FTP_URL); } From 52c8210fae61052475c156675c99f02c050ac9b0 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Fri, 20 Jan 2017 15:31:45 -0500 Subject: [PATCH 067/137] Increase Test Coverage, fix tiny bug in Node class (regarding OVERLAPS) (#779) * added tests to the most egregious classes (regarding test coverage) I could find. * fixed a few minor bugs in the logic of overlaps in the Node class. --- src/main/java/htsjdk/samtools/util/DateParser.java | 41 ------ .../java/htsjdk/samtools/util/IntervalTree.java | 6 +- .../java/htsjdk/samtools/util/SequenceUtil.java | 35 ++--- .../java/htsjdk/samtools/BAMFileIndexTest.java | 15 ++ .../java/htsjdk/samtools/util/DateParserTest.java | 152 ++++++++++++++++++++ .../htsjdk/samtools/util/IntervalTreeMapTest.java | 4 +- .../htsjdk/samtools/util/IntervalTreeTest.java | 155 +++++++++++++++++++-- .../htsjdk/samtools/util/SequenceUtilTest.java | 119 +++++++++++++++- 8 files changed, 437 insertions(+), 90 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/util/DateParserTest.java diff --git a/src/main/java/htsjdk/samtools/util/DateParser.java b/src/main/java/htsjdk/samtools/util/DateParser.java index 02a960986..f2d9481c7 100644 --- a/src/main/java/htsjdk/samtools/util/DateParser.java +++ b/src/main/java/htsjdk/samtools/util/DateParser.java @@ -277,47 +277,6 @@ public static String getIsoDate(Date date) { .append("Z").toString(); } - public static void test(String isodate) { - System.out.println("----------------------------------"); - try { - Date date = parse(isodate); - System.out.println(">> "+isodate); - System.out.println(">> "+date.toString()+" ["+date.getTime()+"]"); - System.out.println(">> "+getIsoDate(date)); - } catch (InvalidDateException ex) { - System.err.println(isodate+" is invalid"); - System.err.println(ex.getMessage()); - } - System.out.println("----------------------------------"); - } - - public static void test(Date date) { - String isodate = null; - System.out.println("----------------------------------"); - try { - System.out.println(">> "+date.toString()+" ["+date.getTime()+"]"); - isodate = getIsoDate(date); - System.out.println(">> "+isodate); - date = parse(isodate); - System.out.println(">> "+date.toString()+" ["+date.getTime()+"]"); - } catch (InvalidDateException ex) { - System.err.println(isodate+" is invalid"); - System.err.println(ex.getMessage()); - } - System.out.println("----------------------------------"); - } - - public static void main(String args[]) { - test("1997-07-16T19:20:30.45-02:00"); - test("1997-07-16T19:20:30+01:00"); - test("1997-07-16T19:20:30+01:00"); - test("1997-07-16T19:20"); - test("1997-07-16"); - test("1997-07"); - test("1997"); - test(new Date()); - } - public static class InvalidDateException extends SAMException { public InvalidDateException() { } diff --git a/src/main/java/htsjdk/samtools/util/IntervalTree.java b/src/main/java/htsjdk/samtools/util/IntervalTree.java index 49c3017e1..cd4acdb69 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalTree.java +++ b/src/main/java/htsjdk/samtools/util/IntervalTree.java @@ -482,7 +482,7 @@ public int getEnd() public int getLength() { - return mEnd - mStart; + return mEnd - mStart + 1 ; } public int getRelationship( final Node interval ) @@ -492,14 +492,14 @@ public int getRelationship( final Node interval ) result = HAS_LESSER_PART; if ( mEnd > interval.getEnd() ) result |= HAS_GREATER_PART; - if ( mStart < interval.getEnd() && interval.getStart() < mEnd ) + if ( mStart <= interval.getEnd() && interval.getStart() <= mEnd ) result |= HAS_OVERLAPPING_PART; return result; } public boolean isAdjacent( final Node interval ) { - return mStart == interval.getEnd() || mEnd == interval.getStart(); + return mStart == interval.getEnd() + 1 || mEnd + 1 == interval.getStart(); } public V1 getValue() diff --git a/src/main/java/htsjdk/samtools/util/SequenceUtil.java b/src/main/java/htsjdk/samtools/util/SequenceUtil.java index 3108cee0d..92c1a507d 100644 --- a/src/main/java/htsjdk/samtools/util/SequenceUtil.java +++ b/src/main/java/htsjdk/samtools/util/SequenceUtil.java @@ -620,32 +620,7 @@ public static byte complement(final byte b) { } } - /** Reverses and complements the bases in place. */ - public static void reverseComplement(final byte[] bases) { - final int lastIndex = bases.length - 1; - int i, j; - for (i = 0, j = lastIndex; i < j; ++i, --j) { - final byte tmp = complement(bases[i]); - bases[i] = complement(bases[j]); - bases[j] = tmp; - } - if (bases.length % 2 == 1) { - bases[i] = complement(bases[i]); - } - } - - /** Reverses the quals in place. */ - public static void reverseQualities(final byte[] quals) { - final int lastIndex = quals.length - 1; - - int i, j; - for (i = 0, j = lastIndex; i < j; ++i, --j) { - final byte tmp = quals[i]; - quals[i] = quals[j]; - quals[j] = tmp; - } - } /** * Returns true if the bases are equal OR if the mismatch can be accounted for by @@ -836,6 +811,16 @@ else if (cigElOp.consumesReferenceBases()) { return ret; } + /** Reverses and complements the bases in place. */ + public static void reverseComplement(final byte[] bases) { + reverseComplement(bases, 0, bases.length); + } + + /** Reverses the quals in place. */ + public static void reverseQualities(final byte[] quals) { + reverse(quals, 0, quals.length); + } + public static void reverse(final byte[] array, final int offset, final int len) { final int lastIndex = len - 1; diff --git a/src/test/java/htsjdk/samtools/BAMFileIndexTest.java b/src/test/java/htsjdk/samtools/BAMFileIndexTest.java index 170bc4726..33c3709c0 100755 --- a/src/test/java/htsjdk/samtools/BAMFileIndexTest.java +++ b/src/test/java/htsjdk/samtools/BAMFileIndexTest.java @@ -181,6 +181,21 @@ public void testQueryAlignmentStart() { CloserUtil.close(reader); } + @DataProvider(name = "queryIntervalsData") + public Object[][] queryIntervalsData(){ + return new Object[][] { + {true, 1}, + {false, 2} + }; + } + @Test(dataProvider = "queryIntervalsData") + public void testQueryIntervals(final boolean contained, final int expected) { + final SamReader reader = SamReaderFactory.makeDefault().enable().open(BAM_FILE); + + final CloseableIterator it = reader.query("chr1", 202661637, 202661812, contained); + Assert.assertEquals(countElements(it), expected); + } + @Test public void testQueryMate() { final SamReader reader = SamReaderFactory.makeDefault().open(BAM_FILE); diff --git a/src/test/java/htsjdk/samtools/util/DateParserTest.java b/src/test/java/htsjdk/samtools/util/DateParserTest.java new file mode 100644 index 000000000..71313c15b --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/DateParserTest.java @@ -0,0 +1,152 @@ +// DateParser.java +// $Id: DateParser.java,v 1.3 2001/01/04 13:26:19 bmahe Exp $ +// (c) COPYRIGHT MIT, INRIA and Keio, 2000. + +/* +W3C IPR SOFTWARE NOTICE + +Copyright 1995-1998 World Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de Recherche en Informatique et en +Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/ + +This W3C work (including software, documents, or other related items) is +being provided by the copyright holders under the following license. By +obtaining, using and/or copying this work, you (the licensee) agree that you +have read, understood, and will comply with the following terms and +conditions: + +Permission to use, copy, and modify this software and its documentation, +with or without modification, for any purpose and without fee or royalty is +hereby granted, provided that you include the following on ALL copies of the +software and documentation or portions thereof, including modifications, +that you make: + + 1. The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + 2. Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, a short notice of the following form + (hypertext is preferred, text is permitted) should be used within the + body of any redistributed or derivative code: "Copyright World Wide + Web Consortium, (Massachusetts Institute of Technology, Institut + National de Recherche en Informatique et en Automatique, Keio + University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" + 3. Notice of any changes or modifications to the W3C files, including the + date changes were made. (We recommend you provide URIs to the location + from which the code is derived). + +In addition, creators of derivitive works must include the full text of this +NOTICE in a location viewable to users of the derivitive work. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS +MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR +PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE +ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR +DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising +or publicity pertaining to the software without specific, written prior +permission. Title to copyright in this software and any associated +documentation will at all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on August 14 +1998. See the older formulation for the policy prior to this date. Please +see our Copyright FAQ for common questions about using materials from our +site, including specific terms and conditions for packages like libwww, +Amaya, and Jigsaw. Other questions about this notice can be directed to +site-policy@w3.org . + + + + +webmaster +(last updated 14-Aug-1998) + + */ + +package htsjdk.samtools.util; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.text.DateFormat; +import java.util.Date; + +/** + * NOTE: This code has been taken from w3.org, and modified slightly to handle timezones of the form [-+]DDDD, + * and also to fix a bug in the application of time zone to the parsed date. + * + * Date parser for ISO 8601 format + * http://www.w3.org/TR/1998/NOTE-datetime-19980827 + * @version $Revision: 1.3 $ + * @author bmahe@w3.org + */ + +public class DateParserTest { + + private static void test(final String isodate) { + Date date = DateParser.parse(isodate); + final String isodateRoundTrip = DateParser.getIsoDate(date); + + final Date orig = DateParser.parse(isodate); + final Date roundTrip = DateParser.parse(isodateRoundTrip); + + assertDatesAreClose(orig, roundTrip); + } + + private static void test(final Date date) { + String isodate; + isodate = DateParser.getIsoDate(date); + final Date dateRoundTrip = DateParser.parse(isodate); + + assertDatesAreClose(date, dateRoundTrip); + Assert.assertTrue(Math.abs(Date.parse(date.toString()) - Date.parse(dateRoundTrip.toString())) < 10); + + } + + @DataProvider(name="dateDate") + public Object[][] dateData() { + return new Object[][]{ + {"1997-07-16T19:20:30.45-02:00"}, + {"1997-07-16T19:20:30+01:00"}, + {"1997-07-16T19:20:30+01:00"}, + {"1997-07-16T19:20"}, + {"1997-07-16"}, + {"1997-07"}, + {"1997"}, + }; + } + + @Test(dataProvider = "dateDate") + public static void testString(final String string) { + test(string); + } + + @Test(dataProvider = "dateDate") + public static void testDates(final String string) { + test(DateParser.parse(string)); + } + + @Test + public static void testDate() { + test(new Date()); + } + + public static void assertDatesAreClose(final Date lhs, final Date rhs) { + Assert.assertEquals(lhs.getYear(), rhs.getYear()); + Assert.assertEquals(lhs.getMonth(), rhs.getMonth()); + Assert.assertEquals(lhs.getDate(), rhs.getDate()); + Assert.assertEquals(lhs.getDay(), rhs.getDay()); + Assert.assertEquals(lhs.getHours(), rhs.getHours()); + Assert.assertEquals(lhs.getMinutes(), rhs.getMinutes()); + Assert.assertEquals(lhs.getSeconds(), rhs.getSeconds()); + Assert.assertEquals(lhs.getTimezoneOffset(), rhs.getTimezoneOffset()); + } +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java b/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java index 2e725ff43..533f9652e 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java @@ -37,8 +37,8 @@ public void testBasic() { m.put(chr1Interval, chr1Interval); Interval chr2Interval = new Interval("chr2", 1,200); m.put(chr2Interval, chr2Interval); - - + + Assert.assertTrue(m.containsContained(new Interval("chr1", 9,101))); Assert.assertTrue(m.containsOverlapping(new Interval("chr1", 50,150))); Assert.assertFalse(m.containsOverlapping(new Interval("chr3", 1,100))); diff --git a/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java b/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java index 50d84c0b9..34cb3c5be 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java @@ -24,13 +24,18 @@ package htsjdk.samtools.util; import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Iterator; +import static htsjdk.samtools.util.IntervalTree.Node.HAS_OVERLAPPING_PART; + /** * @author alecw@broadinstitute.org */ +@Test(singleThreaded=true) // to assure that the common resources aren't clobbered public class IntervalTreeTest { @Test public void testNoMatches() @@ -57,29 +62,147 @@ private int countElements(final Iterator> it) { return ret; } + private final IntervalTree intervalTree = new IntervalTree(); + + @BeforeMethod + public void init(){ //due to the destructive nature of removeMany test... + intervalTree.clear(); + + // each interval has a "name:length" + intervalTree.put(1, 10, "foo1:10"); + intervalTree.put(2, 9, "foo2:8"); + intervalTree.put(3, 8, "foo3:6"); + intervalTree.put(4, 7, "foo4:4"); + intervalTree.put(5, 6, "foo5:2"); + intervalTree.put(1, 9, "foo6:9"); + } + + @Test + public void testLength(){ + + Iterator> iterator = intervalTree.iterator(); + Iterable> iterable = () -> iterator; + + for (IntervalTree.Node node : iterable) { + Assert.assertEquals(node.getLength(), Integer.parseInt(node.getValue().replaceAll(".*:", ""))); + } + } + + @DataProvider(name="adjacentIntervalsTestData") + public Object[][] adjacentIntervalsTestData() { + return new Object[][]{ + {1, 4, 5, 10, true}, + {1, 3, 5, 10, false}, + {1, 4, 6, 10, false}, + {1, 2, 6, 10, false}, + {1, 10, 6, 10, false}, + {1, 10, 11, 20, true}, + {1, 10, 11, 20, true}, + }; + } + + @Test(dataProvider = "adjacentIntervalsTestData") + public void testAdjacent(int start1, int end1, int start2, int end2, boolean areAdjacent){ + + final IntervalTree.Node node1 = new IntervalTree.Node<>(start1, end1, "one"); + final IntervalTree.Node node2 = new IntervalTree.Node<>(start2, end2, "two"); + + Assert.assertTrue(node1.isAdjacent(node2) == areAdjacent); + Assert.assertTrue(node2.isAdjacent(node1) == areAdjacent); + } + + + @Test + public void testRank() { + for (IntervalTree.Node node: intervalTree) { + Assert.assertEquals(intervalTree.findByIndex( + intervalTree.getIndex(node.getStart(), node.getEnd())), node); + } + } + + @Test + public void testIterator() { + + final IntervalTree.Node testNode = new IntervalTree.Node<>(3, 4, "foobar1"); + int count = 0; + Iterator> iterator = intervalTree.iterator(testNode.getStart(), testNode.getEnd()); + Iterable> iterable = () -> iterator; + for (IntervalTree.Node node : iterable) { + Assert.assertTrue(node.compare(testNode.getStart(), testNode.getEnd()) <= 0); + count++; + } + Assert.assertEquals(count, 3); // foobar3, foobar4, and foobar5 only. + } + + @Test + public void testRemoveMany() { + Iterator> iterator = intervalTree.reverseIterator(); + Iterable> iterable = () -> iterator; + + for (IntervalTree.Node node : iterable) { + intervalTree.removeNode(node); + } + Assert.assertEquals(intervalTree.size(), 0); + } + + @Test + public void testRevIterator() { + + final IntervalTree.Node testNode = new IntervalTree.Node<>(3, 4, "foobar1"); + int count = 0; + Iterator> iterator = intervalTree.reverseIterator(testNode.getStart(), testNode.getEnd()); + Iterable> iterable = () -> iterator; + for (IntervalTree.Node node : iterable) { + Assert.assertTrue(node.compare(testNode.getStart(), testNode.getEnd()) >= 0); + count++; + } + Assert.assertEquals(count, 3); // foobar1, foobar2, and foobar6 + } + + + @Test + public void testOverlapIterator() { + + final IntervalTree.Node testNode = new IntervalTree.Node<>(3, 4, "foobar1"); + int count = 0; + Iterator> iterator = intervalTree.overlappers(testNode.getStart(), testNode.getEnd()); + Iterable> iterable = () -> iterator; + for (IntervalTree.Node node : iterable) { + Assert.assertTrue( (testNode.getRelationship(node) & HAS_OVERLAPPING_PART) != 0, String.format("%s with %s = %d", node.toString(), testNode.toString(), node.getRelationship(testNode))); + count++; + } + Assert.assertEquals(count, 5); // foobar1, foobar2, foobar3, foobar4, and foobar6 + } + + + @Test + public void testTotalRevIterator() { + + int count = 0; + Iterator> iterator = intervalTree.reverseIterator(); + Iterable> iterable = () -> iterator; + + for (IntervalTree.Node ignored : iterable) { + count++; + } + Assert.assertEquals(count, intervalTree.size()); // foobar1, foobar2, and foobar6 + } + @Test public void testMatches() { - final IntervalTree intervalTree = new IntervalTree(); - intervalTree.put(1, 10, "foo1"); - intervalTree.put(2, 9, "foo2"); - intervalTree.put(3, 8, "foo3"); - intervalTree.put(4, 7, "foo4"); - intervalTree.put(5, 6, "foo5"); - intervalTree.put(1, 9, "foo6"); - // Single match Assert.assertEquals(countElements(intervalTree.overlappers(10, 10)), 1, "Test single overlap"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(10, 10), "foo1"), "Test single overlap for correct overlapee"); + Assert.assertTrue(iteratorContains(intervalTree.overlappers(10, 10), "foo1:10"), "Test single overlap for correct overlapee"); // Multiple matches Assert.assertEquals(countElements(intervalTree.overlappers(7, 8)), 5, "Test multiple overlap"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(7, 8), "foo1"), "Test multiple overlap for correct overlapees"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(7, 8), "foo2"), "Test multiple overlap for correct overlapees"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(7, 8), "foo3"), "Test multiple overlap for correct overlapees"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(7, 8), "foo4"), "Test multiple overlap for correct overlapees"); - Assert.assertTrue(iteratorContains(intervalTree.overlappers(7, 8), "foo6"), "Test multiple overlap for correct overlapees"); - Assert.assertTrue(!iteratorContains(intervalTree.overlappers(7, 8), "foo5"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue( iteratorContains(intervalTree.overlappers(7, 8), "foo1:10"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue( iteratorContains(intervalTree.overlappers(7, 8), "foo2:8"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue( iteratorContains(intervalTree.overlappers(7, 8), "foo3:6"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue( iteratorContains(intervalTree.overlappers(7, 8), "foo4:4"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue( iteratorContains(intervalTree.overlappers(7, 8), "foo6:9"), "Test multiple overlap for correct overlapees"); + Assert.assertTrue(!iteratorContains(intervalTree.overlappers(7, 8), "foo5:2"), "Test multiple overlap for correct overlapees"); } private boolean iteratorContains(final Iterator> nodeIterator, final String s) { @@ -184,4 +307,6 @@ public void testRemove() { Assert.assertEquals(intervalTree.remove(46402360, 46402594), "frob"); intervalTree.checkMaxEnds(); } + + } diff --git a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java index 008cca507..7c96b742d 100644 --- a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java @@ -24,7 +24,6 @@ package htsjdk.samtools.util; import htsjdk.samtools.*; -import htsjdk.samtools.reference.ReferenceSequence; import htsjdk.samtools.reference.ReferenceSequenceFile; import htsjdk.samtools.reference.ReferenceSequenceFileFactory; import org.testng.Assert; @@ -32,9 +31,7 @@ import org.testng.annotations.Test; import java.io.File; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author alecw@broadinstitute.org @@ -144,6 +141,11 @@ public void testCountMismatches(final String readString, final String cigar, fin final SAMRecord rec = new SAMRecord(null); rec.setReadName("test"); rec.setReadString(readString); + final byte[] byteArray = new byte[readString.length()]; + + Arrays.fill(byteArray, (byte)33); + + rec.setBaseQualities(byteArray); rec.setCigarString(cigar); final byte[] refBases = StringUtil.stringToBytes(reference); @@ -151,6 +153,9 @@ public void testCountMismatches(final String readString, final String cigar, fin final int nExact = SequenceUtil.countMismatches(rec, refBases, -1, false, false); Assert.assertEquals(nExact, expectedMismatchesExact); + final int sumMismatchesQualityExact = SequenceUtil.sumQualitiesOfMismatches(rec, refBases, -1, false); + Assert.assertEquals(sumMismatchesQualityExact, expectedMismatchesExact * 33); + final int nAmbiguous = SequenceUtil.countMismatches(rec, refBases, -1, false, true); Assert.assertEquals(nAmbiguous, expectedMismatchesAmbiguous); } @@ -175,6 +180,58 @@ public void testCountMismatches(final String readString, final String cigar, fin }; } + @DataProvider(name="mismatchBisulfiteCountsDataProvider") + public Object[][] mismatchBisulfiteCountsDataProvider() { + + List tests = new ArrayList<>(); + final List bases = Arrays.asList("A","C","T","G"); + + for (final String base : bases) { + for (final String ref : bases) { + for (final Boolean strand : Arrays.asList(true, false)) { + + final Integer count; + + if (base.equals(ref)) count = 0; + else if (base.equals("A") && ref.equals("G") && !strand) count = 0; + else if (base.equals("T") && ref.equals("C") && strand) count = 0; + else count = 1; + + tests.add(new Object[]{base, "1M", ref, strand, count}); + + } + } + } + return tests.toArray(new Object[1][]); + } + + + @Test(dataProvider = "mismatchBisulfiteCountsDataProvider") + public void testMismatchBisulfiteCounts(final String readString, final String cigar, final String reference, + final boolean positiveStrand, final int expectedMismatches) { + + final byte baseQuality = 30; + final SAMRecord rec = new SAMRecord(null); + rec.setReadName("test"); + rec.setReadString(readString); + rec.setReadNegativeStrandFlag(!positiveStrand); + final byte[] byteArray = new byte[readString.length()]; + + Arrays.fill(byteArray,baseQuality); + + rec.setBaseQualities(byteArray); + rec.setCigarString(cigar); + + final byte[] refBases = StringUtil.stringToBytes(reference); + + final int nExact = SequenceUtil.countMismatches(rec, refBases, -1, true, false); + Assert.assertEquals(nExact, expectedMismatches); + + final int sumMismatchesQualityExact = SequenceUtil.sumQualitiesOfMismatches(rec, refBases, -1, true); + Assert.assertEquals(sumMismatchesQualityExact, expectedMismatches * baseQuality); + + } + @Test(dataProvider = "countInsertedAndDeletedBasesTestCases") public void testCountInsertedAndDeletedBases(final String cigarString, final int insertedBases, final int deletedBases) { final Cigar cigar = TextCigarCodec.decode(cigarString); @@ -450,4 +507,58 @@ public void testCalculateNmTag() { } }); } + + @DataProvider(name = "testNmFromCigarProvider") + Object[][] testNmFromCigar() { + return new Object[][]{ + {"1M", 0}, + {"1S1D", 1}, + {"1H3X", 3}, + {"1H5=3M2X", 2}, + {"5P5M", 0}, + {"5S8I", 8} + }; + } + + @Test(dataProvider = "testNmFromCigarProvider") + public void testNmTagFromCigar(final String cigarString, final int expectedNmValue) { + final SAMRecord rec = new SAMRecord(null); + rec.setReadName("test"); + rec.setCigarString(cigarString); + + Assert.assertEquals(SequenceUtil.calculateSamNmTagFromCigar(rec),expectedNmValue); + } + + @Test + public void testReverseComplement() { + Assert.assertEquals(SequenceUtil.reverseComplement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),"ZYXWVUASRQPONMLKJIHCFEDGBT"); + Assert.assertEquals(SequenceUtil.reverseComplement("abcdefghijklmnopqrstuvwxy"),"yxwvuasrqponmlkjihcfedgbt"); //missing "z" on purpose so that we test both even-lengthed and odd-lengthed strings + } + + @Test + public void testUpperCase() { + Assert.assertEquals(SequenceUtil.upperCase(StringUtil.stringToBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), StringUtil.stringToBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + Assert.assertEquals(SequenceUtil.upperCase(StringUtil.stringToBytes("abcdefghijklmnopqrstuvwxyz")), StringUtil.stringToBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + Assert.assertEquals(SequenceUtil.upperCase(StringUtil.stringToBytes("1234567890!@#$%^&*()")), StringUtil.stringToBytes("1234567890!@#$%^&*()")); + } + + @Test + public void testReverseQualities() { + + final byte[] qualities1 = new byte[] {10, 20, 30, 40}; + SequenceUtil.reverseQualities(qualities1); + assertEquals(qualities1, new byte[] {40, 30, 20, 10}); + + final byte[] qualities2 = {10, 20, 30}; + SequenceUtil.reverseQualities(qualities2); + assertEquals(qualities2, new byte[]{30, 20, 10}); + } + + private void assertEquals(final byte[] actual, final byte[] expected) { + Assert.assertEquals(actual.length, expected.length, "Arrays do not have equal lengths"); + + for (int i = 0; i < actual.length; ++i) { + Assert.assertEquals(actual[i], expected[i], "Array differ at position " + i); + } + } } From e015d1e182390383e61f4acb984500338cde2fc4 Mon Sep 17 00:00:00 2001 From: sooheelee Date: Fri, 20 Jan 2017 15:43:21 -0500 Subject: [PATCH 068/137] improve ambiguous documentation in SAMRecord.getAlignmentStart() (#782) * improved documentation for SAMRecord.getAlignmentStart, setAlignmentStart and getAlignmentEnd to remove ambiguity --- src/main/java/htsjdk/samtools/SAMRecord.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 13ec3860a..9f03b2312 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -572,14 +572,16 @@ protected static String resolveNameFromIndex(final int referenceIndex, final SAM } /** - * @return 1-based inclusive leftmost position of the clipped sequence, or 0 if there is no position. + * @return 1-based inclusive leftmost position of the sequence remaining after clipping, or 0 + * if there is no position, e.g. for unmapped read. */ public int getAlignmentStart() { return mAlignmentStart; } /** - * @param value 1-based inclusive leftmost position of the clipped sequence, or 0 if there is no position. + * @param value 1-based inclusive leftmost position of the sequence remaining after clipping or 0 + * if there is no position, e.g. for unmapped read. */ public void setAlignmentStart(final int value) { mAlignmentStart = value; @@ -590,7 +592,8 @@ public void setAlignmentStart(final int value) { } /** - * @return 1-based inclusive rightmost position of the clipped sequence, or 0 read if unmapped. + * @return 1-based inclusive rightmost position of the sequence remaining after clipping or 0 + * if there is no position, e.g. for unmapped read. */ public int getAlignmentEnd() { if (getReadUnmappedFlag()) { From 19bd8483f193908dfd36b76bd8e6a80cf3db434b Mon Sep 17 00:00:00 2001 From: JP Martin Date: Fri, 20 Jan 2017 14:08:03 -0800 Subject: [PATCH 069/137] wrap both path and index (#785) Add optional wrapper in PathInputResource for the underlying SeekableByteChannel This allows users to provide their own buffering or prefetching, without them having to change htsjdk. * Index wrapper code * Add tests and javadoc Tested this version with GATK's ReadsDataSourceUnitTest, and it passes. --- .../java/htsjdk/samtools/SamInputResource.java | 8 +++- .../java/htsjdk/samtools/SamReaderFactory.java | 48 ++++++++++------------ .../htsjdk/samtools/PathInputResourceTest.java | 46 +++++++++++++++++++++ .../java/htsjdk/samtools/SamReaderFactoryTest.java | 5 +-- src/test/java/htsjdk/samtools/SamReaderTest.java | 1 - 5 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/PathInputResourceTest.java diff --git a/src/main/java/htsjdk/samtools/SamInputResource.java b/src/main/java/htsjdk/samtools/SamInputResource.java index c1ffb2dd4..a039e5aa3 100644 --- a/src/main/java/htsjdk/samtools/SamInputResource.java +++ b/src/main/java/htsjdk/samtools/SamInputResource.java @@ -133,7 +133,13 @@ public SamInputResource index(final File file) { /** Updates the index to point at the provided resource, then returns itself. */ public SamInputResource index(final Path path) { - this.index = new PathInputResource(path, Function.identity()); + this.index = new PathInputResource(path); + return this; + } + + /** Updates the index to point at the provided resource, with the provided wrapper, then returns itself. */ + public SamInputResource index(final Path path, Function wrapper) { + this.index = new PathInputResource(path, wrapper); return this; } diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index a5eef5038..8f203d5c0 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -81,10 +81,28 @@ abstract public SamReader open(final File file); + /** + * Open the specified path (without using any wrappers). + * + * @param path the SAM or BAM file to open. + */ public SamReader open(final Path path) { - final SamInputResource r = SamInputResource.of(path, getPathWrapper()); + return open(path, null, null); + } + + /** + * Open the specified path, using the specified wrappers for prefetching/caching. + * + * @param path the SAM or BAM file to open + * @param dataWrapper the wrapper for the data (or null for none) + * @param indexWrapper the wrapper for the index (or null for none) + */ + public SamReader open(final Path path, + Function dataWrapper, + Function indexWrapper) { + final SamInputResource r = SamInputResource.of(path, dataWrapper); final Path indexMaybe = SamFiles.findIndex(path); - if (indexMaybe != null) r.index(indexMaybe); + if (indexMaybe != null) r.index(indexMaybe, indexWrapper); return open(r); } @@ -106,25 +124,6 @@ public SamReader open(final Path path) { /** Sets a specific Option to a boolean value. * */ abstract public SamReaderFactory setOption(final Option option, boolean value); - /** Sets a wrapper to modify the SeekableByteChannel from an opened Path, e.g. to add - * buffering or prefetching. This only works on Path inputs since we need a SeekableByteChannel. - * - * @param wrapper how to modify the SeekableByteChannel (Function.identity to unset) - * @return this - */ - public SamReaderFactory setPathWrapper(Function wrapper) { - this.pathWrapper = wrapper; - return this; - } - - /** Gets the wrapper previously set via setPathWrapper. - * - * @return the wrapper. - */ - public Function getPathWrapper() { - return pathWrapper; - } - /** Sets the specified reference sequence * */ abstract public SamReaderFactory referenceSequence(File referenceSequence); @@ -178,15 +177,10 @@ public static SamReaderFactory make() { private CRAMReferenceSource referenceSource; private SamReaderFactoryImpl(final EnumSet

ssxKP2? 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ðTd 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�ndB$y`%lCrU;#+>q?L3IbA@ zJ`YKROV^V;Ex18~$lGACiWTCn{mzZ{Af!g%&g!Z131&v(CDZ;I>rw9t0-i1A5K-UnNVd_ zT~aYBt&6h2=%fc*UMZ?-Sr*FdiomP9B+F`v=R}U>gf&^=RC#_~`t3$L>9d}8*TeFY zf^<4yo}`FyCVALHr`W@ch39)6k~H!?+YS7l=e1ilRz-Wgp4aF&9oOx&>~6d5^)h** zrJTIcEVOM5ebnnDTWdD$>Bidg<1K2cg1fL0xvP<;1HfB^zZGORNkX8oCYz#@ZIhAm zRJy}wRTDJ;fJ!ieBQ>|v%??L3|=%-j>16!0b`cr@BAo_ zM{`A(Eb{4e_>hc>M@TziTZl&vEWqG&dd)}$M(Nazb>KBu|Q5Ff1 zvpA@(UaPij$BJClOTeJ&JcwC^R5!t!1Qe4cK?Odci6oJ<0?s^;F&k6?pQNJT98#nx zidR-P$b4`*A)1VhCXOnIVw|oZ%N+dNnIWjFBV>I)^t&DnE&2X5x|D7N2~Ho9Okyx4 zE1i?jiJzH%GMSJZW}*_yNmODgWFI7ryC-Q^Z#fOO3NLYu#N5Wy^Ft zSe?pREM}y!XHpQQ#|{a>6+TpOJ{{>tK>lHjQ6|zED;szO|nMK8g?X9 z-6)Z83AC|7RVDl10#`A|t|%(6s5%-{q9o(269vd@NNPo{&^yV1VFj@Ty9tFc$wL^% zZ9NG!2b7Y#$c+<5@fUU@7NmeE1hALve)|$fOTpKixR0)s-$TTu< zmhKOL9}S7u35n63Qt>vVzD#nzDoHC>&{qNr3ce)3&w3f!`+8lg+4DQ~ir4ZwzHfPr zpa%%IK@sH1`^|tX^#xa@dF|glKD50Es$yN zyOk4k`UimFZ^$k60Q0HY#B^}2QKLnyQ-%HfVha1|qQLVE&f4Hbuzbp|bBi2X-~~hS1W@{BnkzQEwZZ^-QWdDVKt2xG)+)A z+8FT{{S!?xaW-tIO!{h?2{7!Lcy>h!uBVEzAOgc9J8sbu7 zxheMdypS-&aVHi{s zraOKFQJ+Q@PIvsMvH@~fMIVNA{HTT?0vTxxg8T#pf6z0!<3*rJh}oyCu! zTuB8MC{I_@YRSYX-5WLi}$6hTHiq0yxRYw1D0<+;!`+Xs05{ z8jU1tBIyfI)e0%7(jyVBCx=>ee_DhaCq%eTL^z3Hyc|6ObRo8*s4sx#3Fa-RI%{Q; zb;TUJ>F)xD`?&=e%~lQ2wp&fJ(X7%)$9x9zQnC1?OjB+~j2;N}vL^97U#^G@5*q}x zQr39jEy#0F<|$%>k1V9hE`%6rthh8{sMQ&jRcF^T2f?Yi6yJWj0n%fl`3U4=+9{2)HQie`!R*{5^H^N4)e=|6 zCoSa!o3>|c`c5{js^V6Ya3b74xl@ZIhGrV%6C<}a<1#?Njjf0oKlaVArMY#F$ z&Bcsltp$!V!)VHQDc^3eyZwF;c0B)%>v?yau;2E;>}j~Yzzn=r_^#jV!1bn2`V#^N z948MByEym0-3^>>8@D9vz1{J8xVj#W~P_D^2tD={s$}j>_c7cQaC)nIXk@b1meC*{a(%Nb#B- zZ5VtBtTlbwVMj2mkQ3A9I_mwLWgq9sa5a+Q_~GT~k54zkM(c&z$CC)_?~AZz5o@yd z#nfo}fou7FMJUHj*CxFOO}n|EzPhkbePv9e+sa5;E~oFu1u6R^N6I#cztV7U zrc|wAQ4@AfU*$Pwa1;r9?a1I&jnqO=#>kwQh)+FnK=phPtI-IBZWwU{LXU+mk=L`B z_Y%Y*J`4L!8p|2yl+VfF;NaEYm_I-aJ3&kf^Rrben!Gx%lr278EUxhihtp9NFs4LZ z<1u?DgRzSl9m!A?ehnQ}8Q@n8MQ5m|swjqvH(+8;DD#-GGc*Zhs-fT}k%kQ45e4`O zlBB5otfUBE4ANJaw=Y>Isq~J)Vh9uv+VLhM&>`6RgKlD7h z8#`?vAU(KIGLPi$;iq&n)E086{h!R3saz++IBRahX<1f0VQ6krlI zG^1E{l`rwU%(AycI4G{HFe^pCRxp77WnSV~j^oNKQ{-0|ro^*Mxy*1J%Sa+8me)#5 z=?#G`mrIHyvV5snDwSC7I#YTH258$h9Svw($id;jhd=#sf8>wC(a0w=18F2N82Ml? z_x(O5c>J#0CG-EfJ+O*NMz$Z2{n*>sPBQ5CyCL~*yL+eG84$4lSp>G2ldk(s*B2K4 zX|5ZLvxSGmA3C;_FN%Nx6=?)>M#=IrxFJ0)ONz= zuvinoNfiq(6fF&Q!|B&+GlCJ{QRgU(hJ{QfsRq6aySNuz8E#8C+(u9MkL7UNCX(E8 zU7SB-no-zeUTTBd)@ZvftznFSniei7?~{{R3ViwFb&00000{{{d;LjnL@LEU{@kR)eTUad6Jj7FE4?&6z)4u(>$!;stvRYfp~{=!IP{2J8jAm|K`- zF=idcgdhB39_&YB1K|fd0th?8_`U`lChRcyod3_v%B;@nu4;`mE&n}Tot2r@3iX%g zobO!!>2!Wgrla^^9Gw~$uI~hI{)dkh$dWG<3Ve5a{bPmw!h7^o;rcU$!jHjqqcI8I z3WDp`^+rQC>~^zlw{^$1+OA#p8v!GUA7P7AgoDaui>YYV_u3D_EWAAzmqfvsxs zjd(&_1h#dSz@A?xfxYsY5U_tQ0$TyFg*mWy0POF}m20=H8?FO_Z8lp}u*GRmKUjNs zcz6&TY-|J@Vkrm?>ZOh0A>jJp;X|=BTwAMe0LrD(Vd3 zs!-E7rB)H(I>RwMCsssRl31b4m5Nt*MqGotScWebOQlO!iW^&-#r2KyW~rP9HvI3L zC9tRdcsv;o`{QBb<4wb~N3l{Ld&A-Qm7pI4!{J~Ujwgf3a5{y5A$(8$pcnWP4?a2$ z;R}9a;7!8ecrt{Kd8XMkt=S@A|ECCS6~LC}1iKG{{n=c>Hgu5eX0z4Sn|4EQ60}#= z;(;%UGM6ja+Cn8BNw&7oVyjfJ$;a?Ft7l)dhr^*eOv~XalD+K8h_;$zvp)%F|4=?X zbK9of0?6&A8|m>=fGsOCH7-9&u%(X_*lA1%%#RA}v~kGTY%Q_bGd=#?`9k4Ajvm+B zrqi}f$84L;ra`e@S&MJ@ptLEnh-SSO;X|nx)#vmPpk`QAY7w~btwi9HMc$J)T5?yxw25g?>{lOs=Oen<#K8L+4UkQz=>HMQdM|VBvr{p`)s+vVG#Q86$D`z2EBSeJhBGlEBl`OgcXuwatFQ} z3{pR4E6j*?Er&nd2LANpgg?!@Mkcal*8(clFl~!~y)u0oURD$Z@TOE5mZWI2;8e_` z5KlO$%tSo8nvHjmRgVv3d^RMFMRs=fU86Z6JdQn~>@FYNB9cCu`XgXc8PTrixJI7_ zu)mN4w$(OVP~?`~M79jZm7u+{WCxe46;>3h9M7>l$8pt)Sd|z>2I`~ovZ$y)pU6>B zluCTHw8^j~u#zlO6I*Vy7(=+~aQ0>-pUt`5f_j!CeNIdC>7ZVyuag z_lEGXVFvB13EDrKp#2?y_CLr$8|*fiZL6i*pw2Bl4qmS++H04=tV#kviZZT>5*(mK zjwM@~Nw$`jY#O%KvVM^khqutz!fUc7kPmMxLfbeqXlLYcGMQ%MU2|($w`m;zz?8*k zYM+R90BC<#4%!Yu+qDcJX$`~FPmi{whyuYH1X%=hV_#@qkI$p6&3VCE3T;>-Ib!jJ zU#Q8QGmG}I>o`3=wfCKOwo&*U@QprQI2GVb ziP>B)ZI(-Gq9j(pIu{YuipB!q0)Qp}UYf#d0>@WM>t%*13nivP^t~ihMS)XPo)ZL4 zsBlR5WSJ4=imYs@ydrb4)0Og%b^-L3xeVqJ6x8JbrD%@F<9>fi(|aTGryP@`fW99F zgHT5;3wpHI!x-h*?}J_)O@hI=8w~n`2_}e!-NDcsk6V*qGzw#nDCW7}yB6wc| zaoWpqj-0k(kw_zmALwnHdPghU)HOjfjkgPLWm)Ys?ETT6faS|c&Nr;BibL4c;j)}b&`YXsw)$3d?_bK_|2eY ze=e6nnP5#_2lK&3(Q3kwNofEgoPA}`66g%K|I%s`FGmSQ|ymI%|e%H0{U{&*tR zGphV5i1l~p#yo&FH5|9uwwk&{#d>88u3ScvEQ-jKc>!Zu6kLo})?^Z_OtEB@Iy;$C z9`%S%Bq?#?LBvlfAlV&oKD^%;`u z(R4H-s7#NpRb=aJ( zA27U^1Gdw)kcT%3*iPhit!%IZfzw1)kX7l5BukPktD>xyRYjCAxgg2F!oj4=k^*0< zi2j%)BL1Kc6p&#_P^uDI0ICXPt#(zV&y0+AK!)GRGm^NlSnuq3U7f)5{I2i49MJQD zA9Q`+^SuE6h4du|Lq7=c2#!fQ0~fvv!+tQ!Uj$($ok32Jl_6)ZB;;&nv3p>#zb!vY z(RNH1a}SH9)B>O;_wz?pKNo{jZ1T(&ku=WaEXED$9kulgO zS?kqTFr9XzLt3kM5JBCT#W9n}RtKQ|)|`SKqm8DzVc8f|(OVJPtLpIewfllB3M?;3 zU|`icf%z(^>8nJk*DysP0qi2kv;yvx%7IrUf-0zC#f~b~t2IHw+;UMu`RCyxyobj} z0}l>Mg~W<1O4`Db>&0GIoE6OE^1?W$ho@0|BKhe@l9UlA5V--|RF90kkoKAfs9) zAYhpdAXWiItt~98IyF>f)uKT zhcltpa)nw+PLbQ6q%Gdt)|55LH)sfu*($E=vyP~nEJg^B@zQ9XmR>I<|=hxX%QN6%3in^%^wdzLof+SQ0 zRpf++imw=vR`h5Ae~;<_k*OkAlB$X zqS0rte-FU^3ppCy#PD^i;X0t!TTPedrB)R1X+69wV31iReW{WC1}7J*XsN(1tI8=` zM^#S3-r&LEFQa@wU>C`_d4^?tIz7IQYq>>Y;v--Llj*3J$yJ0o>EW%HUE;fqiU7^lRDR~l+D4{EKhRFR=ktF6+q zY>fc_(RafJguqP_8b`E!-m|bB*XK{7pJVbVa+ne9VUA#bXF}DERM|J%Eu)F0_O{{D zT*b;*A6^zfq%i^wHa5yGlfxR80o6&)p+-IE8rIXxk%JW*e3cZ9)G!sLDy%F6lgGq` zDrqErjeQmBG5yD9rxJG-A&Vj^7)*()=Tkv68aRo=WpP~Er-uP(`XCCbkhqE;c%Z=h zz8Anj;1jSvKL>WYTJHp~%_afc(F;CGc3~;N4>Ev1yv)i1%gG84z}GlQQUvaztguqO zt}vX!6#0sB;ezm_vUR0aR9Q_DIZY|6C8?%yRkgIvYnQ6Zh4aF5^0QLuoGfZ;McpiN z%9ZL1aLby&V4gE$yx#@ToyNp)cNzMU z1|YuFZQ?JTv#K-U%TnjRl0%&>()({VFc9LH%~J~>PXtIIvV?I;%mZQ9O1%z(25+dm zQKcCXW>q^%nBgo(6f{o=Gx@rp@tkyh)Ud^Ovkid2iJ(I)QpNfdF6IB z5wL$h0(*ph61%)2bF3)Ifog;2=zKeeQgoy zB|v?MsQdH%gdfNP$&(RrXGd=iQk{fJOq-1)kBub{QSYC)PQvu$g{ZSREsGz>LeyW4 zz2j+4D5L?XTRAG%CUqZ8({n=vNP2D*y+O)iuqL=Rbo|W?8i*hvjp&gBW$mlmKMA6Im2mvh@rMb@V8QMquq%(we zyk=ZEe*eB!CKmEqTj+aj?8sTslbW za-}HBa&ev6Dr+xPNktE-3#e&Tj%S5Rsah;!E24lLSrsw2QQRzZOsQJkU>Rz_8G<#Z zmCNO7nJw`GSK)byKmgSbLWk(f9sQP?tR_${r$cD zyZ7$x-PzmUyZ+A28@Jxxx&FqRd++Y;-P*gmw}0<8yzag6i8tSQYv=m)*KfRa?;W`L z-dlU{iF@ztz4_*w*WbLcbMw|aZ{NIm<7NzdO7V_6{&;LQO~;zPOe^RkI}WtC=aY5} zJUJpx(HJjwI^A9m=yK1Cror^4w6Cex>vem^1rmmFSxMYPo`L)I1#sWWAN>IT^oG%FI}Il) zj9<;FuCJ{f`Yga9R#mmCC=$Reb7i4Y7QuaGiJ>HIq$IhZBouN2BS`n~Zyt$uJy^hQ1ee-4U!_)aO8~>UP>$ znxtOw^eFv4cM-V1CxUxtW?B;+ds4Zy*@9Q2QFsV$(yu>>RkVj#5OP==(rm-wiOlM9 zzF(8G1W{C^GPCwfX@e=2H?M&8EHd!2$rLLaC1z`5bF=t#aig?RE3%wY?=Bs)5gy1CKx{}?$M6Nt6YQc;UI8_aioy6Km^$U z;oCVSk3YR&FoT6NSB#d?b__RaGh0o=KHNC0W7=MlFg{2en5dk27WR#b8`Z>VA^u98 zCgy?c@QJ@duS7RvBA1?0RH>GZP`Jj9?k=D%?$XCLnwR@m@nU`$nQ>e-lu=k?Xzf@dVDd z3LehpTRp(CERkmP4`&I#FkC-ozSRe^Qi>MiQmvh4VB`fO*CHQgEfo-6TSeKvpOuln z8;ShQSdI;5?=$&5+zt*1b-?Vot`$wVT3P(0%W=JUoQzSmbnrJXUJwO;BR4{gm=8U~ z>xxWb3K|wH149PJoS`|59V6vRI%ib=sLDK1o>pB1@Bfy^27BXFD~13_V)sT9k|Qx%b8IjO?35?AFae5J~9oUm2C zvdL8>RuP$UX>*g~nJQD-TwmL$FeQ%J+PcJS!SCU?&GOdf*5>*K%cS78?H#?j81coy zgAZ`Uh+ExW$MCzj`d#o<#}j`tv919-0?&Uu84f&u2%gC}L`SwCc*9}P>2^DQIQD>D zA&VM$sn9yIKc@y@l2eIA9E`w9{eB~_k2X-qQWH+LZER`FZT)_OfY@$ zKwKv^&NM4p(<0j%iEeJ;n;KV3NN7z-QnjX$sj_j57`RlObfS=yyhhTvDa0LT$7r5i zLN)J%EObhe3*+Pk8%E|*J!5i#WRghZe@3C-${|!(_j;H^sB}{0Y`QKS>P{o-1v~}b zvZ(NcOOblysCZe$RxqtzPt?BP(xNJO!of(-Z#|}1)CANNH6L#zRQlP$ z`AQEHJdL=R{w{88pirWTUM-!$Czj$zK=K2sTh`JEbO>x95RahA3h-8Gpdcr)UC zJ4d{~7vTPZ+=2?+slm2TydAgEa!-pl)=E)rzR*+BqOlD*o>C&+G?SO$tx-=&JrVD- zrp`a06%)S<-gk3qoh!iC-p=7`;2#4&w;L8_@(r^^`T45KTzq&LVTs#5sWUq>)-z(R zMZ}!VCFYd7lRj`t1Bgk8GQ>)D?2ZF$&B@8$p8>qLwo`SN#0cps$jrj?L0 z|9BDJCYoR})KcJI*%B&pNst6p zP-RA}TrM(OMX4f|i#)>?#WKTy=gTX+C<=0=$cVhmN#!k;t(4hviK+5in-#gHRO*<; zEAu1(BC1kN+cBgWE8OFSq!|e^KP|bD=v@14@wW4vME6etV526>T##)V5o2SW>oI{@CGAQB$X-CL%M4lqoi0Xti z-1?5u`sgvJN2Js<>7@5A9Not5Ccyn>PW(ehz#Yd1$aRC(N3AN^L-DeVbM_HQnGhb# z5>S^%(_&IZjjO_GDViLc+SmYK0*QLh0N#&W-x;~xDgfc@1Runo-O!a)nSZ2$-9k>IVyc&oGw?g-u*#am12p_AtVY0svU=I}mCboo)Qq^pFyK_WQL=<@e+bos9V-v2ZQZ?{c4LD45R8k+tTr2+Q< z?z=grn{@t~4Hu^v>P_8U9d41pEvkwrid9uEX{D+p5Glpo8OTV7I!+=~fORSIMyZOU z4p~{{ijv6lK*U%@9{i5Fu zeBajnw(i59J#fvA*>1y?YyEEDRPA0z58!Wp?d71;f3edEf>xu|>ifOHD^|bTF`buM z&006;^sbur!11p(o84YJlv8T~o0>`BPXQo*Czm{ffwjO_YMYjAt}cI6-Btyjzw`{x zSJ^H0xh+ZH_*$*1apfX*S}RhSuSs%IyaGm;sfkjJ7i5e*)WJormSk-MhjFk9BWP7d z5S0ox?L9Amt`-=qqbv%fGj{=-I$PA)>2x~Hm%5!+&**$y_j{cl3SY;uTS4FOJ;xjP zzU7-NF7IuxGXVLC5IyaXL~bV_m-? z$GZM40Q*OCr4RgK(`Y%g`j}eRRsE!J4aY$9GcGmrk~o!*y(BIt#7v8Eq{vI+V=sxT zVMzcM&ejyUhLu|AOr=Mt*v?L~c@`bxY3{6cJUN&hE?RNIDY=98n2pK7Bso7?0QW2m zQa@iP?B<3+uvxQZlLQ0&I4#^{*te|klB|lNq_CHf1I-S6da=O(rX z+J=RhB;9UBLC#fm`G&7h>a3)hbK)q)E8{4|m6d8OVallx2pyS59AgZ2qPoZgIcQ33j=r z@}R7_GVo_j7S|LUI7y~x)kv;ci?&2WV<2p5ELW`pJ5xE~2|(EltckN@?HEDR&ETEX z{a_#H(@`Jj(;qh8*avD@FTQxK)-+#y38>Ju#>>s2=V~v(IkHRT%RN8nBY@#(Ji@9D z(9d9*;b=T;j!BirguIN#lW_{^@xYpP`n`T{JR18W+6Nko_c!MFN;p%)%fZ_w`*k>0 z(`ws|W>iSMvUE><0d%g!EAar8I1zzueaE@Q^HC5rih5|o;?r8)8g@X`aHeHiyv?1a zeHL>VM+Nj0E80A36nn&@$XO;&z+^Jb^oSwc!dli-`dVK<#-;n(4w zhvz8b0U*9c5HB69udT0%>lE?B2c@-O3Wy(4-}tb!0cL*r{)P9ml5$#mM>lfde(dG6 zuO6gPphB0R@Q;p1!7&hpR`XHF9DkzBiE}bXDzLDe4h*V@;zn~37@kr^Rbe@m<1ewL z)5u(oD9q*Va#DHols022$Kydb><6xIX3}{n5DH6z;F?4fT8{#RQz<@` zvYIFnFOglY$dDNg+qd9zymBfKK5~jL@?0XIhf%R3-ARzRFQbW>p|RhTW9M!G+Hd7J zsV;ggZUgr#0yAty_)Rf(s;SI2J|bRfZRNRc4u@lAh{nH+Pz;xX{@N0f+H`=4fE^ z?9i`!u{$zIcjI;k{a`kSDD;BZIq^ep)}reN?S4NDf|jFuK?d$`&T&$Ca8h5-FNngh zM5Bc}ayKFsyRvYH_raCaq*`3qNJKfB&`O*Vbykm|%<3`JbUg+JGbCCTgM~FRnUz6? zP$`lka_}f+LQuv|Gu0nMS)bU#vFNl|(zp%Ep z{`^Hr+$nvdh^>O-DnQ91e*()YXp$sp0&rOn zI`IlC%N0Qpve~wo@SQo!N+(hzJ0j4b6!DyjyhTc$hy}%vU`X~uz}$~dXD8sSJ`diQ zuJ3hP@ZZYTPJAjy)&2{B``2<*t%;@OO~=7{<7U(dxS~~kI6b^Clf4jX30+1t$Y8)O!JDWn=vUkkJV9_w44?VYf@6O)68OHEFj{)cCoYXb77W zOgkR!#Y?|Ul`M_ss{8Xq?l`pF2F|TFvDx~4qD{n2UJ`BGUssCJmPIhD1k{?Gh#dS% z0Xsw_PEt7eRA^^GnENvxzYu^VP&6J*h!ZkS2anKC!IPt4IP4CmnIfXUkmEsq3OvXk z&)q%%cLvd2(`sAIR=hFDs&?-1eg)$~-=sq_;vIqrGM@Yql1BszZ#r0#RMJ|w2JO|#W%JGO4okip7QcZdbw8clIV zDppMkosXp0Prv8R4VD%!ei)$ zYqnd41J3?$!Y{dhZiz`IM+es&AK<~Uii(C&2u%=`nxts#6<$+0iBZW01cD@JB3n^$ zMqgQzS?#Jso&Z?}4_MYDNfgq7;Om3^DFxn0jbuE1cSnEk&fUGcd%O4U?(W^XeRqHN z?%s{pZtmTCXZNjpx9;A(yL)T**52(KZ{NH3&fdK@_wL--dvAAd@7}$;ckb-(gR8o` zd;8X%-8;8p*pH7S;ZC_dzwh<>VW&;zbha9uFbMmtpznED+|&*G-ChuO!=M#4R1L`_huxNCW}0-xnM z9KoncJXhivjw_WFLEw3ItH^9sQetkpJC?D4w&l_130c8pFtDOYdnTE-XW;4KcrqA! zbo3sc9K!Ozc#a43V}nT#?iqUCU;zKfK#IY z1k*Y#2sE#%&he;BHZ`%WbW;FLo5c$R;?!V8X%a8M8d7qIzl{@lPLkBqfN+w?J)#vZ zN2cc2ygnt&dVB7CG`|A1w%_ghlPDtphY{M}88uX;HEjY?xEga{}f~ zy4_V#ETw0JMLT$y_KuYb4j!LX@9*E+gReX9?!m&tJu*FdZx7Dz@7}w$kF%%u@9y1% zGjML7T*Kpg`@8$(ke<1B@6Ilq0|k5c-rn8ao44`Y?b|!Mchahsz&WM?!Pr-x1^c3i z#3XN1h~u2<*t(L3AS319nMnBz_tydUPvu%ztL-=zqHb9Y8mU-W$ff%Vh7_={7iZvV zIDT5KlF~nrTuD|bwpvY)FyL3$>eOe#IYD)RoTvho^krSeC+BL7NH?6SW2_4+iKT3o z711^yg<*YED@NN7I#}7;?|Xi~jXUdv9#-?>;NuW(3-KR(wfmVd*CCyP9A({;#6dLB zI*e`WVBnJr8MMDEr()(0L8t%Q+)NKhxof#@+i{I3BC@i0*L^`2BvDjkQZXap_-ds} z_YA--n8<(`nUz=>8>cIv%|+l)8e1bm48AoF|KJG`L6lLFm0|@;DX<7f1-D8P%8Dpz z=@JUl+Oe!Nndk`vI)9w5WQ28gggJ$u0w0hXaD0Q)uYFPsj%DEO03MsR>yydoz@I#% zU}WPyKjSXV(4LLA_zcMQkLAw*M32caF{kbttD=1vp-oC42(qY((beG;HJUM^kpUOD z6&JPFwW!YnjXVi^QDq*P0j#M|>GW2SY~iKh4npoojACP>F3Zh!cQ&#lw(bX?nWKk1qzFR635cNoUiGBNJI znp3Os)rGBkdfRB2NZ4G%L9C5B-cMdG%KMOoMa7Z{$EE%2*FS&+rj2Df$Pg*90eH$@=la27mcw#dnHiD$KR ztDb4>SZ8vEO=72egr|knoO@au+qJ?F*j&G3p~ddCd%aE=c$VD`{8rF|E1uo;f@r(5GnB;BN8=qkT5gWBy>dlo6%L3f%0d z=?-%0Do>W~V`o!EWf-Z~xp$Lv6IZ4*=itK8u_m8g&<5||lzb3xw}sn$SjnUzuB2@N0#&Do_% z7Vn1%-ZSz3=YaRm=ZLq_HsPhA+ZcN>o00#$BtMV5v2gw1knDmXim1A!3TXpTkf_I@ zH8~~U+H6h@t-JyUPQ(fUmC8Bx;Bqydcya(dV~h={kB+jM z6SCn&I1MMj07-b2Ku*TkOfu^HyA$g@^T;rt@m=}*x|waOfm?mUudt$Z76Qg4uW#Wk`YSjEd%lWb{S<~LJ%?0ZG zvBDi-hH-m)1Wp-z&HSkGHmymB)^>`*Z>L_fYt9WzR)(H`PYzl8%OKv@^Tpd~8@7W( zCcz)0+I%_TKAZ`+cwfMs!!S2hRi!9fjT0xSBFDC{dD)f{R<1>1b85qJ0=uHHyed`M zXowXJ*Ql9zOOtruhPgn%KS%?Ijvpe`9;=GE8e=f;f z;W=En8ET`24q3x)orw3wBD_UH*Z^-icFKer+3Q|ObF~O>mFjX(;OnYJ`&clvLJT)6 z^L&wcp|XBi5i0nL2m|2YLCWH7=mXu&K%V5N3-d(@1chVi()^l5oc!2u^}^TA6$@N)v1AcpeT#nIfU6Tcprkme9sZPZvupTjkgVx#v_S<28y# z)7-Jrdm<-Tun`vQ2UH+({p|%8Q2bZ|d|dXE^>4>Cw=sw$3%*BKDl(eHmwV$O)`*aH z(DBRNUJo~FUXXvz1i(I*6W{(7P`E#st8lRi9@834rx`UeEMr`!DdSo?^l_6ZQK$fV zq+$;ZZPd$4G3ekuY+y#)tSkmmdX%Kz8XpHs;sc#pq zQ|sHgHBLK96Y@kPiLRuAEMsB6FDC%@TLAbk=O#38W|4`xOItS#GwK0e&AuMu0B2RE zo-xt3+Ps^jMV@gS$)3f$)Wo`~wK#vRiHai84+HU1i4xF+451bcyM1(6`>@N+G+rw zHg%$i%S6d95b=mlkAh#45MOJtbiLnHL@>r8k?Q04MqqXDcXEA%625UALZuvz> za_rs!+M{?#+%%Xy3K=f-`J7}Tn$};+h1=BIuHiUsx9P;e=abPsJPvJbb+nbVmQ_+x zrzKK1k2bamF4_qDqoQV)${yyf440b8_pB7?o%4mlH{}PXaPJuqZ{2mlvYr<4GP7Cb za2}whm5ZAd2BcFIaoC2Gst>?&f?Bm!OVSCoBq~K5T$a`ckP4v-&BW|^^l4CUPPp4Sm>4LnDeG*bkBT1u;3s+Jf5Z$w#ywH`==wHZ*PyIrY zT{bT`OrkA9377J(PDyylY z#K|l#vlU4ZqzWg=B{*WrTvd>?61%0yY6ZLYnM)T1S!7YM3zuYOt5_7*88OYC4R^;$ zjd?vw$;5!{Vi68P-!{Xr?}u3E6~GC<(Sb|-pw}lQL4oH5{a)bvLDvfc3rG95dR=(i z>N)|O#NXO`<)yagb-b|K@4n(YP1EeY?6kv-M*l*Ncl;Xw_g8Y@Cc7}hQwZ|ih`T3F zMtgb$ZJ7Z;=|0S=T*nZkoJt{bbdL;C6*N(nYm!1*V&I9uXbAA8Dv|Xn&i2HjFSrNC z_(~!=O|pj5d8$fgP#mC*@|`l_kDWLN3E+ct{nwG!{%(8_M7{mdn}9wPew#cnZBhhA z{&`p+=7$&s4Sje#LL0s_8vU=Me6v{-&Rr}N{z+~e3{1G8H!KVLWm?pRud2_7Q(u-8 z8iiCy7it^`Q8i8z1?nZKiT$Q6P}H8I<(5cU2*#x4stC{`i&hY7g(u2is=*sZNKEOH^ow>yPRE1MC<>vC@S{Gt0Y_$yy|70R z|5}9j59LHYKMx{)CkJtt6z#jc;T-tcL^vu@!a~oS%HW+Ym6(09yc5)m1vH%maw&qbJ@dmNZshGks?`)x#B8fMeHW*V0H z3hB)lc8BAzhm$;WNEtzVFzt~yA(sD4h7*D`oryM?q=TNX#!~(xIer;d{rz-~TE`iL z81U4w|J7XxmjDO)||ltetqo9(Dp?PJ-Ry0NcoTE@V2i2;-ASuTyLWzZea+_ zHRA!^t6A~k!;Oc&Amb=*%q1!W(TJP_TvxABpA0NER{hjT{|$+70r(ZTPH!XOF`Nsc zkg;eYrl>I9T@@8H?qrZp8u122e6Xk|?veJsl7jma*Ns~byhwJxwo~cx-A}jDRSJ^B}QdKN+#d2wFt0GA}LWu1))P^f?p;D1JPN-BX zo0|+PVhg0gX=O)+P zFdPv=FwT?*eK98l^REE&pUjnWQXPa$&d?)=e3ejJ1UZhaEkyw(Q^pFkSGKbL;kmOD#CtX!4|Y+lSjZKNvV!HE^{7r?qkN18*|3Qg0nGv)S$~Z>bfn-!i#Nstk3S80lR}=->St++#daFf zXiwTjkH9pZ`n3w+(`>8lmm;nH#9Yhen>j6)F4%I*bxFIe4T!%B_kIl=wUuIS$ln4X za-$@1lUKJGv4%5^B}rl=c}u_?RgK@k%!q(9pwxOz00+K~J+u|vwT0QJN|L}vx&hdK z?W#)Vv8m|pW4MMDWKK#q+TKCr4MaW_ep@2G-rv3biJSL6`PS|I-Mjbh?C$QqwR`82 zxAyk#+}XeR`rf_WySH|+_x9cWTlenXc^A8A-@bGA=I&dsU*Fxmbz_gN+qZZ3-@CWB z|L#71V(;FayLWbP+`9A5?)+2(+@vgMwfj+=#cPM5L#bQeMbg%fLov9;(jWkS=Y{^D z=k(!iH==w!&yU9;$MYNEOz1@ujsHi4`%fb7r7VLpE*1)ZKR2Ob+wGREH`>VAqitAL zV{Z=*)@gB=Aj+5v!CtCby?&L5u8dqAcOnGjWEO*HbxA8~Vx3WGjUYT5vLe7kA=fyN zYOw;(ibiKxE9wR(3bMfk;ub!cnCs76IJz4Pg8r2p+K#*0wXGI# zch__b>Zz@$+t&}J%Q##_6lIodxm2skM8B(QUc3z}xG9e#l}ALQquIyVoao-;)&N*Y zh~Kg*f^h(|UfWc)^e79;;3j!393ONE+QNl^muDPq5^3t=)%C2;KJATT!6$~_CL!il z2-6?aR4TsM(`hH;r~OP0XaC~`L3RVPBO|xd=>lrFi>%k?w+(vFxL~ zhhxzLGsGS@a(Qi~hM>-i~uU;Z55%fyEKbSB8A!GFWN3MsO#DyM}w>scOv3 zXdAXhrVnbV1ZhInBkrb=K6?p=5@{rCuVRBWnsX6UduA+Eg1Nqkxxa7FPqUKVTSRj* zoNSX}x(FM%>7=Yb-HsKbw<3LuS4Pl(9z*SAq;a1~K>zs#rQR57*A2bdwv3jg7nCOp zg$8{8$$~{`-5=$IeJS|&|@M1!YHvxzwm-|zMPZtJw8=@%@5!ylw^Sj)01 z4OrDr<*+J(gVWG$f&(?O3ouxyZUKX`A~FKYNjxvA0tfQK;*PU5M!mAJ@my6CHD23% zp|VwBHaE*mnPDpH*cZoA^9oE1erU1)YL#a;;kL_)tZ@QvUnxm4dQZi*OXXskO4GuA zs&exN?c!!wPrX4m!+I(bz3rXXCtr7xw7uVjDi?R%8X*A!CPZk_Xc{}kBdlK0O<+;M z6n>iVxH8k!2h&;4+=v8P#-ngJ9)!hHVJZ38ZE~;OfAi-a0&Be zXIgA)RgK17Ds=3p%8INgYGS2aDW`C6MB%a2u209NJ>GilPj8=u*!x~96s4WasmQ_$ z$f3g##z;rw&M+7N>f@GYW+eS97vc$!Gdhj)4)${Bv_s-!E!Q^Nj$=4x;m?D*{r7O= zisWj&UKdKwl`4F7bAx3sR=Fz2Zn0N*VXJb5+2k*+Z?3a!v9w-htLLi~mVLg;uCFoA zR4N=>DueX1)hb&N_zG89Ugb_mV|1n#@$584`>*)jE=ZASzDR}+>Rq?%wM^T01EbS! z`kvXr6yqx>=C8m%-Fyk>5O!LfPL~WM91Qw{lk7ppZT`g^N&nvf`KGes z8V;>YTUpRi(nSnlT%S&n%uzD$Rm-!{-9K)%YC#6{s+!{=&NJ5!U7x_CxwWo?0 z9($?MNphYg(X*oyX!ImCN}n7I2SaOUX4v%K%ZY=35nQOl+&Of%(FUqv;}$f!6_p~c zCgRh2{k}@(YuD&l1DrKTJZCbS2)t#Lc*P2bgXQX^=UvliX);NUP)8g4_vnlpm8Q$c zC(%$;6(rOdzZq>g!93+QKa!O8X}slRC73TUx@dC5b6V8ex!`;zz4=Qy^@zXwOuSR^ zk!cs;HajfTR5GcbcH*f-fE9t!I8IW83VOqe%#|@ZCRAA& zh@6VG527sMK5kW+7pr8Ga;aQgV-%oua3~geU%LzSsBQwe7WgUMtAZx3A`e#(sCf zz;+#{7d4vTJ+!edAyX+CIbKc|pz0VKIUe*( zWNZm4ozcX{SB7Qu?3~iw4l?TYS8~+tZ!c($wm{vsT-^4{G#U+x#nKsw2nn)Xu1tr2 z(%JWFj6>Y0L#*kMMx6u)dVeYoEfSdn95k}OCRUJTH`GMe$yl^o!edpp(*(O_2c6VlaX8PUL-793v0!y}xAqe4_@qpUku#F{><+zbbq?JfV|KYA%c&bQypWF&rb<)zG@&-@7eDARZ= zllhDAeYrI7ANl_Fwpl8f& z16~$&UDZ`pR$2H8UMYs8Yr0s}WKGg^)qs!noFOTSE~*7x)ig!nG)ZDr#!z)xR25Z) zU)V04zO-$gX6}Z&)q1xwfPdZlgI)6f=#y$@dvKcZ@1F9X-#vZ#0X%QR^Y#~~nc&{8 z_f4G4-Fv6}7x#99An<&62)g7?KwiUXcj+^E?*)Myz4U!}>IA+=f6aj>8^8BGzv;uH z=Q?i3_d5;G_nR%R?FH?2K=A%xz1n}(HhO(^W z{ZP&hgXhDuv*%A0<+;3(f0jSQ1KPsh=9PwJ0^Bvz zYM4&VuGKT8mopjd)l4QP=-Nck!>OQ$VHO1aStRIZ*=NC%m%oWK27bGHsv`L1Ewenu zy%NX$r3m+zUIgyyH*pB=|7V2zUq)GPC1w3rZ)GyiQgE*|tQz26FP9p2t!!IF))!Un zVIawIrhqpJx}+3TPz6QFkVV1J z1Wl1to)JWa*93{nX+l#`{h#q;inPct@OO6L z_X+5~9fAH|MWDYN1AQAn_X+4pDR&y>O2e$xZM$K@gJWgBf~&v#S|;;+Y}d0ByZ-!Q zyFS+LA|oq;aO+lHP;c>!VzA6=c5UUoRiU`{o^IsceD$5elEAP9fh{N-I#=YEg~A$} z&*v0Q&=tX02Jrc%xAMUEG`^5qUt4~6g)uluk~HS+^_X@)0`Sf9z$|xbex-!=y*d?p zXM6_~;0|~V?tlW^0k0+QfX?2dgTvk3UTL_tE2ryN@3pes#Qkx^s8;;l0OOTlWvQcaINuPwt)^pX_|r-#U7HGTM1~|G~Yx z-}?N~$@c#K{^-%*!T1h{G%<+1R|`J)!0LD#okrj{+h(;|`q*g(cF7AG8%40RfJfDH z+Hh$jXj{#;(`f-;^SrLt3O)vf&@R1Sb?bp&cPgz``Qx$^G~4iT{eu#@1HKd80sl2} z_!+VPb8rVdPq_nX@V{9v)!;6uS8Am)75YVGUXeg48v>AYT`2%d1EwZwBB*Wyx> zRpKR(URjkT$>2A1Ls1k#D;5>x`pD}vUXwsQD8>fxyrQV`8n8T&eRwi-@DEfTY=RsY zdMgt8${bC9WugDtB;K0}eHFNN71XomS6$H97TA2WSw z6}mn(2WTGJ3wff=;Xe;(iI_&b1)_I?ovLR>IF7 zj@|BwmhUG%zUCp2|5gO~FGrBaS^Td6oBMq!Yz~ZjrBSWgWzhF#)t+$pvF8>B&x7-x zn}!-b|4f!O<=NSDIeZ=}inq#(Ji8mnBWYa2j}K zV7~dBDv02P3HWJN1i(@Z>I!nUbvbM>Q2*ZwOu@|a^SX`p4)Q$wrhc(=7U{#y>{DgfqzEa zIKM+3xpB0P1nyj7>u^VMkujrq?B$_P?6qwEyp*=6{B$`J}*`4fNiustw+I9h`T9 z`6M=%qT$cQ=0L-*JohAU*k$nAQ7{2b6i<{?c!9hO!*uYfNWHT0L4Xw$_@WL>K*LLl zQc)Ao<`)%%ICb!&lD_~PxqJ+Bn*i=ih1|R`PWv3O9L7a>_TU#$q&*y8gzq=$i%s|p zfBGo*o5R7TO921P2=KoiMdx0+LUfM$Zy?<@cra;Hmcp!la8aYr88sU3-9C2{k2UaPC$-VFx{1~Vh znB;i|?@hw!HYXSLXRe=|a6mun4I6`gKe{X-;(ucz;!~Uc$H1olt`wVYHY^7;ItV%V z?bV3AFBFnfJ8JZ1MoEKa>ZN=6>lK;@)HcjNg&_?NMmsth9lf78HSrUXhg-+?|`QfjStso_%3qb z@G(l^sDh%z4SAGwv0_P=@{O_M1Sb5sY7}mexazX2hkXx3onZb?BFz6*lxT~G<$eV) z|MMxB6L+m#s)1IomTUD&=I7wzzxLWx(8uIWi3$3%bAqm*pif!5zN+ZrySLUrT$jZ+ zgtfPnH}kUk+EVUKX1%bU(-~p)R(|>QB}S9=x0hefFXvX5G-mm|oUtLjm0QuWE8?5W zxivAHFUW=b>npc!zw`R4vdq1^dK>uqK0sdUnzj!FuH=`&P50AWHdODgN9z6cS>!v% zUmPCqeXDox==9|7UU&Ci|1jJ-IPyQcA3l2U@WK7#N4sD2w(bU>JsKSx5AFtAckh4k z+33;TPw$_6wco%0`RL2f_rkmP@9llL_3+@~R`59>6?}1ccl%pk49}-s+;YcneDZ$T z^}SN({bsG?yz5{)3xiR4g1dfcWQ0V``9kpcdGUsuYTuFJqS7<)GJ=O z+}ilWwmT)K-mW#cMKSH&p2X*=Z-LSKq< ztc%L}*)uTcI!U>}A*po|dND9WL!&EW(xwpcCq|r_czh=iTTWgYGG43b8aM#t*ToIw zaEio>d|cXXD(!Z8s^KTBeL0f$!YN11CO)w;4%z^@?e>Z{7Vm zL+f+SteX~>?lB{$B0d<#WPNP(`{?|yZMNtjGJ>29|uUH zh(`2?VGw_kz#zHoPJp>=a&P#aOMbxecRaV_x^AQCH9MV7y;1MDZKqmowVY)gyL>pnHkbd<|!s-$p?qhSRnz+afRF1suz& zSfShV``{S0J7xT+>-T{f_j=$TIbJ^`7qvTH=T6uU$y@q+_Ec%(D=K*ZJcjpde*32Y z?@?+H6o|B0HXV$Qz`vc7317}+mR=;ZW4JAc4AKh!_wjrH<&1Z4mRTkf^AtN+6vRN(GGCM_RR2igMdALpvc>nA?6e z%5A^8fT8Uioo;^$5;?+P`0?@X$>Y)S(ecUY(dh7GbbNTShY|4+hP+RXk50ZiIXO8! zKHM3>IXc=uIy^c^3xUr)UeGRW6hEYeEuI&2g2W+jeb=W|FIWdRu6~TlAQRI5t0}qd z-vadia*DK9F#lm&HLFnuE=?Whg@rx)L_kw1&-%*rJt0)axL9TED}#?s2$ewz74?!4 zaEyCL6|Iz4ajGgQiYCVmZ3ROm)mW&ca$|^<2hu(0C2Z)NvjCBrJqcp6TygT3ZYtxB+8TS%8Q7VNT$<*V}$M+vuX4B}v{U8mD_TUFP! zT(9lpuKeLh9XM!VvHwf%yNN z5*+(^Kzuhv!W~2$+~g`oN=(P07JV^V<@-Z_DND;03?jB*%#95-@|YCjF&QZWZX^ZV zFld^SkZ(e<@i%xX`&hvqcb+O`MBJLnxN~C=uV8M1km9jGj;(P*@GlYY{_w(+h=2cG z42`XvuR;I06z9omRIv!%1}QoW>Z}8lW>{l zoXsiH{pbVu6XKfzroxBauo8B=cA^0N??z1~@zRy@TjSD|S;~$Su4JM;FWFY+mtM;_@z_rb3B6LLPr`{fwk z9N_&6DR@_~D$%J~R-<06n^qoNbeakv?XxQ zKmlk)NivYAfs$asJCik|AZgfxbWYAk6WnhkX`hEFiIS##z8QT>)PJu*BIck3{N8X7 z48mT2(CiNeJ&c!u#^?@Way~Zbe{Yh|p6d5H;QsHW#Kyp&R~prljdkcw-KGicg)t9l z!UI9nY4DRMdW?-}x)D_=z+*99r7+HO<6Qw)Yg!S1ozOOP=r9h>Q$J2c8_yZ>m`oW% zV^!xSa`qHVhnI^utxemEid16!F8Pa*}%|2E<5$(ogaHwAE;B(wo*@X~CvRyJt~ z!=iw1cp#K|0&jv_5la|~F%gedrBHQlL~Vx(i1-BWBK5~3ycNWo*M(xdW(6Bf2DMnt zihaWozbv6es*lFk&AT*W0=*f({MS?BF#i*HX@4};OT#);$Er6fPN_;0T+i~u1+X6a z!Ba`+Iq6+R2ggj14H>~z#R$%#NSv`K>_Ue^I#1vP}nd7Cs*a8Nx?VBE=6Z9!|1D^oZC(1e@wQ&I+OyIo2NhtMym{O(m zX8`dpQdW1k>}pY;2^*<|PR zD>&_xFJ}$`{GUdE|K=pnK2_=tDD|IBFGMu45V2OrrqL=2IrwR>!j)I`&a?oYH&&ho zS&!p{b&i!}Nu<77kyanV8%bkgnp_qnNg}yv5}~1*oaDa8n%tm1I&ZM&0`)Qe_7IUL z{<*W2R+6~>qJvj9R{tUhYkpH-Fcl~a^Kb$?YHwF2P;9QtK;i-M+&G8tBq(Og@ ziajv6x>Yk9mRWXac5_ilmxoKLs>za0I6IZkvH3Qp=9_ea!oTY*==6?Ir=X+FH^ic{ zN;Jk=fy)cLDisphO=Rp&TFc;#@XTX@mDEGn`MGMA@!6bt3HOPKg^SH*r`-;~TMOC= z<^J82pzHq#%Kd3-MWR&(>V|Va%yOw>(x$;Hi}~~CRLq}xf`lD|nhs*l@!+wsqAJK} zi3_OMjiO2vyO>by8qxfCcOdXovFj7Xu11O-uTXU?Uyf^cocrO#iqdY5Yu@V*Lx8dm zmOB6u4!yAN_Xqt!5AT8gq-Qt*aT@Z|&*IlcpC$zSw^FS6-vGdWJhe3(tCVWhIuiD3 zohH&2hC5gSeXS$Jq9V&8IMgauAFEOxXq$+)|;FEP9^YwpCd<~N7058SXHcDrFKZ1uY6RU=3PtQH=+2vs=I*^SHGwlJaI z<8KJwzY*d6j{$En#U=muQx&?^sMjmChGp06$UR$7n`dPY^|?0HT+$Y z%ZUNkr@9rMqI^s8BtMw(!M)zFjQp<` zcy2%Nv9Exj{SPCwzn9{j{f8jky%gytQ=II&=RKJu)YYrD%vE1 zmKYr}RV|`o25$}9ehe*Ass_hC%ppe_42P~}J46!!^FCLJ(Cv?+>!NhXY!m6+rkVq`8 zf%?BA!2j9AHJb+3dC=x3seyH%=cZMvV*^PQRC?yGy-IVdSJUWW;8Tqr%Cdy*o{|!! zQvft2ill1eWC@GvTpO!%5M)K`{e`lByD z`~1_(fcu@E8O1(dij$ETK(rqOW6BOseZMp4hoHQJc*-ozFZ*5$czMPr|0MvPeK|E% zZa18|P3hCd*cq=j76nEV)Xo-tzP`|B6L+8|P6ILcjB%&%~ zcQ_{tJa=u}FAT8nKYH-!;bpk}PS52AeRB-$yhEis#3HIezZdi_!F^2M{`rKiPYwG2 z3L5@NI$a0I?FvEPjH;j(H0RG&veL^24)Fjv=ZVo4z?ka_4XzTyzDDX@Fj@}(wleMh zMdXE;E$6ifO(!&v`ekU*!I~>tBHjx2yf%ldztABMD!5<$VGjdnAiKRGXldm>|CPe%HP4UFP4PZY^t#HIjw;E1kv`V!qjl3+V&1E@Q(kVrc zirKKUGiv`aNF3dW4yETLm{J7Qe=d$bp5t>4Y#oDi@zmL%J}*-`i?;u@PGp*MCW<`O z^%^EL_~wi{Us${e+T)RYzm3doEy>)l1ZFpdxsh=WYy9g8%ogfGZBB{wm@f zhQW%j`Jo@kvYf>(A)K6tp;I+7-oWCx$jMF^Z`lxK7031I0%~>6m~XOYPRxS0(Irpo z_g(|gGXVM;fS&oLMSLyYdT;jAVeJq7us^PS35VWrzPX+`1oZK!rp&D|YfBb5rq#jV z5ID19Vj{&XIgXS031Dr1A1=Hc@-RDGISaDSLoHazYT9#8k~Kxbacl~w(a0#)v+A_m z0eO0iu1WMZU}zbPgpOEFkVJkhe|5|Uh`F;>p2OK*sA5lpcQ_UFgp5Z~v_75O5TXt6 z!>;REmkoDHp#Bn|F6l|sG354R=|vG>%BxPjQ8TOMO6I#D=wE@Lrz&peh<6UYI4v;&tBO;V#(V`S)@~Etg~1*HU6kr)Q^UJX87E#WBIx7x~-sz-_+aXYT#9x?fOP3 zz){)0-@enS*Go0O;a`IG_z={`-QKUK$oenCXN445r%b(CwM!UpwVli_0_@)g*e?_G zFj#)NqWQscpk(DBgtLVDJuhR*gk;KR^*WilWbo9gqh3d`7mcE{(D1ex>^m98n~}7fI7{ z>Z-IN$_gvz6aj0^TFj9YX)pgprHtt{3X%!vn3pH62Q<^um z=Y>kS5C6%AgPtD{=X)^hhXc3Y3~Qd(_xhcFh^^h-up4?l4ZOuzKJnF~%<9F5fc=jn zu-{6N^7|m=|4Ax~v&=@ttl$jwa=l#6EP-YZ;U@;SoTArdfLjZLkidNwhW_$0Xm&y6 z#JAa{JdEi;xcuihZk$(a{-nTfYXZ~LKN0XF9};V%mNt{XNmv~UUPzcGV) zJYW8;IeSq|oGMVQRY2lP)%rLnb2Y85O%az>F$aiB6j)^DQK&^PVxu@qgEcy@7??l@ z?`=cljn$&g^PuGwP7qf30>`al!4p#`F!|f7Ifi*}iOH_<9J3}0JkN3Q5S&x;%dvd9 zd1KJ-_rq3yTBAB!s~U`7jN@`djo08+Z!iRE90q1ImN%LwP{#C}U4~aJ*RA_FlG{rL zT;G}4@uZCZnH03)A28i%%1hbV9@Umf_}f-?~>6IALA4Ww_EqgXug4yD2`YHu*I24Y`*}c?O#lt z%~S!boeCY8p7|-z=fCz6j^Fd22hX0CURhR&R>YKnw?==hILJ0BEq_*86H)}$Eb_t1!&DW3RM zm3rsk%X^O>ZSUQ`zqNfZI@!H{bZ~HRyti|3diZen@ZRCU;la_)-r?cV{`TSS=wyF1 zIzAd5?w=fuj`5%4qhp*Ob2vIVI2!F8jYj*Elxf;=EwkNh!kZbz6Rbqq5zG z=emLC;}{9w_1bB`ZIBBBNd=9sJ-G ztJC_E;<*0Ah`RX4`dp1xPegx+J}1xPNlisM#v{`4x~8QEyU*3ByimnXboW?Yr&kva z&g=69^?Rbw)JT!=&%kT?w^Aj%O6RVDb}yCe+9aiw!27qp7sFfm-W?2ghXijw{Qmc| z+V`~VAHO4E_8O#I#fgZd^;g5b9>Df8sa@pE>h$w^mrmYkACX zNG$j~IRW>L$r>Zc2HhpZ6PYH3EI9O5(PdmZW01gEaRYlsHn4CF{z_F5#hHzvH&n>| zj^_n++$_{7gBRp>;6JKacUvv5>3A*QL(i$@((!tc8`PrybU^k;({Y(TT_^2!1G-YW z8|{_GCqEjd`9~tKH*SrWf1ggAreIFq+9qL702{jjoeFIWOV4D4RG(|%bE?mS>~MwX zbLFYB#PLhKqzj_X={zYMhX?+5<%L@gbeQW*p z`rBFIo!7WkhF@p$OKkRy9K&V{>+A0_@G`F|dF6JV&#n~SL7{fG%xc#xZ&iG=vQ_oX z(pIe3H?fc4jVL_wM&vhP)S~59-Ei3RBgRZ~N@!RI-M-_HfN}#JRuW+@+m$=$Nm_Qt zyHl^)<+4@Tut~$MRmLx9AR3(OaLCO(lzUFWdayGY$A#V{B_<|S#|zGuh+&e~E{QPCAa z#BOd?pKD%kTE$4(0QkX7_xxA^2c)-)9=M|JhGBP_O(ywg(%91<_}!5F9lb<)>Em1H z2j<)Zu+LJ=t4(GrI29YqK&zE9)w2sKSvfnDfa++Ij3Mq<)%XH}Dob>|GH(bx$eqMV z5?8<>D}v13!UzN>t6Z_D6tl~DfnCq%IYAM5O%r*EVH7bd$~Y#=;Muj@TB@GC!Sz8D zL|V>Ip@t_=p&guQ*7v+YzlVv$lJ5`tL4VN1->8<`aIALI?YDw{za54hFYp@epw|n- zA!eS;Zo;U3GS#z*5cEeWqK$hx$p&t_Q8S&WpnpNRUJjp0Dsm{E#4&V2phyp`5D7J^ z-6Hl`@a)Z<6bw8+0lWVqk&M{|(yo*;c4B=?Bpdr66H}+d_Gb%-ZI{STf3TscaH9#oE#kO?;RW;?rh&b zIyu=JjZQ|mZu98mh-~9LJ;ukAWAfsdEaE&pK8B~0(eW`^+j+7(IyxdBVIcbG=y3n= z=qN29(syw`SI-6L0vBtAJQ}ma5ZO!(;JGvTFm+l|LZ6o=`h4mdFTa_|{B#O!u3#Lj zjHO_x)ob<0IbH;7lyKS8C0aCk}_;Nwtn46_(>P>`JX#&HNPn zCBOD6Fy@6*4Rhnp^0K0^tLw7BfX>b8a$X02RRKSzpn!kMNjce26b&bes*%Rs*dTs;o29shHL9RO!|>c5W!^iZI`;N)QKH0fFKTQkf2eOl-R_73zqdxD&{DAC98y7 z$~Nf)x+_6ECCp0pOj0y9&kHiktz}t;5jcqxC56xB*Vk8a>uc)_cDE~v%IX?irQ&v5 zO;QC#7I71%&S7t?LQLugkyKeH5~+}F*s6v_0-7Svle_DH{q>$P*qwH@>^N?gV>3{ z9t*fN5%8%+{VxIRd+7phfoRujxbeDbI~na2&=9X6Z@zLWH`2dX+wzh;g}fxM@jTCE zZx=YOAhN@y&6H%Wd1P*1B#NcSnUE`9Y7iy8DqoG{9~+YUIbg@5AA| zT*PqBmx*jrEozT@DRXZEb`1plKluOBdMB((qh7PB4X0GCR-+k3i>Y#DWvL)B5@_jy zrr@euxhNMEg?d8(w`52l$dXVLq~c0`y};xdCMPO73tF33C2ma+nJmW(V!n_qi0e$D zkT0x@>l_bcOyWs#Hb5=h@YV?(u(sT`>-oLjfGpSQ(UAs`+l!Ov{FWPrLlVlcNTk!U z23Tv^@Amutctsc8@l_$OJFeGpdvqGY82W@@*HQ%g|CwYJX0u7)7u9UsfLg0sCG)yq zOE~_|z*1RF))>;-4li(&0ZtvnvW}@+WYEzRqMnp%S`9LhYngnY#^ib~*bA3peeGu~ z*gc%pzDbHgg3US(q$2A}0j$ljKC|Tb=K%DdP7QHl4;;xE z+vDNTm&vz}^&dpOmjU>pva+$WsK-e%r@D_>w<%@;&fcv}`@0$#PY zs$H`(Ft@C&m`6529oHPc_JFQAe(jq^9oHOVs{=)M&;j*M6f_af&amsZL8~`H%K{1p z|5-!a<`N7$;GDI4xTe1Rk!^3>A&UJ&_{EM78l7zTx?@#Jw4xq<>60)S*siDOb^gut zu|z=AEld~VBKtBZ_A9}DPOUFLQFK+*3P`gstU-nfqE;``UUxb_l!is3m}V3JnWNRo zH+1v~(dw9tp0C%xW_Es!GZyj9{-BFvh&KD1aQd5*S-^wgIJUBRev0IRVJ`yq(+KS5 zG{!lJdX?TBPbEq#t1j7C@mDeHHH!AaV4p4N@xZ1+gI$yObyVAFOe%efa{`+r*i|&f z-;FJ}PQu;u3xMWZ@%gEw#Mw;wctHqe0?jcHGFJrw1I++J)T(q15P(WeKv0Ys5GK)O zjYb>=1R)a51PJQa4Z=4qa&e|~4(!d?iztCaWpI`*UFb4JX#aYIcKg=(!Jc>K)>2!I zn&p6bEtMj9TNvr=6O3e$s*EBw8(^ynyi$lCOoPf1Up69@sneI|q%EmgqXGd6wemwy zNj>Nf$pUcFNE{4$h%VA)L^PR5SX`bbClT-Jq!irk0fBm&%9_hK7|OJPJJ-PKXMO@` z^WVp!yNhBUA#mm(ihTrxGv}WUJ&DC-S-hwV+o z?kd)YcGz^=o@2GUm$BrTLoolD^O#R8>i#8|gE9xqtEJ4J0KEU^D_6iA{G=5b<6Zs` zFo&xm%mt0pWkD9%+*0TTT@8V zUd6P%qMb|HA7auzws-a_N&C*p@yX8i!QR2{<3~FOk9W6sA8#G*JQ|&jPEYou96e@> zFh>uU9v&W^>|u`n6dn%oQ#kPX(aFi^kRrN>ADY@NFOW1>|8Q#3zD#PIv3aFZEkz}Fi^ZCWzRl@8 zujC~~X1Fy;7QvNa6-i`e+$|076@|mbQC(I!Mbagups<>%NFc8Otf1(IAi_J5#psn_ zWa;Q-noZIL^ju_3(dH(UZjQ2Ty3J~<+VU{?;JD4EUvoUS)p9%70O+-Sx8=IHYuamp z=j!^7-}3RTPjZMp{E#t`WNW+=)S_e}%`Q!|N(Ak3AFvy7sknjtBcN>mQi`%gZ`CQ| z+{JR)tWS1PE+*SRr=EL&oGQsGgUMYiveG5YAP6{)M?=vi0Snm>IgXPg+=HRXI0qe^ zP?lvFb}jctfq@r=JXb)VdHl5q7o*QZIsgJ^?+x65#3erHwAw8ad`7Vk`a!Q>CD9HyaGU*@P9Mv+pW-XM5Ayvy zRi~4RTDx2WrCzp7N}w0U+xKK0JHF{$2V~5OO35>lV_JwoAXg1z>Iso=&Xeacryi4M z{0a*-^tlB3D%$)9d!`Y{L5NWa{D&~<{w9q-kkG`0GZW;m&7cX|Phx1xGvSE~(5^P@ zDk;93&e2^QY#gg-6bl?v$O)3bh`Bt!T42`-LLT?d6>&p}OiQJ4625?dDy+!oc`nbB zMRY|~1t&`u^0Gp}#zIRu0vj~PHNl=#W36Nq_~${#{=t+ea1BwgOI4t=wWuy? zQDYirmkL=?lu*)W#6gv@9zzBWg(+Q;#2W-wkn)nI=t?9>pkW0;5(QCX1XYv-014+b z2%^mLs;(%a$f-PoJtv}yRqm_v%;^My_gc`-wVlK;d@~SEV~5Uv2I;mS%7oSi9}pU*yD9*+4{esW9vz&n5O z%y#~o_8>mrbYfKbDAB`@6FqmoxhW@<-TadXj&2IO`OiS|{#1(OnK)$FtW_GdN)_BP z2_(inxC}kNmNR%m;iO{0kl9sEme$!NQCWXY z7T5S&>T7vLy{)Xu@+(V5k$X=95+uK_D}0fCP1N6gcEQR_n5+(plO_}%Q}J) z!{P+{aUI|4nx!qPG6f`&TYsUuU>ff`LEZbHfBOHHp`Z`*CJ z{!yoC*FWfdQ0aIZZmHebxa0Y?a=YpKtqr&3_>B$M`@|?#g0|Ok18{xol}6w;I_)+Y zkdny3gef_g-wk%}ug{x&Vb;J3G^(Y_c=ht4N_TllAuL5Ds{&+_Tp_B}C>j$-)zHTb zCtfC_QKN^%S0#}_>_JdeMwV5e# zL$^YO{qCTPc6k8S7_;>7(n%D}gpou@`8T?LB zAsNyFpA{rtkO8WW|w#| z=)REn|HM~~irDG*6mzeIje39_{Br z`b@TY5O9rX&r77h6@#>alAN6p?c=0KE5?kIN^^lERfMR|=_$BbrH{Qqm#|{+Xf}Jq zH$|6q;+94(>gEMPeFgLrV1Jqd_VL*uhXOS1f9>7lr!l>5?YMDw!>Y zh8tm(CL(QUQ7TWP(w!cS(9Z*1oB)lNs`JN2-yD;@+3kXL-5mIvLySudHoKE(#3q40 zr5=Ia-RI*)DLhKh-RA~twyv}SQ! zLb!Sf5MNl6pV2vV>e@P!TjAGn=CdR#qNuXjEHCoH z+XaJ_4MpQv#gOwX_*j4-qi713&ue;KRCHP3BsEh08=%*hK}~GP8!Di$h_Wd2oT3OZ zN9-8G%&~EkmkN02=-z`z_dnlx@MvdiXaC@EZ|~^wmtXGe?d%R7o;*G{IUH?2K0G#c}0 zI8dVB$HhP!I7|h%?#jHTN^?i1+#J*G15{Y3*s|Pe-7kB7%d7Z4E_T6WQM>7OJfAcn z-RT5gfcc}qZ3p-ixY&_|xukJJQq+Xx(oQtml15t5659Op6bB4V_uW(pCtaUbyD~iZ41w~XvO_Nprb)l%LIaN{f9KxiC#xhyVDPqgFrh&|gm{39>aY=++#0C~n z<{RjEgZ6$Or-|d-EL9}~M!}&|l^=o@G_P5Asuj1@?0`1$>hHG!M$7elyX|)XwNCI6 z=_!i<1>6wuXc$g3B4+|A?Jgv(hUAm+q?}~G;r$fB#{BT_O>gRU8dkM#;%IQEPSZz= zLcNlI!fN?~$YMa62UnZrK`-Z7Auo!eu!7AedLhdS$~w4Fil&PScvScVw}!0ivZAQO zUqf*>sMU^ox^eTis^Z-OzxG3r9&WU%m0F|I0e8091n;)d=v3`uZxmGHH7lz%X*Tq_w$;@|S zQdXx=3OtaZ7uATNsg$8%6PjEkxkOOdGO2kk79(y>iZf|HxK6s8keKrq2=>jvdS=8< z{0t@!!-t+XW16{MDchG>~BZht6aD>J_ne5bJW(jdTsHrJ{&fE3;UW-Po~$ z#cg!xjpCj3@#32T~f*#JNWk`#O=WZf@!|uN8ie1 z{*{z`h0`EOLM*p3%h4>!h3N6q{IYzuguBZXTsjP%k&H!Ch9)8_Dhi53$}SX@EAXqU zip(q7ylU_w#~M6l+zmnH7*0?M9H%pysN_^e$iYBw_9$b)$m(>)2jt- zx7zeut(xa}jTT&Ag&)st+AhKRI}z4flZLRV9o0bG{^eA0LrHULcEhnMU`EIDa25XNRCf`(OhAUg#`Rt;6*4V=;-3mRE-4{BBho(%Gr zrz)9DV^?ujou()_uOa3W#pmHHpkX^l`}giY+C4ZJ9UScM9UUBQjUMeB9q%6>?d^`R z>h9?H=;7hP(aF)_&IwLwIM^K>VddQEG5pTSSEJLDt&<}%EC;8t!*xdoqr*d-*DzaJ zr{Z)5yIiZez?s1<@?5XsH9BZR;l1B<;a>+=AbLoV$wPoo)T)l}md1=d?Z&O?xDeoB zO5ys-B%C?x69MSedI@(Xm1!x)!om&4v`9t?YO|Jfo_3|32!=7yuEu%oYK*ljp-^*5 zF>aPe6zSWmYv${m1k~FTsFUvRkIpfsW(^n9gZE20@&Xc#hV<##*^`_gE1;}(oVNiS z3TKpoMDjc*XgtoPlYm!&jt3dlv7HNC90Rw`<2*BkbpEQK{lOv9iW|lEmoPF`+}OaN zwT%3kQ0gBBj$Em=TXa`8nHlOdJzQ+saatYUZFi6iqgbLKM<-&#{zPUcLqoe=s~bBL zcWA~T+DVC9%K-IP=}C$juC1PTeBfLc>a}d1$uVs1-KF(>fh{aAF&xXYs|-_E z&ogXch0h8Fcz%=T3-7MrfLMWNxqOb#3u1v|bNn*TaU3JCMJ`g`tfEMafM7E`gOy%u z!urh*bXZj~LB%dtNRM`<+JY~w%D8j8>et+wg%eib$F%&=@>^cJ5jA*Qq{A0qgcH9C z$D;-0jo0z8t_xsE$o1YtuBT-D4+9zhqba4)Hcqc|sx>T~v>lT=zYAmS0~-hDR8gY^ zXe#JqLmMwR#DTPA3mWb5B@-*j2N=Hskyk*T6`HOfp)9f=kJ|4IElORgxESEZ%5{+1 zK{{i$V`;^dFX+npFeUdGzqmJWqgfok83S9KVdFmna{V8tC~*@5*0mbCUuL;RYT&UJ zei0f@jrNm>hYO0X2?a@(*c@}4fg1oT3^X!}7Co>cZDG+lwMe4?sz6o0AW3#M(d2}X z=|q#G8pnwqz{cZMd$V=$mrq~m9PJ(+k9HsKAM8KA|7iH=@Zj)dG&+VqC#R#)0k*S{ z7MA1fqtg@8&T@JT-1`K(VDLTmz?_b7NF{A|IVFuRqtOWXII!`VK9?})^#(z+7y2Nw zpIBiJ#C5Yzb{UQ{&Dea1?PwJ2#ZICQet%L2Kds9BgCO2Jsp5^9X0uesUj0ffDiK={ z?X&00OPnTvg9RW-qQK!WB`V&kB*_BD@t~PGBI=yPi&7-s22!vB_+Uj%jna|8*^r!( zN&`>^8>{3xAO(`Zs;S=gjgap|&oGh4e%R|xg>}&D_j)}q?DxAg@)Cq$KNbfI`z=zC z-H(KM9EhOlv}OPvh!&>^N3fH&2mK-++n_3)@Nq<2 z*5=Qhy*YujDLlP#yZ(P|7I# zxFAhKJ&snIV4XuUrWmueBL*D_F%jgM+L5tf6T?mg8?#i3MB>+SqCxfM){=PGOo4y| z!#ZNyI}c_5O6Vp}0_)^zwZlnA@|2W+8?5%9OtIP~0UNvpY*PiuCmVQ(a1YN3cQ$GuvNb`sK9n%Ef=HTKbqR-E*F%4-E)Twl|9+}FdtAqtWxt9fpHNtEAR(^-yr?;YVipz%v< zlB!8Or{(gRB(sL7GC7$`#K|j_ZZ&N$@e3F2POa&+n;&`IM%}O1J=^o!6|e32&8FEd zH`;c&=+t~KXm&cCmf!I{X*TNbH@&9Y_Um4?(se(nxL&jV@yD%?J8r9FVPv)5@ym|i zZq?wjn%%7W$?)lM3RgqV`xnzA<=~2y9IOX5D<+MpEiBo~PjdNOF3n?hWijwyO1Nqh{Kbs2O%KR=)f+|3uWuvJTCN(q1ZEii>=Vj!v6+V)Okks=y=v zOH)LZ0?!LLhC){LAA;@;kASz?9~W~E=;$U2($tSyz#*H^v?*On=<(Bu&pidZG68!w z-HBYSTCLfnR4i)WSXi>pmPloH4of<5(*Po`Z)g$@n8z#$f<|q3(bypE9UE9dg1*@X zep19jUtQZUHi{cXZ3D+A!i$X!^uKgPmaqmiwr>39W;}h*^#T*egVfy?CcVhmkf^ki z6nRcc!X`QFaqerJz@BW#i@iuB#1f+YyHZ5^M*;0Wo$iK#Dlg;iu9{hm);TRG*H1%b ziAFt%+l}(9lg=AMpGa>^sEs*7jlhz2ED&Sdr&=V5Qko%Een{LfB6^dcN-Vl^o}yjq zjHL)QJp}B36oLI?2)2%H*uOdru-uCD`MKaJ4hb?#7(2D>aw(Gq7t8}!?E4p=6w$xV z9)g*}uCMbvm(Q;YyqM2r3piki&$A*c%Nj2$BCvs&8>S&wiv`Rl0-pInVBUHB;K9z$ z_SV*e-JQLy`(HlXKiuEn*?PS9_~2l)b8xu#czZkZ#n;hD?=_oPHR^fr*l9P3I0K#Q zc-{Vhj5&yE1ILAFzbB=@`@=WqjVi}jnp4AK{&IzKw8fNoHjtk_k!67g@+FCisue^I zeJLK}-ebX*Fgrq$Ud7^!U>j4xmUTnLYBo5kERd|4hSL_+i$g3oCwLl$pvm1q4^t!U zo(qoDz#GKGx<^d*6*3`n1$#JSwtsJm*?v438Iv^IG*n(E72c)|x}2iDAlA=Uo~Q_k z29KbHby3F!Fd86aNQ$C~vO=^m0!dcI;94i#D1)O-Fi8rw9u&BUZg-I!J*spO}9TTUGc|d-SIOnW$BK$2z6uT zouEA)@%NLHfZEjk66dtoR_IFWIu`X?b=%5(4=x_Pb}d#utCyMFZ9!WHr!2R+wzigI z+1zqo5oA=-lE}Tc%oLbByT-4}oCpe=;qq%bhrJqW67VrW6y>$|mR1X*SS-p6s|cd5 z@l%f*H2EU{-Kk#BdUqb(AMTDGY(3t&|HZv~_wGM_eE;s&;qCz{>HWR!Pwzf@_-O0# zy`2XK+k2p}A8l{lJKjDzIv(BIAC2~R_x29%efrrK5BK&?PY)hFKHS|q9_<{DQtA-N z@mnAJA=WI^q7}JT$?Ez2R?lyTVOSX#G*tTCu-hlwbO#pp^tC{7cYD2Vzg>1}ZH&wI zyv~5Sa>+8|pG?u_KMt7x{Je@K+cHgXz3X<=LcgeZpFLRu$pvkVgDsGTk;Y4drZ@;e z(<41@M0y+-a!&L(`3-c*i5@o;h4^8lUNK5_gErUZJ7G76mY0X5`*M?N?aj%^uSvJ< zIn{jbT%^b8Nzn42OrhnCH`5nyI=FYjtW|2*!%>Qc(O&~>ZZhveCyurp0c%Xycrlt# zo@%ry*p!VMGS0D5$yyTh<*>(A7B2$(=A^>h2_mF^A-z@pVnTjJu*atR_ovh}{HJea zGCz|NIjxYXH@JVhVOn-Iik&VD_VXo4SWy+36@jo7^2;1AvI3uD3VDWOxpfvd;&ReD zyT0}7lK@|=tt{UlieZYN8!fc59_hGTR2LO#z31xC!?dMlS_RGw!P4z6Umd8T_2 zHU5J+6OX&y1Y2AOb8UgTq>F%@db{me;~F*FuUn4aw%YAF(coQNBz5k1UW0t=+a160 zApm6eaq)8C0|o3P`l^2_LOWiwv;m_1`%~382Gc-+S1`U(s+ThscwiEEVvF@u<1KK` zcBdUd67v$v@ayYqSr*qs$)cFcvMZdzXeb@ zV{E?0bF}jaZ1?`b!Qsw4+nsiNKkS6#)lhH~;GPk9uGs^B(8Z1EQ5j3WACi%SQEd~J zzVz`Vs}lb}O1QiQpr59O%Pp%>$EDe(37FGN*k$OR6Y!NK@QMq<8mo{s=?b`BoFXa) z3m>2^mw8Fa2>`H+s~c5;7dVd71W?~1C*uw<8Mrs07 z$Fi`5|NmzQ+y~nK001A02m}BC000301^_}s0sxss<-G}z9BFnQ)-!|Iog3YaKB_A3 zyRfr6sJk+=tMaN7yTlBzg8>>qUqEwp*b3QV=%Qqjzz{(;DN-kGQKCgs=6Wq^gNH4e zQkb?CmLq73VVg_Yw89}PY$+V3NZF>TSXnDYnB(`}pP7}}oz>ITbM=J(X;f!rW)->p z<-5Lj{6X*$tfd>b4tsCix<3xRAhi3#=`@;7N7Lyr8V`rlD1t*+!(lkw3VWmBXfPQ~ zMt%#|1g{N(!61NNgTaLE91Nz@pzno#7!C$*=y{$WO_Q~>eLH;Tx1L#|^+#V^T2g!0 zSD#sWd+96e)Y8=#mX_Z7-S^&W)W+|8DGax_>$O^=)^Hncr{UJyPQByS?RLFeTe|s~ zrKPp$%Gyv}Q{H`|t_B)4swo)n`tvp#RJvlmhy0Y@5^kk@(N@4XVRAil& zWf-Pmn(%6wrY@TB3(r|Pyu}Of9*^lJd}q-w!_;&Q-oRT`HQC1v_Eq=-pNEg=nx<{n z4xibsA1>V(-mll{OJAPcr~f@~G?um}hfCp&L*vaGhf8n6?``T8BNc+CX-2WaN2Ez_HSm;RtW9CkfQwv&^{uxC*hqK?Rul* zw%tb4b-GP}yxVl@=LTC4R83Rl6-kz5Ni{@KSLK&@(W>Z1i4%3s)DS@fuonQ(DjbeFPss>3q z!fXe~G}(OLoicG~IfmTZ7kn8_p2g3xj?17(pgb=?By| zDPMNRBu#D^v5H2GZ4%0awMD-ydN}Yx46i}2_&L)3baI|(f5K=-gVFiWE?9{5gdNPi zCnwr}A{FhK)&4%vqtgY^t~t$4^Soe#B1ZWm;Z4NW&{N4x4L0D;u(jBflfee1U?v6|A0&9as74!fYwDt*C<095=@c#YskS%2<{9kkCvVMx{U-~+ z9s<~3DKy$3+D@Zc2M?{&i1ql&FjAsYbyZoLs%uB;>dMjD(=c3la`dFMq8ycuj>44^ zoQsqvPgjqguC5*(Jw}U+5E_KFk-^%c_f{5blVMG$>l!KnSgMvhDugwD5$b^iYxt#K ze4~95nKQ04I%l*9?C(7Z_N6m|-D(op-TI3QR zpIxn#H#n)xivrK{mE{Uw5!T>P;7USC;Nghd*toKZUY(+Hyd?45y0Cdg=DFu3kzW(I z7eqmXqm36tN!Sqi4A^!j?1r5=ct0EC{Vd`g!w|Mw?RNY1&$nAGZwzbDgLvn4w+Bwt zAn*olT-Puds^d#<3;v_n$hNy~Z-mS9MxzKXbm7)7usdI;Z*8@%wZe8M?B&eqcctcZ z<|sb_7=E$I>0d~0a5Naug#n%#gz(P` z@i2%SyatPT%Kux8_V)nVvVmxSXNoqS!*v{O_z#q89c-%8?#5Bk!cf2aSOJ6}F5*Dm zI53MUDOX9+UnLW%B+8XJP3)>+%8JC4t5BIKSJbnzLdsPk=S$Hn#WJOmrq08$l`9<%%CbuhpK)V(>Weq2Cy zI3U&U6u3>c+W?$FJ=hTc+MOnIk{5KEj;hsV%_I!jKcqMkD{3Yl6JeWJQRXq}dP;E^ zvAs27S30$#hDLre7*eg!l%n5cUl^$#Ctwx0JM-4rtZ3%q9JFUTdq1`U#F%j&GBif+7K?RTESZK(U z^@#DA@!M4TXF_`((f%hf*zcmjKE`%@?Ii%afZA$f{M!Y;3|!?#?3^tO^_r|B+Xx_O zgr||?vrIG3Q70S+EzkmhWtDVyaxuA#R=A|*1H+4}!YM8q!U1pD0r&KlMDA1yH2 z5D~fcM!gO;yVL5l?DK*xG1?J4uGC_K5;oD7wgd~qSIx`;jyxwb>#C+2G0F+xGqG0i zYYp(`=W*5-3v8cxVUuab*P5bhcAXMblZ21V;x~zTk<(z#T_do+nE@MZ_OltVUjneF z1zNm=35rI|>DIt)i65Oh_SWw8v9r?J=e;4nng zo0E2tSB|(p6XX8O0=Rnz+iyO6^yQo5?frWw49KCZ$YjC75euVD0J}58xV41 zF0#>THJJ5YGyr=NEz6p&N+7PpJkmlHZ)&kUHnQ>^)0`yr8Yy&)tqpp2Orf*11;pY+ zgI;{ce6EzvUolsmgk+iUE3@dtc#_FgBw&AEfvXmRkl!uP=q(olFsEjNr`C0vc8kDX zRG%v=0Va}lnsev^ly0jP|6U4B3|po0v+$W-&MB zn^T-liN_YP*_oA#!oI&C3fl#+zoR(i3_{&$V-(i4YYwyDiwgG2+GFssQIruA6lip) z8BlOetj75$IgZNlfw0WjL}$V;I<2$wEN?}60$hGY0k8j4kaG@$Egw_Lf$xW57=YI8 zhXD)4!an?_BR7uBSWFg1g)1T1{N%)dg<}bm_J74_{}7-p33<`}bAa|=Eo4~gZnJII z-FBIsK^Q@a}}=4l{fi~&z6MC6^X0x!g_@hMM-=?hWj{0 zTFXG)u*1gfRx@n03zWG4h(8C23xN1@A2;FxAl`1*+U>UA3f*up?oS3jlAZp*Zw=b* z*V{yQzutQNYP%jq!{HdN^?F+nr-$CCJsOS2?18~u0B}?O^)$WoQfiP4Ap!5&LkQI zSsmRqlN+gNJg*zLx7(Y6faKw}yO7XvJD%$VuM z9EWfQldbBk4uKg1vJLua#bTX{gZA|am58PPicRt);l~#<+3_N@zdM7r#3cLDNwkkM zY7NlijT+eSR$NbV4yX-7GS(GSG?a?LqtFVbBq)ZaXb@CeAhNn_=(27oDqvf!DyFWG z$fK~sEgFPl=)R*kTj---y-F~HRO`sismeUTKAlfXXf8mb9U`IGgFps;dqLEPs3#=b z$qXpVPt7%>u^kNu{$NITCafo2%jwoXo9fnCRP+ZSDvAnZ-+?GG!+f{BtAK zEooZS(BLl*QI@0whK8aUl48odrfy#R93;yUvV}kmLVRJ6rDq2Wx zD8!Uy5X9HpUZ2^~?~Aei^D)-XW~A+OJ6dm%9c?z2ZsX186FEj~fS;pqu*wP-+fsl{ zR8(0(B1}=FQl+f%U>-G5(8#(a zHSo?dyvG4x?sU##PIFuYdar)3-`fiMv8o!5#^AyXhEyg!3|+TJS;X-$z)+N`bjDNk zWWZr*wp#d!>DY6|gX?kq&UiW;4~J2(74(9fmHP{+m77W3-vFt5TEIng+zz<0?S=!8 zIIVV^C0r1Ml`veEH5LBay2>ghSydfz3|fU!LVE1nXqv`K=@`wRrxkPTH6@+N8M3OT z&X{F)9tj~Rvqgm zYxQQWw$yqKgckl)hoQc*y1M#wstli;sRHM0gyXR#i502BNuu_=SmBpnmV`>BQm(Aa z;tLX-6-#2}az&P8VSSz3d`aZ_az^kP0D8MN)3s+5yv0@+_CwbT!@*T7vB5f4Q{`sY)uP&j%4Ew5X5Gv?4DvF{ig05~F6$w7UE9G*9t0?lSB$WhdRpk_3 z1?a(AqPqm{jsl(!_?hbb6#c~sADdf_*Y_PS2)xkqJ6rJDe`Bi#Z#^&YLnrEW-ToH3 zvPd7nna~dV0Z6#-^?LoSei*d;zT@}nf$f3Jdo%AQ@qWWF%(=5aTHwz91Ca0kpunAN zfI|!R6g90=t23+@7VWh_)>Taa+e)n>26Def%m!MTihhg=fq|l|ziFvmy>Z2r0-3u6f{?^{{az@N!NRVy#k1-JeUb-u&#+68HZW5rJS2 z>K(g_#89`$sCq}0PD|Tf zjEs(fPSkiX7>~QG0OMF*CU%_08QZJc0(s4L0RK034$5K_liOo*x| zaw36Y5K9ARWNlUo$y$csMI{KZQc6)YRrwSYO0eQ5EMlEFQxlI@tBqthF1Aid`7&qsKCMH?pE>#HXQ* zQI;u)D|GHckdshvURSsY5ES; zwm%)`Jf|NksCWCPfc7sGmPd42uHCU~SOXQ;Hl71(#F!~VmKsfxqdGx|j*m7MYq%*P zB2;=`jp+$2b!EkH7Ac9vwd==(_3$IbI=4p0Y8!tnj)JpEmfs6%{Pz{8@h(Qe&05oT zo6ToS=BwsD)PxOzv-`}TKq8eoBj#N_0JW^b=Pg!5c#&Sx36Y9b*9A^ z1{)+hrWaGWPO6(ovbBU@)Z)ZiA;G9>^d1|HNQX1gH>|W^>V46kM>`$=1LxP-#Eqec znv{HCoUL$uoUK95nbHghOJ@b4RYMSLr}IgIU@{P@F$gBQUKZ(EW=sovy-o+=;}N;D z@V&UshW6~@_51<6j^Q6;_y6|{+TyVsHg@(sF7%SYPi}Q=x7D`KjkKnvEk4M?$K~Ew z1&r3PY5~-wT&rng^?L4g5DRo#+Z#dH)!7xq!IOE~zZ&iul@V z>%tng)~d2xmiW!`2EWb=s#GaUiXy{*qR5puD?BHNf*|tn%8To38=@!*iYkiPQsN1K zUc24RaQnG>!(1it*|`1C^CpA-8?{c~4f}0>JQ|OE-*w%-9rUmS3@e4kqfr>w3b9{w z*r-zrE+$Cx9_&vPc(A_+O7>3{aDSL%ahr84{B)WzKd`XeJz1ta8mdOt%MKEwkg3v) zc*qFWj%cy>Y8kN-O$Se)+@besq>+l5L@I`rGyqr^zX1?d`B4!1ouiEDbb0I$DusQIJ$#;$;bCzQzma!ip7;OF>X&wOkU)5+{~PtiTisk2*mflO(`-r%DtZ3ob5|b`kJu$J0Dv`)qyp~i3G7=E^iny>B+w|aI z#Wa%w7oCWWSxYTw*joU3r*XS(&oQiLY1Z)WKRSH)2-m^4KVoZl@4=(PL-^U=e?*ty z_#qw~JbLu-058G2UHECDiw zySJt%R!+WuDwXe-?jXIdECHY=dk*_`turSk1)wsbT4kK;F$f@zX zpG=Tz^VK+hP9?{?uvOzi$oKmaPaj;3Po`;@n&XteTF{sCcLC}jD2R=kNHaES-7cWs zXvF#1MOArq7~pt7RnZ7@Kvau;UP;tU8Yrbx=}SyFS$v8$qxyME!V{LN>zrb0vPR?O zXx5N~V9Pj{%;Y{L<-G++6=OZ~o$34}iqDM3lm2)z4o4GK6$NJpDCI*&aOC9rtEpVi zz($tw>jiR6&37%waoo03x6cc;0xp_@8FL-Qx{B~JEsPSXVZuPu1yccaysm17LX46w zVWvjK-e?Mys>3X?F=f zV)Aj#zs8aaSa^EzJFupO>!ef{DeLIiMdBwXUVLg4pTB-ghkva=hyPE2^nL? z)4^dF2sw+r7MAPjV}aK>d9zYk-{1^IlXz}*;|jO_+Ip$Hxv^R1c(o#$0svmFa2p#~ zfG;wv;lNnt%ax4^UV*q+tT4rnwOHKxdZ|(_mp0c8QRdXSy_KIhy_Fj52~xpZVbqRB zpnL~-&G%f}1=ku@6pbUQU!9U?jzM+EfQ9IKQcnYGxY_H&7YuP+?05wJJL}MC54~Y9 z%#)d4E65A}uK@Os7wB-C@`4>38#TJkM#DZY+OmN?ak8p0huUPm8VKjrs}`e#b&Us0 zEP;s1B(ieSk!PuJjjD`wcm-2{ntersLsMN>OhJKwPSH)NShO$L91K<>J}Vo~6p*uP zQG8gu=zPGGI3CtX*?a9E>_>h;)i_?>Vt;McqLHHgN7C}S6X93O?Y1EPvRh8`yig09 z=y;K*RR(LU>t+>)*k~eFOldm0V+v)Dm?I9>UE?sv&zNmY{Nwxtn*69r3WVzxmOK-Y zjpgMKe=EkPAmQJ~7->v3$y*RnjaZgvGU-n#>_*Kz9Hp8XC3_r9CNr(h$WD4U8H@)s zK;^{BS?r%KAkhAsv_t;*G^0-2?bJb)L%1C$iWark$Xx51jPaJ4##`W+Q;(HNRUhb` zQ{0RswL#XJu8Jq}5|kKXchg{-Dt#fA-OU~EdU0kModo8C$6MJxJi0BH7X0agwBS6f zNv!K_yY9eiyK{cDab|NIU9*H#l7%WDNoT&9WteGnZJvm(v)u{Qe7|^%{h@(yvd{fE z2;&b70?HoPY^VN7fuIyn99a-(ePSRSpB95F3j!T~U=W%FLZ@bbQXssaBp9D42xpqW zmkS_%It?IZB<~NWDbs=$nQqN)y7gA0)u^2p10$ZTfsLII+LK|?RFzVtTH4u*-M+ep z6p_NFs!rh?vf(LL!qiFKRyc}UU_+G>6Z(lMI5))a=PGd*m0U}QGfF2Xc^)HY{<~D8=p!)`B@bq z;vYkOtg_5ViXReTt787#s+#E-05ujibT;R9zFi!TGKUd@I9gDuPcHhpWL)ZalytuR zW(Miwyui_26BwlI z3`tQ{zAQ=DccBT$_+hE3Y-y4#Lx`Zll?qPH#ivHrL_?7vnvgd&RgrY_*^4jMNfU7< zNThcxevEu}iM@0EfZ8$!NnAL<1}rZe_@UPe9oD&lf#Oya_28=44Y#%iL7>e6d#1zx zZP4N0Upx#Iw09dPH$bS^X`C1AczX}z0x(Y0vDk&my|Hf0iaRtYDO06_eAU46P=i!B zYIB`3KBU#NDxBG0JXYXY`8353#fCa3!Pn1T?2=nKTHxyi3A(R>WZx`|6>ZnCn{~Hq zcRS2)UD#SbflyJGivvX#A{GaVR6A_U0!0fSi&%%lEt(a7AyfHt$5~%QsM%W7Gb^}u z;&_si>#wJBJ%jq|0QI*QK;3m4Z5(M`Z@YH09n*d109#jeJ?kAyiFd4Lz2oB?{%KiQ zm3ebB?|44gpE$WY7wiviu(Jhlagh#z{rDu%;sk23OYbF3X!8wfMLh;cV z!2TZq>>n&Nq&Pvoj=Mv&JB@bx++gFZ_v3~%(XHkjL#kOc_^4`9aSQ}QDj}ZKFIpNg zt}2!2o2oKb;4Z2ujlrIr73^dkD}rJR_gtjA^QQD?3*w<)0k9tzNVbh-i=8%Z4pDE` z>+#k*3p&W9BN;pD*nXHG*s`J$D{jyjC06Z3f^8TX!B#~A6=@7+ThnxlZi+a}Ojk6} z)C#AHs9aTXZmrA3$(-fjIh$a|ivBt{%uIF%04feegMSf`C4=j*M(!vYO@@QXAm5+< za|M$9BLMdG;&ci&%XV9i+v?a(9Q=Y{PZtsFcONro408<`#hHW5z?&=ASkOo#OOKHp zCfz~wmdOSZ>+mRL3zF^7Y0MGrvs5g8=<7Jz_Vlb^zc0%XuXuYz{L);RDx$3l`7+fJ zM8AKyc)z?R)w{H4+TnC-@Wyvx8J#U@16VaZ13E=JGk{=`@zo6 z-K4p$uuS!wE9kV^cGro*Fdj+zg}7LPK>w#FL_9xv_=|=8A2v#O7mPPV!QDnG;wNo) z^)wO30RpmWYRF7%D%j*BYkFBi!8dR^jImCNn$y+w(xp;WXPsY2Z{he6iN+RdhFL*E ztSVU5t5;2316P~%TdRsDXt|jLt=8>!W+%1zjUOKh+-F!R!xaWJdKfFln>a=L;CF@0mMFsO~mu ztu}6x*lx91lk~#YyBer~*~|JGsb5Q0a9#}l1M{t@VB5*}CJ$WK7TUYrza@C zjz0;vg^vSP$r0hDs`^}C?c&gxIM?FZWGmi#Yc!q?!3cxZ#ma|4HmF^Y+e%U%lQ8ks z{@F3Gv-?E+bpZRv3-{-1;B-E_({j7rMz^)}6mI^;^B~{Tg*eS{dNiBbrmhOSsjjb8 z43T^7@(K?UDz0u;Os=GtgbJ^%tBScQudS~bI=?3B)X7&_=Ql-_Go_VvPBKKCY03){ z{~5_LlnPvsZ@b>rc|_RaeTj~?zF9NxLL{r3I6yN@2+xOMlz!~6St z4-dERJ=l&O96sE=0~a3bjJNOHeQ%OiFA5tiC-j^zTx)FgqtL(Bg?Dyn2fbd< z^rE2FZ#uOoaOyZt$d4kg=eZsTv0rc7TVdb#2hrBnmixKhAnL;x_&xt~{#M_Pa!%WS zSkS499{P6`c<6N+Gt;cqv8B&Ws$dq4#@>DUSfivA1>S1fm75Q|ne&|l-f9wfn#eHIA96OCe;}tAZ)KwLS9~-#WUlns38nxYHFSbdw3OG(4o*+fh=bP_~ z(|*wO0yhAk2&cLQ;I{RmUO?MLgy6z~j)qf_ANKG!sO}&N(mB>_NL=c+g)B=MXCw!i zI@dvdu=Ovb#f&pw?FRttKU5fxwcSn)!|-Oe8PBs>Sgt{htJvvRt$>`&5ffravI#2tHI4wgHs&| zwiuaJxL4CeQs5ZSp`aVO^l4Gz=h5K#J16nxyI94H4GKe?+Av}VY{KXFoWOcEa^d%b zT;D9*AOf&%H@hI=on~B+eh#cP+zQ*WVzJguEwQ(3+XzijR8{8HH2Q+uRID+l6eW$( zI3d;wi8bhSP#GF+!Z{D?`8s^o077dN7lWtGx9kG`MaLsQ)4WH2?RYQb;>DzGLewW) zbHr_?THgl(yM+*Ro%2E<>&)fR<7`qg$wX(x9W==VZ0fN9m{F-x{^6r!(dk2c-QPdJ)_QjG5w+Yue8~Fj;qpG*_7HD4ghRZtv-e=<(ZT-1 zecUN=Cm#eeEgK8>XqwTVX=nX0Am7Kp=7G;qAAb{zdsgyO(m(t1($XI+%v>~)#KsZ; zyU`&M8_8^ddz!+%athp~Sj4BJ8lSxLIr0+@{^FC55>QNjO3-Q#%$pJ_ZfQWqn^7?(b5? zJrV^h$3zBO)+O0c4N*1)FuIlqhEvow73924P7w@45>!KxRY6h}9Xqw*lA>dHRGt?d zy}yKi)N59&8OE_q`itga$3%7%hf27?9&P=DFE|Vx&ibJnH7TfyTqMAKkG|RI`@ZiW zKkoTkzUKzL{FZ9}NdeYan0vP{5#1qj6LfpKW5*uzVxiH~ci(-C9w0`; zc8awJvJ$21$;^cW>t8Cs`sJ64C%bl7g^AsCn~i4N-m<7vSF2cXkHhPAT~^qp=Hzpk zI4&y-bxLCzbD+*JTc+_U&#`iSa_g+Q(b2_m*Ji+-1?r-8vZZ2jt>+Ch5%n-fas5(3 zhUznL?l+3Nb#W8(4k&TQX|~S|wnhFKBA0mO8pJ@j(;>;Jq~Zv|Tw01@donGR@*RxY zrs6RyNrQ9e3WdY>1M0(AGq8`3JsOjf#kMU@hY+4^o%G7t%7Oi#78IEL{W$~g8aVK- z-t5r$!A^sfjxDUjp9I)NgWfmhl4%26l17~fj#(XUI@C@r=u)E?jXfJI)uBS*W*Tv~ zFFXZv$Dmyti#@|>I^WTrL3m>4>VkCw@-PSXe^wyb5}f9VcT7t#@=>1c06}sQOS;ET|@y?Rh+A>kzfk|6^^>n6~b)(+La_$S5<{vDyCi0^O~D4 zx_G?ubpBew*d z6@U(~cikqA>TY+PT0Jh`Tg;-WE6bXmG+D4X=0vjSWKa~FUelD>0o@so3D8tE4Uw$a zOjip>245_jdO|>tt<$9YWIi#K?!4gVu>Ly*VetP9>ioM4um-=$tv9+|oIluZ#iKSB zROQM_Kxut8W|9?rQ0@UYiK9;RLa42i@vly#n4u{O-A{@fvrIa9Q1h_9IJJAz)7M!m zJJJN#2{<4+thgO)(Yd2|vy19JuY0`($~;%T7C?sFp~wXV;nKTfQy4?aisbEO^` ztS_AfYrBT~)dSY;^J9(ZnuN2YiH0GA%O$OrE0xPLtW_OES2DE(Yh9pbDiV4_;WT`U z3Vaj2`W10`UDT8r*7A9msLNx5*ZW#61UvM%WPR65|k2GevV)ln0 z4<>^NwXjY5yj)Vg>17RWLP9tm4)~x{(aIVo828siVuw8_(n|A)9nI@YhIUYW((30qr!vR#Y;h z(Qr5#j-%-?!m6D~fdgE)MrSCpBeavD9{=@=C#Q4&HdxX>RcJ|D5DnG9ccO*|gnD6d zdx|?f;{0^%DK%m%%5p;%?qY;W*1!#~aWgRRcVEQaRy2vDQBF9hTg9nq3TgezNCs(8uZAw5@U& z^g0kzZ$(>C5cGY5`nyj;ed%maV~0z(5s&IQAJnQ`(NtX&d5nl83wKgBHfg^{MU$5m zV@1ckvJI;uNmp>-k|6P-B&=W7%ABt9FN@2(#xE-?8o#kFC|68@=cM&3Wl1npndeMH z_&LLLbHfELzNy`9EU>{%(OM^Gl#w|8(&dHaIi84f?fN zkIBW;_3DuH>QPp&MmTL%QD4$auYft)lwRI=tbKR?s-Ts1m^>e{idjQW( z$Mbtp^tpy@_j3&6ue`{1-ON^1ecQ`ROJ6Nc*I`L|9URzhJ;N`e*d5ImyXf8HV)wp# zg?%{d7{6!_8kv^ST!-*8afk57-ZJ`OcL>j2x#09y<^-pIx-f~*u>th9{Q|jMZ0RKyj%$5bs@Po~C4}?j7@_>%6_mmM z8W`}OD6F*sux%`Z2Nx=y(zK{thfg1CNcb{-SXU&A?LtPtQDX`1na((6lHqC`5`jz2 zu`ZNjAyFJJSu7Pnp^Kmri1Rydt{Q0ZopBOgpvKxv{2HEwv{YhC&+`#no|^MI5q9(#vEU z$wv&3v7f;-1e{!H#`?WVWjTzhFo>2+GEb6G+)b9NgFc0dNM&eplG-IOPW+|M1wmp- zf3?7p{zI^&-!U(L)9w-kolciwu&`>KK9)6I#(l!DA_9d&qa2Qjwp26nn$=WkR9cdz zkCX2ZlF|(*Z5GqE2qfzoBPLKNtaiHS37q*5#^~lAhm`os5En#Z#gE8fItqfEZvEAQ z;OGy5ymwdFPd@opD1Y^NC~>&}5Suj061kz136)fY>jDT92Fnk2SYGjI!Kybic= zdQ!4TI=6y@Nm1N1lgzEDt>MF>=@qb?JXXQz7?)Fx(QwR4aM;RW{f&Yo{I7yw9~O8` zbr&-?UE6Wn-A>$szp!dO!hs8GQH6~4Kx2QRc)zqLP#M;JKO$|-A)7Y_rNjj=n z#5N?8c~WG6Ei6Mv&8=E3z;m@}dy|&au-#U@W!KINw#}xWNBH^IUG}gG{34Tsv&c0svsDqA`6PD zTXMCkR&`F2EK6BNV=IBN6m;V84Ox%DHfRKf&Qdq3tQf_n^kRjielrNdUephrz`f>q z-5}_NL>u6=eaCBgeN1(Gy%6aI;v7832XXC$G1Kp}K}KwU&VJ?`_WJ6Q6ct)^6 z-Q3tp0+9?=z$CVH{y3Y9Q@nUw2vd0-pI-dWZp+oXjHjumocZka<8?2;Rv_6w0+PL7 zAlVIDr#t51i0NYS0V3 zM(8(Pkm-zGtW~dGy{bNEqE3KVNj5R47QmH~v$ZeVgTr?)dC)#532lsC8+&tK3!S2m(|H@as zvgbcK+}(Y&bNFcI(H`7&Z|~vW&ffn1u`c|~LNy+FelO_xec##gF;5sqy&&-Ydf&yT zgU$r(IdF|0)jM@5uWR8aBerj{2eu&5?YX$u+1fom)lSL_A zvx>$*$5*V@z+M2BKxw}p99A`7yf2QI*0t>*Xa%(}v;il;C71R#qvO!}`B3;F59wvpdD3$=pu64W61^ zYd5=bKlT|VI??3eV^(sCLnAFK-=3K%Pc_r>R7N0TX%114bZ6S}M;1RrEwXr8(&lcl zapCOt0`B$neInn2l~d)vo~rVhleY14vc<-6A`{}^mJNcdot!!ir_!rtl@S^Sm}y<0z0WblfOQ;jmuM+C-Cs4(P(u*L z_CC~c@-W;DD!i=Xhty3?n@e(CoVwL0)wqd!1y*?w?{b#bR7DV`#Tpxa+;FD6ef(jy z$`RUh8p8o$9TBxtfcnoV^ItD8+!%7dT^Mq=>aN}Bc3r#KwL9m<8YwP_iUIJl zE`T4ds1{C-Ok(7U5XaaFg_eSuqV(&WyEp_%R%& zqJ0`7?hqVm8(w<89e7}?eahg&VT@WlPLfcJ(R9c=M7dy@cVZUAw zg#933eX9^_Oag<0hJ0pH-*GOmMF_ymRZRz<4BRdp6{^XG%!BW(nv#jV1t9esnr>AU z%aBb2q!qvh&rR9n0q+=WJpr3V;_9akY&YmQK@DII{C*FyCUT_*K!ckW#q}Bv!is}( zlJ*01%iUxcd~n=JOOA@b{vQj7)4v5^e|OVK;CG!8q!V2cwZc9*6!ICm&A6 z;|P8RZk~UO7{%N!M|j)N0bHFWtuPcz&UF zlcTuZq|k?cR$2!gy+)han~El&`8ILHm9WN(S9nX1aL$5iu2;&N%dct`O_CJsKpcOj9{=}xe@n}c9 zWd2ltE9iH;>1a$%fK&9RyUnpT9&c^+d;K8X8js1p_OA8&J#x9>w3ko*|1Sl>@Gs06 z-HiJ~J2vM2Ta8-0Y4Bn~cLXaAhNu_sose;ou!uSS1Yw=*p!1Ik zeC3}{w^TWvi=+YkPTO@lPQ2T~yH6K{`snd%oV30ntd&X|>zftulLVFDRCL@ON1{#9 zG*Obkz*Y0txmhsYP#-lIE)5!tPA zmX|AhX}Pk>tzUk%gmVZ*VNH?N6?Id>Rx_zm*?e{Rm2&x&mtI|db&~@#s+giAi)F4{ zUIT@DxwKw_wQ9_T8~(qai*}KIZGfd4ckOnk(`>iifUuy|zSe5C+i!f{N8yApa}YS= zaW4Qo#u5CHuhr|VR{aajW~0?6B^wThVbEr)NsL9SIV}L;w@!dC)2}}RApE{UH@1Vj zRyhr~+3du{Rp$T!Nd<~EWXsT1iahijy~v6)I2D`d&AjWXWVI`!Ar+mK>X^Jt3w7-= zE+Pftgdh8{ZCdsAo$Y(~A3QpI^l<-RreWFO?FkKsBl$U|Kv~D%s9;`Pfu>Dw8nMOR zXMWU4>hBj_FDPh06gmkeH7jLM~Yizu$D$A6~ z=QTr7d1h;M1EdLes?-J4!X^)4Nf`oBMdf%|(RfwnR1iZDQC=6pM>ew5vf~UgLCYty zL)m0{^VZET-F_1wez=#Ro)YFiT@Z!7?z2vVFeV(faJXwf&JzY&q&-F0dcvw(s=&9ud={(Mjn}{HNb2V3xI)mzI9A zK)Trs$W8-Cm9-j9Jl=IdRZjA48UUcCkRFG1RYeiTfRx7k>883;Q7bsE2$X=V>Ox7- z6&YDaP2GeiM1=blyh1ZsAz2T_+>YyjdZE*=Z3WG(D02G9 zDhFLR^6CMy&0F}%9NfQEpv?aYDD%yN+!1RUZ8}}I(Q4R?fLctE&!$&kpeJ1LIOnr`@(8JWE zPfdkv%g_i@i&17Jdslf8rkn;?B#=h>b!Sd;;iHyy$`Bl1X;c;-^S>u)91N@H zH~nyHI37kmM9SfqHsnCT#*w2#R#ZP3#hZjDYkFekWc{C~vOX&dxC^rW%Y}YA&Zq0t zYi`}H<6go)|NPR@SK$K-3Hp;YtR`1<97|=ejDo=?8PPE|FX6;GrtZ_eP+2xqQ86o` zaHTBp9KUjzU*Q3K)v^FyRl;skRncTs&QO{eS$}}CUOOr4soP$TWxf1i%leI-z553b zclP%7K-~BD_ICGo_IAA4^)%No9(%>CRxym|Z)(-HX-B1Ix^;M!?Z2TUHGWT`caK2gS0GkDdKYZK%G#5) z{G$;(`*Exc^f(%zF{n?P8Gam|{RQ~FkT{|eK6=ci9}&w)UK94_V=xsrX|pnGq^YS>iKTgn zCZRSK^szXir&Sf5tK@}$O`C(teL$t#8PqefNIutd$Lub;*w1`OvUqs-2m}!QD_FbW zaBaVpzu~ygm53d(&!A`rlazT(r(0zuGwnp1j%a3CP7zPWL4AAz{eK>#|F2R#JhQuB z2U$EUltt`Qvg=LI#?3~zMvut<4UfPnoJmw{gy-afo>eP2)=1GRn+T~HKd^|i7jZ>~WUeR*wNSrsd2x;d_*sMy$)f&DF{ zrW?0gjX|?O8qaQky}dgJ`}_O5`*+^HeedqK-MqKG`}T1A?w!%wyKjE+od-K_-@JY2_Lpwn+I{o>op+F#zI$hPZ|A{-?bFjv8m5wP z_4{_%@7O^wXjMVwzTRpe@p+C1D@GvE!%Dr=m`E99WJIeL$rmW(5l$d9$y(NO$HX?C<>z{k<&d{mWOEH_!k+pj~CScdvhvo z8;EwNUU%GPx87={Ql7~8@PwnSW^g~s;66{vO+}GQSJu{7McvXg`T0$?yt=ZYU0E%? zc!ls`qEQC{TuK#@BNu?{tgcJe@p$A^j7s-?*%(t$6 zE7tnbd%yF~zWLsJ@I<`#t@MeQhx`XzlEf*ZDhPt8mSoWH%*pCZz_p$m6s_)+lNg8`Q2l48G+_;b&^#4LK&pIZjS z{`(93xh{Iw9eA3kM$c(4{n_W3LwEBTR(X9^XKp2Yip;rM1&2;CnA2wdf98eR?b`nU z03VA81ONa4009360763o0N6^^y?KlyX?7>ZuCDIsqpCZ}B$HR%7g=4^U5q=(9AL3K3(3cNC3&nFNNWqS2GGndU>FM-mf?SF zKrr-|4P*6(e2iAsVabNqws!sA7ZGHT8O&s6W%ra`WirWNu(~>b-+S-(-uK@76h)CG zQ4}hQ49^rmT(KZ%o~PaM=9l!NzDxh9IMh$Z%Txj+i8=wTIrLzkDS{7(I`N*5PR8;)n=SNuCr1K*eBufMC&{v)C=vED z0w2R(swg62k7&cA!YUPBf}e0*0*Rqt%)gVqkuJUc+RYsN7m8xKz)`2r-$V4RuBLhV zWC2+51@Kz90It380ysR_0|^(v&erC}M_-+}M+H%9X`#N=xt{n2PJ46jd1qoG=N zb>nWeW*UZJ4lJ|Vi7kNtD_j6?hYR3Z>O12FAix6nHxm|s(a;*IQ8Qr`n5yYGDffCR zl?Cw62R-e1$Gy=>MktJqeg3HGay-v-j3~+yFUf+87z4iITM{U{QUO?DIdJf%EM!U& zypQUy2#TnPaE+H>(I|eRBfZ7)>K{7QZ+zcLgWi4)zR_s(b03CPy76F)jK{pE@Wfc zp3Y`_z0pxdI63JFRln!*@DHH{0f84Mz$-Tqcm*&Ao#z>up&3S?N&?)i$YA7oK~eh{Cwai$b$gzAJhNrU&2y{Jsa5@NU=jJoh}<#Sr-)hRELpoW4$eRsAZGZu(H*`(6 z%v1`1e+a;ztMMn}lofum`b zUMZIuhN7slR01iN%i#l6J`Z*PK2&w9Yfr#7Bd}=z`xOA24n6$E!M>y**ffA0*yese zG}43NunT`Y!#7OxuGw)rogSEgZolQYX0z39HQk{Lut<^MCb3ojzIiTjcR(De01EkaNfoJeD3m-TK^vJbmc*GpuA4x7aXznyf&9}?h<=0jeMl3Tc88!j+iMl^T^o_1&4?}H- z|HVMtUzySN%Xs($lpXZv@U^qG0hI2%j|7rT@fMINr|!3hcR#4zxnr8f2j<8hntfYO zP=BKBe;zJ?G-`WuTHAL)+kY)V+cje6HC1bvwyM@rzY7fS7_dK|()Ea{S5HQMA$trj zmUvF0WI-&862YACcn%)RGQ(7qJOhdjSj$Yv=UJBJIF3AU2>{QL815~^T|+ZJ(R(d! z4dxOysjBc;jhp+x%kj&zfB!Yd=8`wnIW9$vM+sGaD;fK@dcphHpM{mXD zG`@3W%Drb2-1}z$`#(s~d%Iz&4cjqm4OQ1wYl8bZzC8=Lk3ij@pY({j_l~_KP7-;J zE-|Gnns6qQ%jB|DaizGDO=olO@L=|Z@{MA#EQ>;!7HJ6CXf*ZYf8ad0$1;n%8h7ug z@OFfJF;w{CCEWXywe?RQZ?Em_K3QK|TYExy`~r?WkST>nY6r^)U^YRFRvUk>5M+c}p}1Q+-gMW3*(w~F(L?Xl= z8b;*d#|A#~@Yg~Qe{oHBkx~J{>V>rWyt`u(dH5d6>Z}zH*~E2O|J_&T|SM zI&~3%#{3LP(g5c>2j#H<+--!r76;q88an6sUBaaYL^-?N^X`vH1a<<(V;K;7H|UK9 ziD35^Aaa2aup_gc4bA$iF|aoP?7x%X%~dq(nyCYw(<}$v`CdrpejiXj)3S%*Iv<4V z$5QWT;_Ce1RESa>Hg?SQ&b@9NG&Rh>9=^$K|0A;{L& z_V)JUE&Sg6boAi7kHq9Oj~n}JKpLI{xgYgH!22$F1H})%eZTE@@94F=RnxR|?R~>A z4bSPio?&R{5bz3IGdo~JIvO$Se-R>I7(4U{@@s(nuOz5Bz^*pxbsL^(wq@BQRrd#| z=jUQBgqT-*K45-Sbum~Y3ZB5*LyJx>!AcbZYmQOfd5S$FF^6s=@+LTRb)xa=ixIiU z>s#Bv*0*=Iw@EGz{%kJn$OE}3f1lR%b2NNb2<}HQ+=alrUypF#oQu1mH&j)%fyp^W z&9+h>1Mt^hJrj6DzDJNhA;<%l>v@8}%>iDFfajBd7lku%J|zIS69S$9_bCH^w%r|* z>tvI=xPK)wWp|4SboxG^@E;^PS<3+h)NIU_S#>?N2LSv9Xx~{0QjDBOVYZ~uJ06XC zezhub94eC_N|GciK^QHG6`3KhgpL;A;I|S~xoHaix-2V0l{PX_hH46kBGv3{SCamR+LrOo0Xe z$x?KAft=VG5<~tLA|JE4X58kcLYtdf6!WW)2=jiw={AQRFf^~z>6k>iX4i8J_)UYe z1drKwJDo-Hp!0=b{{O@<=Sb4*(=q1%;~S~e*Z$?d`d6nzC}7i$4ZON;)m0<)UAXu6 zUkQlTZv*rv7hzgeWLirxfCNdA{AB!!lLrkyfC<$w2;U}u!WyeU}uG*CiByLxgwI@=NBV&LX! zYg~AVV|ZDi1-_6iW^=`N-=s=(o-R`bn&G5iO^Cq*u8c~B?5UEyRat~p5Xav~{A*oJ zo5BADHpSc9ds`dZ`v>bg+Ydi}^znm7U+J$sUjKCc$=btp{5)9`yTJpljKf1%An<1& zSH>=^f+YOs%3#n3?>X`Z15B@|#@(tB7=~7>+nrt*$y@4E8B-=qpuC#;`7!?A0{nXk z_#3!CqNz30Y&e!#w^93_|52~|mEOmnKIruaA3oUG+t>%^ zH)=+(QSx0tevV=XyP#O}K5}dq z5U&B^x*6XV)92FcbHMg|H^`Ea{K-WUCo>Y;7W3xUy3B&NaOPxM2-tr!1~xMjRrTh8 zZR5U(3Xzqqs``0QhrtvNME+Q)9%D{Coe`?OAf#PM2w|Q;t;~V?;v_y2fck?|P}`S^ zsK$`Y0C487`3#v-H*u_PlZfg75cp0a0tRkS;4XCCGPKmU0fX-Y26NJtX)g?>3Ma>Y z8q-(Dd>S#}3ob890xMuPQUMwyWA;j^a1}AYL=qGRF0)`YIYptF^M2WL1TeUZ7#NFT zaGq@ib?dsl9@YU|Bqe=HBCxUApxf@_HbkB?8l zqh@{Hul7J6(~&+tI}fK95sHFfjG^fgfGr|{dRQCkp8ZhOyL!S#R649JO9SYyEdu&N z(w-}8YipaE8|&+j5%l!s9O}XX|B_&@l7JrZsQ1U9PaNuB0}cPB1Pyl@YD3j@9dun+ zjXD7wfuB`wpi^0Jq-iv-K3_P2cqv^ysvZf?A$+b1e7gEfq$E&sB}!e1U|QLUP*m}v z$;l*%v1bHdGrIL*Z0FgD!+tG6jzCL1#1U+c&a>{?IGg*!=7YpaXK9-psXie?vvFKS{EP8hLQFeBA zwt@U^Zf|X@KY2J0&kM@DeA%=kzY8(6arcf5k&~ z_=Oq2t^oWSiGJOJHBfVEb=-y4>^g~@kFTG?9f0pmTsr2c{hpVpO3wlE9s&ON880#d z&xw)%G)oTmCPP7nbsnON$(pyJ-+5ofVhPQ%YIoF{djF2LI<^DqWj#K)-CnOhBwBfa z0+VxO?D26wJM`*)SZEpwUOErp#e^9DSHfkWCR8E(Em#KsBw-m?#P~zp4y!-|JCOQq zxak*Q70i|$qawMh&?C+U_Tx?Co!TJ_{j|vH2i{j6p)}PvY8NnAZMZhK2A6SqQWI z-?AEYORY7Gx}}+^hv7mXeMLa}(^(0di!#Sy(06j;SF7Ly(xlAz1pLJ3r~LwHR^fOq zk_t8@iDC%49fN#|`xip;SGa2KA0fSiWh`4;@DC*J-}DqSa^x- znMx85aihddU{}ov?u6A{3vqunhWmp1W9LYq->$p0xxTx*w}%x=yW4wYuXJy38{);i zt*gd;h7L}1MS}YuhPaz!YClo(uZ=s$W^vcdhNC+q%+n1g^$c$MJ%E0?`XwFK6IV}y zdP9uvfX_oo=7Zap&^*gt9!>;R=ZqL#=b*hU@TWk?{u|U3m zWq~}AaWwBvT0|r1266r3Nt^S8EP@-cMSv=QJ+=t`CM<&ANLU00$?0hp5IP;!f|2?- zwDK6$OQ%(THlx?WjGm7vogkxka@5aMlKLi=5Sq1*2TG7q=QNo@Bnp1&LC@xBPOC%L?z0JQ&M zVlL0H8n$JE!?$&N9@;0kjnf-}$sb`s1Xj%V(wU=UPk5g0flE(kfatq|$VxnLF+nJW zyEhEOGA!=8E#18A-t4R-qJ0sE|M^JV;UgKlH}o+An?zylwyIrrZ+2D)+8fsNznp4;6_Bgk%-??qtbw^k0 z<4dY}=eDiY)VgEVqifck+p4P9;qNo*an~UQi%YF1Y!Bta3_a2>T(C1 z&+cAWUw5!i$}GVR?c(anT}klJx3#kZhjH-mf5&V|(2hJ^TANf{he=_zZq)RKW^1~Y zGT>hB^;9ZuNak+|9)aVihOq-l2r+RYDaul%TCG;b-7HupTd4%KIAYlD5EaZylkUaw z<><8>>@j0sTWnMC08p=aYLFOqw1k8~ys+|m%(6V**^MXzV7I?_`1I-1LkRr#4@vX+ z0iqd{&F+%2*@JP}>>k!ml6xkXqu2P(E_`@*cQ;;G*@+|9pHm!ps%`NCLbe8@grF1d z48Iaf80@~8O8r;KWz|LlSQz%AJ2lfzeLMyHWYoZMG-}{DT@{WqRon_X3LJ=cl36NC zjKtDXnU#6DAagu`&Bi84VR0;JX%VDKfHA3_mMXLql;QDY1;`v25k-;|GOCE|FN)nPKo`*qs?O6v-gCXAr&}wb@Jyfqd!O1ahG{y&3Swpzc4Bm;=^; zDAvK)0$r?Q+uHYG;d~n$N}5k+(kDmHfkI^CiQf6Te`W@_QWf%mtXRfzLKWdnzFOr( zp05f@rMQH^ib4@krD%yMb3#E8LS1sYr;w@5XpyYBEQ(aK>P6N&Zh?l+q+M2&d}jbfBWgvt=;YIr#lCSkG={jzQ4Qk zXlHk8ZGCU!$=1%pgPq+CxU`SPVeQezqphutolUH}-CCr+8X^f3VvCOBVQA5L4?EM_ z@XYJ_t40ujHsRH3+ik73JGS5PnqDWk?mDZDcB}0LF$q}{|7U0h>f?@@$(s0|U`_n> zgf(GceZ5sP^#=ZS&06Zau&};|Z579W{PDP-DmyxPo-lAKzA`S7E|jYh`~e#Q-%>>a z&mad=Ouz(Gw5c(b8t5Jx!m25OW}>gsE+MlN79I*Ont-e79?X9oL)+9_rP zcYJs**!STGc?=qB@GEMp5y=!fnU*tUk8_TcECLHMm%-;YBA- z5A|q~MD;^c1B+7OB1v6pkwntQeWLBHV7F+Cu)&}Y2fv~|oZWrWhqJzq)2vAMg?zC^ z5=lQUi5_C?UHX9w!A0`PpU12NJ7XRG9IV6F6Fr8}sMmG7VVJh5k`YDjYuI;zWuS%Q z$&xsaE+n{%3JV@z#FUd#!7&DGMHbNWlXbu-3a9YE90XYu1y;miMT96I$5bSocL9__ zkr|Sqz_$fSjN68X5$v{_;1m|J4ei6t-Ms@a<$GZJw*kS;LmWT1w@;wmI5^lk*giY} z+mFR%xGB6#XyOJ^!My|AOn}?>Hwf3nw|Dl=atdA>r;WKTjvRwOWF%cQfDWrx$ap%O zOh$SFd~fn@l&fsVZHE`xhJQP>4UKWr&GeX}ZzkjzOpFV3)z)!dP2F))Pg6n9l{AxQ z7|)u-w%QDVy$7(LV8PJwm?b`2!DJGl^O$20S^WQ%M0j?3Ra{=cFx0^W=&D|`4O@$B zfQ<=9({fb9j7694n{^XzRVUYsJMY`NZK;+yi9#pI%JDVr&TXp>AB5<1d>hs)7=~pz z#_0^V+Ep($C=J7jO_1G9?A_Vhg=dq}gHvnt!V;xDCNT$EkzqJ64W6A|nU@z5m|l-B zz|6FB!m^crEwSs%vKm^=bj*gX*Xj;wvHv*oPFSNb-nTrNs!};^h%UlPKuA7=CZcfCdThu+=ZQ2{Z~*F;Px} z)Xatb&vYhY;pw?kRqKs0JDse9)SJn@zXsN=gCWA^&zb{<2#eL0rZW`hxt=gm4LlH! zmWX2|7DruHgMMCxJcvQ48us&6X44bVC=T&ddO`}*8L(QWG8Fp(?2efLyBxwU&%oZ^ z#@*CTD&RR+ZPGE<9XXo0D)ydYO-rot#Z!%`GOmvT&zS?0I<1yx_nM|zGtFkpZ8p2zVWUUJ@(gi%0cc65MVdni z?0*!4U6?6W{KsJRe=GrZJ>1B`I%UT;?Rx4ukj;-@!IGtP*7MUreJU2GRgbfz(f35c z`cy&k1ss-!iy>GS&#$&ZEiTj>OG4(YH(7pJx)If=W#Wk!TSSH^WRIv8`N9{a%Dmc>eS6t4^$iz zTcrQ2Q0R>{d{jO0Nr&O_XoMrsPwD;@>}g!X9Sr!sRjE{O-Gc8*wF>?oQ&^J73%o4z z6H=$CbKw3w_Oig*gH!+P@bKwppTYOx)2HCpF+H`nyS=l$zrTHjaEIYTSnUveg&m9C z?l|Lc|CeK=j-AOkECbyCMPjN6{s0eeIA&efQ$KS(oa5mUwT}Atr>1$}rPC7}E=cK; zg(5Q?kO%}bK7t)ACD_5j?eCD%*)>Dgomv=A)K(qbhN|Jzw|bO%nCw2xKTtwMONRaBJYM?fy(fRw=UD4_+y>7G@kN>1Ux3lgS6 zD~eE&NOcyL=178ANAh19Ohrv%kcK_+YQ{<%>XdLcgBJ}WJ z=MWR52V@q_-p=l$?KATobH($+ar~16M%j81iA_i7T3aW2Hn^cLKEiw9( zSWy79E1@4Uzo43my+ANL8LRhUu;0B;g3P_m-JL7a^2LJ8IkaPf_74EaBpNvMwqzO7L@V)AD?2bE$$(4B`ehhagQ=TYK9#2nU9?f($o!Zj8Alg--s)*pXs}*>}31C>VLb6K=@#eQ=c}2lI zlZY;y1QntrV9T40WYIE#YECh^C~>HLK8s-I>A^0z@u!FIctFZ$cXvs$>3}%*&-V8} zIl#2jE_!qlYwYgrTyhcki4uerVU%I|Q4jS*$RdcgwVMewvBuk})K zFpPSwHlBA8flX%X$0%E`7jJc*)VE{>7Y7^g!lWLy%u>ZSm)^Y2C`wVfsx1s4a@T># zMIEp8uBxA&%sg+$a*J#)p|O6_>vgZTF=8yyj^!+fPP=a3{@}LU>iUff%rrP#$c|;? z+&dwKe9pKI=3Kqn&49f_jEKu|f zrjRcHA!jI#;RQ~v$gC{05)VdODKZ6y;VF?(Sel_Fh2?pMqQZ(caOhb6R1r}B1vW3U z6^WMR@stb9A$99U0^CW=9gx1izrQi(u)8agh`EnQVt8ByJs5R+?e}r?AH30T4f@?d z7oNel2VT1mH}|`3uj^yOs@sQ8dPKEHr;^FNQQvD~Arwe3;366b>!O~gP|v?Ph8wS; zoX#nLZ`U9W2FzJfHANd9r}8MpW; z36~c@{PoyeoBBiTp{6B7>WPI3-g%&1fuvByud43(EFmiXJjC1&ZTl;!pC6BltIpXS zFv(C8$EicOZaKO-(eMSgf->36$n!Z#QF1x(z&TJ*k$$sG^CBa$ib6?QSt=K^H}Vpv z1P3FEv_uPhN#Z%>MzNHadALRipwuNf&tuVoC>Bdxo&{cqZz%jqQ4o2F$uKbu*SqzF zwt_eO3qz0>i%RoC!c(7)SeyXkj3V1YaB)wbuZu6CSNPj5Ju zhwX8z9iUb%&+7PY*KfOaqh+-S%fc%vsV-=#t*MaNPbeQf7lb;3pz4(2>A&W{;z(-zEs%SMa2~t&pdQyf|n3KE+sR<5Ci^&Dtt;K>Nl1L$cn@AxDqQspK zLXw$%xW0b>^R=<=#LPBYZLQU5wi_-MdvrX{?f905$I!Kc9LM-^{M8ArwR}H5GosrK ztHiq9@u;~-2-+th+Wj%7o=j-`{sj2#z-)IFgXza0(+Wu>9lzIR$fz9MLO@lwZqI5o@qzp5b{( z=6O+4ayNOoDk^!bxuPokc?>v~q8t(|?An4nXKQz7^U22JN9XC#3rVDaezf61)QrTT zXhvH^)m$vQ0{3fsZP#tPey7#xbecFU{rz?al+* zN6nn-18Nw$iX&2OBeJy@vb4OgB$7e6f*7=8$U*r6sk01NDyeA*&IM-hND4w((aK^H zn%q-~xU4c35p$9h9!Vvm`HoXs2xVt_aw4REq~q=b{<(Pd%0hAvzlOtmAj5m3djZn_ zTZr^9RPVTBy#bDOKY5UY*06O`14nMuoe1mmG&>f$WQ7dRt)y92DhM(j0!o7(FS8UQ ztYk||OK-eg%)diUf`zb#ma+wb0i|AnScl3{Y?)>_p36fZqwpfE1BP8$xv`SX7xMYs zGPj(4V>vIu65_E#l$(dO_0nK%=$_lRdV@i)*Y6Al{d%|6=^Cb4tKGS4n%ZbI@JD@r z&>OVg}tIcL-wYfSee<4`^FoyLk ziMk0`A0%g@LA5R2vKuC_s7r$_NnDyQ@%eI@$!Dcvd5Pn4<+ls`N+w-qg&ZrEa-6VC zuViv7<(!b?iZYuiXGQMa0^BUFD8*%3DJ!rZishg?M=k>s7E47~9}-x9;86mf1ol)O zItlEn?$J$@2yCFq?>Fq0YT7Mb2QsWSR*iaPwPUJgv(;&IIt{he(pFd1)zwwgH9B>_ zsnu0r!>fL?1D3vNx_-m=;my@xL&7xL@p5V7NoYY~# zuMWrV0^A|k7c}A-7sse5(MhjZ{ERkR3N8>E9ocYtV#CS%kquYKQ5INQUkSN0vStZy zhqpv9{Zeio*xFfO`=3|pDK0T9KGh!eB29D&(!(>x{3$E`<*^l?khJfPXD`iyjRUo9 z9l%yKRSU4bDAW)g31Xor2nD`OkOuF~2;>+jSzIo1(wpU_H;N@jS;;en2LNsk}rkzc`#jt zcw@!x`gMp}o9#OCGYD+`Rfvz=j^}%()Anw+%xWE8G;0m>gIlYv+0iTY51K8@_~3&N z>IRTE-EiLfz^pZmMzenBwrQ^3s#_g%wcc#hJXdcUaF0I~3V%5v<%rzuBmrv&6&tKL zp5AVlx>36<+MwCI66rN5GM&gkM)|w#By*h4d}H%-iBK-=Qs~-=cPgW zJk^{hXA!!4v+timJBgnq?H;SRooJzE>ouz$*X+?nu$F;di6yaglP&Vh8=O=wUw@tc zQZ_9WR!U_Sk8cN>B`;sU!BT=;l4u}rWko?7ErY)ox$+x1ilfu-B6+87h(N)sg1CYP z0y|*@4(Px%KhuYfy# zO}FIRt9AoiaiiY83*Nbo1t9IV=eC*2X)hoc31| zoc4bRy8T-TahHW?JE~14>FM=QwJ*reB%F;cMw%T)UD8a{#VLGTvq^seI_HASk(^hx z!l8R+#9#{%bX%aXsRp7hJj1HUDAdbO$LDBgOa`Z#o&(43j|QQK2Q|KTuQwbG2O|u& zM#Fmph~*&ag6M11?~FQAn+Xxvv(vTyqXe)WYg;y>GI43gF zGFxUja-u6QV~QB~HO-UQdbyY{m*6{HVp%Rvm1&+?kr=T46+WksPB^hBWNA!CpLJS$ zEVc4g0Nu7GnE%Mlc?|R6s6Xru2g49OkUj)fL7%%0`t7<-$8{mZYJ(T(^&9XuywkVH z5o3d4y;twKLx|JPIOdaJ{%0}Fg&EB84BwwlaNc@Q9b)4tV>m$1NPX|M6ggw=!l09U zn#4jh&Ed%>tVDMHK)KT-hh5=hkrp_JxfwwyzeY)9SqOA4CqXbRmGX?hgIVCRr3@tq zMYbR^If`NEVu6<96WM0Ym8&OJBLB!iCl!26pJb-H?Xa5I_w`_i7Fgt_hx851b>kKq zYiw|A*^bxg_!ggn-j5WKp0wmMKwGj%SN2D;BF}Mb zo~H8U0;qou=zT6t3v<*v0ddTr#yeV#mj>}5ZyN4XxIqe)#LNgS+=oX?uEKYx(P*`i zymvaS20S-fK-61S$Hgdpq5SFJ3K9S6Bxye0!1xv*{*MwlI7$JxX-cdXA|`Zi7p;HL~3ceZnxb+rZldW?X)|sHfW!BrwQJ!qt}~t-GNKZ zdP}W0TdPK+-Dup^>uP<~T|J|kgCOxIr;$i4goIi%2ojb`+Pc1bK_vV!5?Dh;bdTjE z>tMJFVID>t6+lVLYgU36i3n7TtQsPf{xAyB8~*McG>14 zNPOYS7wlV*!=}7&52G8FOZ)C>8|*AZ!CI%~+;vU2wc1z(ir;B^rrx$&t#+%S0*N-v z)iV`vibQ{POrj?Rw+e(zeZXLiMB*CNpy?cZj!wtZhfgT5$vU|wAI-@gX5+YB5{b54H z^f%`2R2kURrPt~WgPb#gU|$&PEG9!@XAGSRmy!}8WMWK^`G_S#@Bs}25ON$%NB%02 z3IegeDw|aolf(!rV&C^5kQ$OduvUh_cGXleK=F%mV7m3W_b%fNwUSpXQZ=7y2P3YixnBBr3rj6^^rjoP5e47?Js9AvzcNwgYo zK&@s=Qjj1=!9FihK!!bcyXxgRiMuuM$n38^1W2P%t9LIrWVr`XG3qpdGDNor-2kI0MG9jE3ppZ}5^}^Pqez&!9CI3CN_K z36J;SvGRc7WS-Aq!@bOjv{aO2N?|!(79_Ep<3M*gVAd62&;rGCbg4iww7|10oz4~V z0xdCA3ACF@0{iT($#KwvC2`~Osi?^nxKfME+i3xBTJ2y|>iQjE(Qe1b5C$2w(T1x| z2R`d`nh?*ptE;ZvTx}zrx@%a?j^j3L%j+~71cSdlgTb{6U|{PwqS4V)HNZehT?G!o z11cEuWxxRgSA3rm#US}ii5y2}yn=qwVd*Qy7_LCUPf%ly&E-q1%%$^`IL(z0 zF*Q-Y5EOnKiby11B+@bJvN*3g7#{3p8T8i!={$;fh$2sq9j@lrOL3 z%BAu$&GI5g%V3juj-x~#K%z_77f_mk4gXjsSx|e7|QZ;GnDbOw*{{B||)V5LmQZ7+4q@R{N7{wO$V{V6fp0 zdV?MY91wx@dfjfT-8Dc=uZNW~ART;Zz?-e!5IX~=NXJ?N>A+cJKc1YR#gpqa0N!%! zuter!8kasfd4|?XU~!a#AO>+Dse3D9)F)SRxThvEJeCZDL&6<)UJ=k@A|K(U(sEg% z3SyaJOB9`O~rjFHooP3g%>2&8EAG#ZO>Yn|iC+Ty>ngW()M%vR%ulH}06tmg7z3hSm}5nQo5t zaW}`bYTI~7p;@zVw7q8OlQQEdm?%WS1V8fNq9B||B&2|a;6X^y>m6q^nRGQHJu3=~ zkS~|Z#r(X)&J{>dX8hBCdpzUb>8i0knDN{vKAcJy%U&oi*Zf>`0JWXLoY|5rpz2nB;g zF(-hn93(oFMI{OwD^W6t)VRt_5H^ynS31lcVB8^+NCVt+Ousd4XvYpD)?XTHZnXOS zUJx4G!w_%?&+lPuaBmodj%4=qBqcDK4mPHWBxWlr#(mjD!~aeQ_tuz4eR_RSxNQ~T zwrZ&?-0TA6!o?xKc8zAsyoeKoX@x@60YI!@=doT976UL_0mBD|;}LSwV-iBnW2OMB z+9jMaPV$6&9CDW9rXdFne~Q)X9$56bz)y`7y&p}!=(={BOjGNQR{QWf^hP9G*Y^e> zqXEv)8e)!aK;VHmfBb771P|TdcOPtr#_{M!^IxaJyq!t5Zj8rI%-R0LpkCETQKDt( zVK3DMee21IfM{a7-Rt~9wMnRbYD&y-@-&d~fcDr!{~q}T1JCIW03?vAVtiw3IYeOs#R?INYuEOD`T33 z)Pj51PxCGBu0?KDie}gBa=%J-(s-46%MYAyc^jXAtP&p!M1X+ zo1*aI^v0LrIrDa$f28DQFjx(9pCj;~#(+q;r_~!{IA!Z+ljBnp(-X%FO@*p|eT??B zA^&=Eeq3)jwpwqPc%lM9`=WZCO>@hPz?CSbQ0B^X4#G`_F0QOl#e9KbipzyOlP~8f zs>~L7p5^kTV!pIoC}wj@1-evxqf{!b6wA3{ZlzSryJE`irI#@Gz;R`qeJ?5` ze38O=)1cE6D=rhd6j*T?tT^Z_Er^UFQw-TVkpx-Hmj#;USIUX8YD8plHolY85KD)c z(?1H#>5EzONORGO!wT>Qz=el_T^J!#Gn+jh3k#ED)$x4VA5SR#cEFndwd5m{AWF3? z8}!^(gEEHGa7zgH(OkGQOE=4O=|*9tkS?y|v)PgWdM?QnO)+dfmwxkR?yX`bmtM&g zi#Jya#bWO5GAQ(NK3!yKnr0Q6UBQ%ng;s#=$vN-{Y^B1Iy>i?VlPG}`1eT8Jw{AbI zd#1if%<@IzjXGXydG6f~=%uM!_G-gzcYUzuZmZ+LwfA-NwxI|5OMcX?)@l>zuZberC@48yogT3vGT5~=F zOs!ay1zrg95T#&tK_x6pApW=_1-)6p8^mn`)fP(}Q%rQ*fdu6d81X{vHIJ9uFO5A1 zt#k)NfQ)$H?iircX@;1S?qS$G=*LEr&vDwJOw|GZ>*G4$iEeKJ*1wim$zV7*WWH{Y zRvzHxPp_p?fBNdUnqfqaOH-(KYFCk`h>BEC3 z2M13ctUX$Pw6(u?@aWO~Cr=)J{LzDlkDh$&KiS#dUjK9lN5yUJ>|Xc`B+qNM8jX(O z)Eg~RtyzXYGC`wP-A>Cy4L6Ni=-R=LYX-#Qo#DM{&?6`Qa|s3ifByyqW7iV9^zhuP z8a%C|jY)Cf#0_-f3w!UQG=EY^XP!S>Df9dam&-`v8_Q*xz6p-Ga;uX05|vwdmsNz< zmzL60I`_`IZ@p1^_Z{4bEvD(BtWa;}6=p?wTYl&DWzhff8?W>EcguoUOq?Vd!~7F; z;@Zm?fNPr9?YQvhzE@lI8+W`W?yBpz>Z=_V z^_q@%+pSeq*Ylh2RhstQX1h~QtU{g2czt6+(#ILE|8`t|I@{!7HFO=17C|?sl+{ zL{XZnbXPP@`voM9_TU}?ck(cr;=ZS#UtFz2naXE>GwiO3qul|tKS?h4Za5mASgmPl zpxhS(I_vRE3Rb&dY6>^!WVwplbprZLOkCnIvDil!qKs#aCZH?_sjrGML76-az>Xg% zjK0(!sxb30P8K_P6Qw9 z2C)=frYMDFI63g3G8sTbP7uWm2^V0DhX{)myhUJuD|5h>IhLM6yEcr^R(^T5Z?OU= z$ZH|cLx_djZWPGkW2oZ2RwZ{_KyhhSAlq*B$n~;n3^%y}?xN)h~<< z`6NpEO;GKBlGyx?18WT1sADxwJz!541^eV=Nt6XfW_g+w3wf>p5b-Qs7NwF%k*YYf z>D3DELPUg!T&R+gMVSDv1Y20Qs$@3ct%`ID?SzPx!7^?m%2Vauwl&liovwNwQdc4o z+@9NRv{tRvMn`XTjAq;IG_9uFcHIUR-*}|t&ht)HMTVt2F$pLA1w&f>Ze0ASpcvui z($hh49pL^(;sHG-&eFx^9n-OGQj3E%IXKYoNT{BWEy}Df9r4xbQKnkWkd%l3`agmt z!NZae1RE2)8+S=ZS=y-pd1jXsagW|=-ZF%+Eu3LqlZg6&nONy}WN6oSS zKDuvX8gw5(|7K#=%LckuBO6z?ImSHJ(joOu7D;hmaD4hgA~0{ii)X$ng2W1xKnu}W zsxL*@#|mA?xvzJ_koesz4T;;cva>P#3Lf`*u(P+hv$nIoLk?y#J`cnJ^Jc~NJfin) zw}qAIQ-tjo$AoQy_;&#D?izPt^9DAz zw>up$OSp-_4&TS~uKET#pT7a$S?ZcUXau23l4ED9w$&x5ifsMw=hk#h=; z4#XO|N^pK|DMtglXLx2sP{3hxMDQ%{gmwoMygwRv!(puGptag;wQ#;vbhTkS*joR4aO<Ye6{ioP z&W98o?YeM{CMTQ(d)I-@mgFi;B62>o=oN|dVUzk4N#vyI!^_stb?cr6Jbm7jsx$Lz z?fvcT^#>cD-g~gK|8#F}=ip%f@Zjme!PCRT13U_Or@c;2H6ceA;m^Z^gT4Ky>xU@Y z2YAx*`*4zthc(+9? zc_%dKT`%l6Bg`Eir)~PLCTvU}&27UqfVEks-l%Juu7@?$7gF(~-Z9v8ocJn{ZA#Ln zCsGo`SJg1B66Ma6iD92M<)l_YCMVxW$~fH;mKn)Xk>jMPnPjFjjLx=sxokSb%A zhK1zO`t7N*q4_eMbxSn*#2NVtSG79&9-U{TUYI10^NnUTH!<*c`g|mN(Wp^QUclqBEaQJSOSVy zgY;^Ibbx*|Cl=qV1Q~W5x}wCEhDZuYA3XdZcq>98buvl2EBL#DC3#-xsW{zAq~c1NH*1;91hnZeRF~5J~|Ee^+n+Z z&IWKB`eorR(kl|LaA{Fm;RKczS)N;=DTxAIEecYZS13u4mdjG5D05)&OY9qdtsuh9EDwL`KHdj!@6=4NFN)@vbdl|Sx6x@adzw)-x_RXf-Xtdj% zmfh%*(-j)ncSxocnJu3TFT%N1RlNb3@<>LG} zchzt^Q%d(62^|=J1Sa-737Z%O9vSFpcxG5lt%qGO7Y2dH0~P`X6!}fOP$ApPL25&) zgszrYSNs+g(&a*|Yjh1X6!);n3-WQ9R>IBWzAHY4jkt3aWM|tc{$Stpy&D}v`{m+KgZ{AV6E8UQ`(*2B(2s9LM-qo}!l#Glz#PgP z<({DZp90$dAaN7JfG@)#^Bc69eOb8QEi!zGFO>_0o0&|3Vv5CluEemS%#?}+s>}*y zUZ8P*vPi=ZiWf>OT`W_~N|~ala+zU@xoojirWv-xh#Ui!mtye7JXPj6fu^}4!>!C$ z?w4na(r9;ND`h)oQjp90|~Dx$wM+o0Q#dy9ck@EiBP*VPdr5 z*x&&$L)sZ&b|hBgZ$y>;Q#p{|NGRm|>2Y*)3T`~T-PFi5c8HQvKLdZu?_W=)yip;W z>G2mxe$6f96j&<%Zn~6S&In32Pc3C%e<#OFH1{Q%i~$9AE8t`!kzXmv6_!GOkbaxF z1@Ux|7bT8gqF~`jj3_Qs;@GXObq_xbaM!FC!@a$;x%TN-?mxc&(T6(+AAh>`)sOuz zKiJ+|-|DXK?eA~zZy&7f?CtLF?{0tcc>i#74Uh7^Kiv3icWv*{_U_)+_E*<7Hh1?n zc6UEo+ub~`mecLj8~WXPP1D-G;neRM6W*!?8Ut#h?iK1bJM8i^K)Ny|#uI9C&`BIJr32k%OWxPlw*|aQT z)Tc`be{I*>u}ZyWMPd+HRxO?BI~4w(F5cxa7J`pX_J3SeAerS)@7v2QZNV zqfrdyVLfjYNqONl@?r|_?~U=ElxqJFDEcoYWI=+llA48wY?x*=Y4F18?LC8VSOC!E zV4@W-2tivk8&7}8W7S5Pq(8>jFtZg}fuI_ZA|ECrXt63Oj<+*3V_C14r}Si65y#59 zV4^dWQ%W8bA&vi%DRch4u{odM{cC{tW+L7eiN;i--{w`rEiVSQJO^%uBQHrc>R)ZR z7qsh9KJ=7LpYVLll+J;BV$T1?8>!U){Fh1I8!Ln{BvTz*x2(&;y(E!gY7!-rc`lWx z!wMe6xbK!j#4BRlcZ=c1MQ|qu?LwHzCZ}vjD?BWxX=^?W_se6=)d1LGXE+*QR2GA6 z7=JOwe+t+0e0NxNI0g4_C202_&pm0wup7FLlb$qPk9Mps=(-DqXV^K*lTo4)$8luv zj~GPUk!s69jY+85%0#szOD>L8J9rEd*?9DlLMo4yc-qS_Ay|1CL+}_ji_oqFIU90R z^Whx0C;7Gi72y8)g#4O{J65V$t2?OUwsu*#8Jc;UpU$kwA;AtBIEd+vvS=iIO}3-? zN;SMjGA==8O$jq=0;vZl)}9!OWVm0hvQf9?*4!~}^I@BjxDh!2b5mUHx5iv;5{msi zaPxmF@d#534_eUbm}N6e-MTE^N>xVRi?s#S5Ztkz#-E%H!(=Qdn!GWSTN_U*!fB}U z)!TY$@J2W=3>z-$uP3qq(4i2#|2T$sP#SYp|UfoEjt4e;ECn);nrQ>kxV zOQq%&h^FUmUcOkxsKkk7wv0RvA}?OXNf#8w@GL8Xhv30ch$54vxbd{$weI091YMn@ z;aAy2+1}gQeX{vvZ*PBpZ+~Zde{F4ZeQj-HdvkMtdv|YpcNe}kK3?0}I4{|eKH%_{pA?D)5dn>^*LR3X!A4ry#xCFlL_`*#nhT=fF*Bessq2lW6gKWBLgqiecZ~6ys1tV_aBjEpcQ1XogCAX2a*>yZ0&9dtuNx!fY zKUyjk3b||{SI94=mzGQUTp_!BV+CY6oy%n4&3vhdM*;{OHt*o(H4Z#kSt)@OF&+~+ zN?=6*xJ;3w>d9#kg>t%(%P$p*X>NsLa#LPhADXeY&GFte9l}k=_NM8N!a+miw?#4y z_*6G0f){!NkRF*e;`;-CGJq&3|LhFBL8sMhxxolW)HuU<-?ZWPZ|E(j<<_TLu6~dZ zD*gJ|DCibJ!LToj0*(^r(ALr{M}i_T+?N!?IV59Kt0bD*SPmq3q;T&NZxD(7#r;fq z0Tf0U@q+1hhWZL*vqR0EtoLnf|HTJNT~^AE<{c@ir9Gl2W=CWK06Lj&$?+jZ=` zGV68qvUrm|IurqCLy<6v^vVV`2y)1Z|2&nr|wc=7&H zeubfn9FynEip-HMWtN-rb=uH$QvW{1B zkR-?e^Vu<$cxcd$U`am6?@QT}Nh}Jv>%>&9$>L)7P5IwvcUMk_ed)l0XQ7F%I(w;P3bQ`(;*U zb=GuO@9a)L`1N#kR%TZ3d;Q7p{rP@>A5I@S^~v3Dz1=K4m>w0PcaMzkc=zZlAHmND z@bkfYM}@%$55q5G72f^esPo?&f*S=Et{e}OCohtl^*9(Q;{cr!>zjNx-=w1{( zcwpD-w&Sc>k3`Vf6%-db}DHH&&Ik;_>ln@#Oe3A6xM;e;E$x#BdP5(wQNhrpt6p@8j$Io&ll#s~qjW3TP{Y_ECcN z&t5MSPT=}H+IHKq+jhfidF^H+2pR-;5zu~CTisYaj@DL{lj17iUmO&VHzEa(0Q%y| zv*L-q8|^B}MzQ#8Rn|0p0|3-?%|O@;#0p+?{AXJ991za02WZ(1 z^l<@iQ&hT#K2Eyby z5^hj^9B!xXv|aeJy;jR^2X@o;3GJn2du^ozQf!(v#UP<&FNR?z?+k`F$g@sJ8+6ey zbe&&-JJ?Tn5sWIJtD}}M=mmb44i8cq?fTTOGqh2ayO##-)_6L(eS3F!JB3byQsy{) zH6LYI!`!ok_OEla|2hh{aeM7b;Rh15@f@ys;r3Cu9ozN%cGIyNcHyV477CxiFIp1s zwbj+9iY6V! zD$XC~x+413|F!48mv5|6k>rtd_zlX~YrLU;)WAzTYPco4!N zzJVWM&+frNzt`*ZwM8qV&;Le_KL7im&%Y-}pL=ao>Omc4J!twaN%(TQTv1kwA%JNZ zOoR6C_umA#)j7ET9>D!O3GS>uZ?+v!=&(Fg>t4V#`qI+<*$UWg zrm9JI>m~^|p+#qyPG{O(H@UZFsuG7AZ^Rdo;AW46$({+TW{{yr_|tehQ$Lt#^vma* zFSVxQQ*h(^Jc~dpBi=ueBi_FRcz=Hm-k{L|Xs-z}?gikaGw*y!;f}yv16xdfd4jbu zhqW<>HEJb}HG5@P8z~nJJOUJLvqn!qM!cQYv}w=a_P>qc&Jpdj^=J3+(|B?+on+j# zpGe%bKb^q+a{%}6$`x<70sm~b-Ev!A^P*_WhOU-1QqCHo4Z_aQCdVuXZBYNQWV0o5 z*qLP0Gh%^K*IDRh=+?$!v@e%%Pj;7Cixqk1uH<04>k``kUm9%z-L+Zd{X^iKe|L^^ zj?tLiY5?R;!*~3`9lZIK^LT5Yfj-wkpAXa;=<~JJr_7zw4Asy}l~Q@Dv@J=xC<%%P z?_iZm<*!#trD9nxfwNYDh+2|0T{bj9&?<1Xv{l)xzF8?LqENiPu~FXK+T2`ylN?8elH~_OGR}@9iBtIoQ8@|L(mH z?|$^jC;JaS`S|fecn9n5-Fvt1-@SY1-u~UkkN55$?jL;eXm9_~!QRIo@7;&1cR#%U z!Gmwzzx(*%$9Jahzklz84?p?bOFlYXbOsbG8#+U9BWW?$%Mqf& z!6-K{IHe zo%LOZ@LpE7pRKLPfS@9&8s)P=aBo_a;vxr$a#<<}t42^O=xQ6PW+^&7Ai6AShN9xr zAgQV$X_BQYGTb0527E%*6jOoUs%G+EPGj0kMaRyi!8=Y{C#h-nGf7~xHA`}5MLP-y ziST#glOW&y5cPZyuA+nwGttuDNus4$w)028q57|K9I6K5?bMqPE_sgQHZKbIl^Q@p zsSSh2!6derpr&ZqFj49Sd=^sKO%!Fz5Dir1@L9Z5Hf4NB=&50@H_LbGU@h|DM)7W3 z8n`udb-~r495+enlzxmS^kYgQIvq^Y;DF<6FS(ZWxFU^?6 zNfy)UEKb6Q0ZND5hcVpXY1z?ffb!NDR`O=Vo`ZRG^HL;p+WaRI zZ9WT^{sKt%_vT2q-S&Ng+iTnPz_Fc+;!UDxfI8PpD9EPH5+R9+M)<+?Qc>abga z;?kX@LroS_vxO=HKc|49kn{1rjMjTN8jXCCPm=u&$ipJeJ4etSm%mBwsHC6R>1t13LR;>*M)2P~3 z8twWts9(lH2KUeggP2i}_Qc{9#_yx#fLE^u5QS0P)sOn{9)hrbuht3saJttIoetjG zu@|5{Q|3Pg!u?<8uuedkdu`wL8*Qf*tMUt=tz&6YhXBh|&^ebZCYHoVkO}!(4ap^( zUt-`J6Jv7$pN1N`VsMgMchumh2`s8y!?&s`vaHC9(Y`#YJnBSJ=PD~F#pn@+TeQUzH0ZH3aP3Eo9e80dsH&`e9vJ*yzuOEW03+%}80Mqefxj%A09oh_h$NAK zG2jVaI>G%zN!{ASpTQ;D(P!95TH z{QFXX&+FMmfH&GM1QpGC01>2bH{9T&kn4(CT(3y7u8EQ?AnQp*-_Q`(ww3Z)RWz%h zDyuR`s$j|jfGFtdwkW9RL@K&itmqObWZ5uPO7ya*5xi9moXau>-Rz%ZD0b|TZ4n9+_R-Cf2vMC#bDmJr(EQ{(v5H*zNjK#CvY2eRd`O6dCEz*t{ z(oe_ZGnMSQ7UBIbIo^Mdr*6{BhCc^*|ChO{7VD`Xbl~9lj^`CN;N}pi4a?-=;Z{t> zUokZRs9Hw(GSJc4P|(-ZEzv@XQq8PbOxKbp3iy+EV`&P@`Jk?qS&fc#E!AY|1=J2==sdU&+AcX;^e z;r*kdPmc~iJvulzI=uhsr$-MDo*W(??L9s`z^g}xM@Nqi4-O6wj`sKW;XV8sxN`7h zZ~w{TgZ(Fuo;=xmlqBS}2&1{6xf3#l8aBGAGDf`)UX`n z$MCWuYocBPaTZa5Uel}FZ*IJ~vAVXtRVfzVs0dQIys@$M^>P{1Zdntz%BydD4KBU0 zb*=Qqjcb)+xg=Jv-zaaD;TB=5DoBE`QLJumyuMN{6-#eiFO{~6#WXw7wx@x8Csl$8 zUbcy)oV-DTK27Q=(_q@`wz@mWL~*;_FbGGJ!2tg3?{vG9$z(9;?Q|MDx8S1V-trpn z+(Md5xBJa^I-YC0Zok>{O8WH)>t zbMheRg%RAmf}k&lIumqQIs;wTii)IYs!|4DOEXMG1`!sknqmRuY7HZPSu;0FilG@> zD(QVNqGs7d#v58#6njt>I=+BmP{Hg9s*(}(0|362k@GX_tiAoc{f8js`+IjDfAVV$bhmZCS4j(@P34Zus@A1(SSf7H_??2i*{NV26gS~?fL6AYtAH!|;4)+iD4);Mh zpwxq|*vFTHy}c(-_V+%1@;EK$us~3eOddsE*aNFQXj3L<5MqX>9iXs-u^uq#MxzdQ zc4z(-$@`GF>+B1|r6`vAM9$;n-OuD?=Kl431*D{zidn8oTapAmmRS|6LZw(LS6Ah#Agkr_)>?UMt5hyls%1fvRb4b>Vx(dz zRw~LO)?DDDhAye)Vo9usf+SVIVrb=*m^Y`b6jkl=#7)zFe>6geEgH3VcL%#+x4+Xx zq;`AQKY-%yj0c0!#Pwq8S`Qw^D4I@((=o5jOm_k7Xb1t;Xf&Emqbc0#xxHSm-=>l+ zq5Uj{_PiVY*8%P0{B(s!EQ&gE&VAl9b^*B29j>sd`nvwAq+@zrGrqpIrkMJhiX~fy zx-NmW-ju+jepMAstD;{km35tL__lzFB4o|fOzJODRSW!ZZ~^ora>}*{&fq>*wj%2F zdoEhbFsz5Q0K%B4(;s~E9j7-4Z?y*y-E`_L5F)!3MEzRW0?pfh%iD>VJ@q^A+V4?L zB8=d>*B?gVkf?7Yg)ExMRKtEI$Avx%1+=Yzg6~}z1x!(Z3q=2nnhFsKC4V&TR$FRq zR_-KXlR-Q-@SH>zwemVSCW;K^d>iP9nW%3sj>4BMbO3Xgl7g6^3J1Ng3m4Is8t{mf zdswkP9kREHYE7>sDaB*gFcZ7}V-EMv<}~j8VSxMNT*KOg=(T}SQ>$(_dE?%aSg)?A zvs@IXELj-l@KPt{qO{YwC`#8Fsa%w4#knY!>7u7Vr49jb!P1edH*I-K*wZh3#bD2s zugRIprlL;K#Lq}WC2NAeXyk&@&4B%LIbi=U0QR$7-V%tm1A%edZGor8z+NUUCg4{r zPAH9erO5I&%&eP8&&eKeh{~rMMf1k_xN7hoaY1?hD1?Mh`wg!NAYU!DrgJ|tZzj|DZTfP?Z2x>tk>QU5+7ENk zW)#$V3mj&#nX@jJlMmDKJ)Y!cgL9vx34R*+$_`Q&1bz zRw^ETt|pJpg6c)Ag^928loXa-8R7oXxdJcHzPMr0)5ZIb3shG6V z4P7y7Oe{H*f|e#hRHju78Jk4V)izDVVk%$5;=ZC;2Aq&}9rIechV2KMCIIk?Vj$+G zz7XuoLq{C&bTfg>TMFjLYWxW4OBp5r1D>*BM5tkO0Lqp1@pRO|=b+yYv$^bF$T8fQ z<2uXOXU@dUWnc>chvnH@wIp@!_2=QOxi2q+XQP@9R zP%d`c4w&zt<+WRWi%(Wq)^Hb}t)$>aS6ojA+jKze!&I=X(j&mik`_BKnYGsQ;3g?g z*DmVzf?k3G$GY$`q&tJj7vQ5!dhm>7RQW&5X&t!^s{FsmQROa9%4xa{Dpxf<#x`D7 zlblxQ8Nka7||@&J))Qr zlHth48G- zfc+Qp!FDjeQV-fqzs~d5%gS}}L{=0mI%x^gG&6_1FZ97IjZHC2kVdgqu`4Xy3}l%c z=@@gy3!^841b=z3<2ZN%B9ZzV&dJ;&`FnnvYyMLiftz74^PLu95MbkNy@@msw;nkD zMKO>y11o@(vIa-kWZ?|HW3H1~)kRg-1kKQ}`4Y^kqMIt|TH@_kN(GZ6=r3(l!EsV0 zQwM=lWjNHS6;o6fYS-Y>2;9s!;*QcV`;#1|#Aym+x+Hl?Ip(ikmQ(NBfG3TrNp4B-7la<~!LW1{WGdQKz&PBSxDXZSyqRCVLWC zMvLfSh(C5=YVsV~^(p4s&r$r385k4%iw_3VaCbK#KHV7O5V{abSBv&f`${U-Rb0dIs|)^k##ry9tcsj*S6WClT_vVOQ4>epD} zR0g$`4>iBUH7!G$d|rIXjy1?&?OhtICu0D0n9d{TAba~fHNfYr4A#Gz)1`m~qu-d@ z_3XRtz(=x;@78%>x-8TuGRib6*je${V4;R7#<>SQwxrfVOPVSf;50ddJK8!XFu_iL zTX;_6X=Xgdl-bc44U=HwdZmE6tAIQf`ya$BtyR)T|zLv5;xu1UPF zozhMg5qHW$n$sbgaheH%hAZ$9(<-auRC! zi(g?3uBGr+R;q#|LLjV_D}p4;RaKJYElF0Y)y-`(fAtQ~Ym6J!L8 zf>GecbV=KB?S_MI8Vwxa=Xh`9C3ruK{NduM%xq->qhGsrI_L4r*9(Ow`FW=R$$B`B zs1q2O&24XQJA8bpBfA$<&Z9}0K_Kli9ybIk~W;@ z<|TTZmkTUrsS`cUkLL6^1y`!JIK9=lG*DB5<3+~1b8Fgx{{lh%-=2p0>iM7s(+yDj z&PAbCWo*+1(<&Mzh2{D!tFk4O1dv(mY(mzpjM+;7Qc0keIn?lBSq3{`QgIle78UrF zqAE&aRZ%O7vMCDU@=$XG#zVATHW}Ef$ z>-x27Rfh<=v?{`@p>I^Ts{oxTuL8tUnhITy?8s?E?t<2`uW}&1`Z8n=)Mtpy=Dr(H zQ&>18E!hPf+p6Q7gmxRQh8^1vsJ1=`TT!?(2zQ)bJM7iEVb~12?VI5@c6P#E(D&cj z>B9&5C@f*v8MJS;I#DlUOaGr7V%6UQnfuOMnM2s?F1B#D;%fXdnGSduj-M$qsmBGn z)y&bYR-#+2lx{WWbSq^{SjZ%UonBt*zHG2}zug^&`b6m%y_hd z8T)-1V_H<#KCZae%W9{TvKQgR-=kSk> zv<|oC#oEf(;phrv$j}+ApBw&u$?Vwkt_vo)@L9-U{jYN}ng1HF{$sgX+-JJN6I;LI# z_DrzfgmZsBx5)ve+ruIH^@h_5E(*5E6t82V@3~Cu4tc$_E|u^ zkhQ$czWACfKzo*0(Qo8$58EKBq-(cuG@ zHZ`=&s;C)~u3Kddd&qg`iVlLSV>2qw*-~_{&v3%haGVzHup(+I*mET_+ToHaa!$w_ zMo~EI#X0N2urrJs#N!*7E#67xs%esIy6quJX=9%Qe0A9Su1{zmpGN!YInj1(m(b?T zi5En>Qry-zWuqt;b=9ny`nF=eUeR^AY*f~^SF4(i2o{Z%uL)KW(3W3aL$q~6UNK5l z(|BVO!Y)DCR7G7;URUAX>V_ywIMhq4mSss<=%byP%f!D^*9!+WxK6z`&NdrFemHoi z1QG>fHNM#n+^8Y?Y|SYcfdb#;BUSYHE_U6|E(SPVB6X0?I2$= z4BU3N-N_UVzH&XzCeK9fDuD2ta}+FiOzi-f=&s%Hn3ue)C4Ht)Q;}TKXl!w;TxrI` zDdkEV05bn*#*@S!DtDU9IORdR+aMv!WUkmk?P^~dc{@upX4iAL=R6Lcu!HP|f0>Br z>h;rUYY(9QgSlyI*TemJ>-BcvwAciXWo4~c1gtBdTQ!^+sc|1U1vHsS1L-}ebZhh+ zXhcwFG?1Ff17{41a9=>(lyyVI4NnY3q31^z&4to-d9Y?$F&S-*Dlyw9m{F#zPmqoE zrI8=hGl<_9SV6FaBS0Xa>ch!ik9Ci=!cNALe(n0{==Xny==VWxgSVda2sxcu|wc;t?m|ek>njk9c zpdPB)=2r2>wO6jLlsAza3rK5dp0hljz=`bC<^`8$f`>bF+T9WQB(7U?Z@q0}Ie#+f z;Z8K8F%#ACq@4(9io?MVCxg*&1d-Kv;<^~AzUAV?fDG3o6B4}wXe&ZCuk~*M?LUyu z$HZ>ImWQ*1gMf+l(vpqMVNIk;RJj}(x(_Vle03< zq+<$Pw1t-;+CoZ=-(HZpnmc)Rc59GxE+g8n<~Yee1Lyo4HSW_WCLh%}ILbb6`#!I1 zuf?)W*$!1fS~~0gHCZ^TTeX|9xz^AJMy?jzqp9>{5RjsYn^TY{jh|Q0Iabk?t|^jk zR83LSaknzeggVZ$1oGo5#Hdt!&@^kBV#sKQ= z+q<`^{&*W8MrbgKB0_y?@2N#F(wa=B)Bf(Fl?k=Ko`l-7TqZW5{yoqd$6m%qd*k|P3dyg7Q2(x6tBu7+@bke@YXmOqk6Ko&e^6a{ z(};;8*f> z>{c_^T)*`2%ULLmORm$b&IvA01LQO8`jzXa$54OeTA}cVa?oyKgOuN_V+>|vX5~lV z<{$ssg+j2DFQtppTD2sBZdc*kG*nqIbwdRaEy=j&uV@+N)hd#*k@ttQ^psNBumGk& zS-%9yQZ$^xW2%ZsJi!VM3&40=(^T;JXACj$);>YhQ-1k5CA_!)@RRR&aQBmkj}M-} z_u;{lM;{;VA3QnOe|-1R{^67TherqZ-hJ={uI%04KR7&ma`5=>y@R8VKRJ55cZd_{ zpFDnexIf-MynlFrBdPcHa1Qm}lfB1D3*>C|dtIm1>fsjLZQK}+rVrC#r54eq!zgOr zqP?#Aw4n!bs*x>7IhF{AR`)uC=w>ts;393b6#MoW%r_IvXUhB!fHMD6IZ+svrfj#_ z@@R7p+b#Ta!2479z;fzbDel(fO-X)Dkwi(Pji)xMLbW1GprI9bwJ=wyN8LqHI^>svrv2s-?0ZR^bp~XNBi;i#GUa>gs3D zo~tCP4I<3=xI-w8E7oL4E-~mo<@q>D^umOR0cxBn`BvAZc zK;SJ@48>UAUaM3#HjBmWjndZjt)eOl>UOm#6^p0>QsS)x=8Z+8abGy(y^rr7JU)2u zqwlzX|NRf{-1+GJyB|(Jyz}JY$4@?baOaadcOHEF@Z%hne!(^9fg?{Fi;!;c(4Fo~ zMw1Cn4vAX*C>mv)w(TUbHiP_s2gpCnow?>WDeU%fKyJX>XqHyzN+_x&RaAA=(5l5v zUDh&%~;(sD{pSD%Ei@UxhjEEu4pV) zyNNATG;juYZdX(=;>N-X$>r&O-RU#~ntK>_T|ex&xBMNjoOHb?RfNh$skpT*R)wwdnot&A6~wZzEmW?R z#A>xD6icP`?P9fhqqr@UU%!5%B2+g^<@MrrsRAFUu9mjoj#5#mz}-SoGc{RSPhssj zk(;WJT%JM5p2yoA-6?W?v5M7a?`bCpSl@8jEw?;&FMoX-&%J%iVfQxaVgvV(X|Mw? zj>@8_l`-9=9JC(;+CQ9I=B5$IcD>yU8hpCm(tLE4v~oVA)s2;MxwO4mTwhyXU)x&W-U11I_4T!_H?~$wrPZzVO_0;; zrBWH>wuk~--p0+Ha2$GRtF*nfQo6?8gL>bnl<-1ny;RyN7mI6K8}M2}KTY09V;w|+ zGeg_GOp;BTz9b^+Iv~ov>vg+3ofe*R+*nNA_%8Ml6k~_I?{>O=hh1~x9}Sm^pu3Z? zo2h-OIjNTG*Yfv(a&feqTgTF;XL|u3l)8jJzA;du)sv^9VJcML6wEk_VwiEh1@kPH zqB0s8-6YA|0?xfB>D4I5LJENC1yTqWZAEOd+M1~=&*^==JYM-A_O3%Nk*8erq<;g} za5NdhjXi#4!6(vd8pXY@v1n_Fi#7xMm%&Bb&u`$sl$wJTP}gbk(#TReT-|`jB2-LJ zznW$08Uq^uljzmpIW!e>ZP?i=z`Js}QWb=%EXq~d9}0K$qtv;9_gFFm+l;}k!V%8% zyHE?qSwOvzl`y?g<6=kc472QjsY$GzyYLp6=29w_o3%1xMi5NWpph`}25fVtyK3-)KBjE&3+4JK!`E39~vh+1ZBsSR$aW154` z`g2&)R5xQQZL)$1Hbuh^4fna$SS9T8XUum$?>Ze{PvY0nE+2A|k!&-E=q&-(k8_PS zcC_2g1~}w?v&n}xE)8}RYHB>Sk+B*Udu0r3q$%>|D>JTx@tPNZI#^x?e?hG#Gly*v z+LuRKb$$H{Cnzg^eVg*~>HiLrDB2`OWfCui8`1uNsC)e;wNrpJFC z&XpGEacrIkupz+X%+sZ@-d&M374s^{B4dKB6UCTGx;d}J4c2pIvWP1d?35B`g3YV| zyNpeF{{blSv)42$+Bb6)d2hjNa|b8L*$t=O1hWk;elzAYEaRVzHjbZany!)-H@R!g z>ztOshzq(`dHe^6Mzs zj!h+QEOjrb$BVn8r?`VUVxpP6eSp9&GME|4Gq45OP(;%#8vq`^#vfpC=m=I*D0cz%v_7XnaD*+-(f7cE?jZ z2_MFjWZKvLX7_8a!{J61tDcSyPa3X7# zCi5P6gjzFetc{u!IcjFrG(@o~BGXVy=xChhLM|HfL@syR-V5ySUWj6^53>lyou5f@JW#~FKr3It~~?0t>x-)GpoY| z=9|I45)*ifdursA36y>_W8chR8`9yI0?&a~U8Kst?d+)wy&g|v>#83 zU#GIB4bo zim|q)z5be^zl!#E?Uk=d;@4i=xK`FyUtN=S)^_=d_Flbr5pdSU%pyfyXZ?p$~BYzMq z^$^5t^Ijt6iV?kW-T*_$I;09SXG*!WUnRT6}*=S5xqa15- z>WpdeSihUZPs#*cCYNzMZ9-H>^D#h)8@^dsLdBSO%nyil zd>;O3rtg+kG}~|}>D5Y_xiUj|d9UrD&&KUy+ifrE(+-_I#|3HaAfdF=Y`6L|e>o13 z+6irJCUyTQK=Fripg@YC)5MM4Y#U1!zi_2c`0lS33QO756ZLqtCQD+KkhrdHl&Tf& zHBkU5QdF~4mBlq}<7=z0Zmw%3ytDv2WPhhK=tslBH=`hE z?}Tsd>+&c^m;W0)m!r$EGtc$|0NaD8xKa2naGHOyfDHd-&|XuN z;#1j>RaKQ_oe@HC8#n}2k42URNGut`e$eR}WkwkOl+s)gs$f*NMT~|uW3ev3510oj zt9p4dobeR>c=8aN132rv&Rx{95%tKBvo7oKot-AWcChg}oOhiwT^)n#Y>+}a!}+^G zmmlUIb#1ly0%+goSSif0 z@4DgT)S_Q{bu!o9zUY;)-vgf2PSd_s(COckuhSb~x*Z!gP;mJ;!R3ti+Um(uL6$0- zu~8N^SzD=SYIS{6*F;rZRn#IVbqTjcm$3_r($+e;$6%oK8hP5-zKdD_-lCivEKvrE zK*kR!nj#C>uXm+8_g1^xZ#!NXwFezL#7S1zm4!1w+3=g#Lyw*# z_q2w?K5C2zr-Y)G$k^}E^>|RqOul~t4K&t$$G6`9+_ zNz<~iurcqX!F#qfb5d=ld-a%?uq}w2RoY%flq+I!p|f^*1UuL0o`YI2&fY~qaboSA zC~~@Z&!pdPcYW5+@mUINk(Bvtv&cn)-2`XNu>tU=Z8r)!+*!YvXoH^Bq{`--n;S)` zTop^3>sxYhqawZ{Zm+#kg?LxRL2`<2ftj~dOHl>v$r6wTs^g9g8t$cT7&Uw#*qCT4 zVhA`YRMuq~ISuKa%p;76Tj$}K%oJ=NRM@S$6SP~cuHS5Uk?(dowT|alJ51gNA@)Ir z%#03kVo5k}a)+r`6uK}O5;z>pP3N5?PV-FY-Up%k&V@B%&-L)N5%96wOM>v3iW>;& zT)cAQqIr=sS^9}Qt6Cfv8BFXj9Syj^pI#AVP_H`bW*MUE|3GTQ)G^17ATSz^C;eeG z8BWHdXgq;`*`3Cwvs_dH_U~LUWX_?@6C5P-`F`DTlU&piF0%qI^T|NPse&5zQRxIW zV5-+>fE>dQN01q&rkFLFrETI~Q<|kBw^PIMotg=7V!;SED^nG-BCLr*1rAl&RK$(V zl0NT2&F7*nPcdmQH>q>T4i*_sK6{%TM%>EwY1kZyec0|eot;h?_WJ#3u+wn-RMIOJ z@ZB5|$ZrSCe@|}y5oOyCZ0e|TIpb$p`L3g8buTDMLfe zDw)X6(-bhLxDAnxJ1kowJ_YbQl+ul@_0{s4R9xFqOY&yv&5~SvP0&?okqdQs_+0=s z4hUq351$hjC3(*HfNd}q;zl|lb;WmrZ#EpK>Bib}O83CI0d(Hd+T4>eJjV zXcTM@gJRFKeP+6s)#Jx&Pq(Y0Ea|1<_WEXZV?(Hd@)q=&Xq#4?vg8>qbC%&U4Mxk= z=`moNsuS`$5r|ASYh6_r?+1H%f+G)ouaWEe4Yyn0>9)bk?S7v8R_;0t_FVt-E`a?< za+_-$-0HcGd#gL1<6ji)lB#ZR8FFd+O+{X>C>yFGY$}RoN*KY)G6q75Vxjdm*v2@f zs+u$pl!yZ|SZx`5vCyuouT}AB&`ef|GBm6ssmkIlGcJ$K_By?W9d>>HR&VE4r_%`g zokkS0`6OY;=fN_?J=;1X^#j+C9Y_6d_-F^BVVG`%9`HywPHz6jgv&Pr8#lRnl;24O zVApMkj+%D8Nl7kDcJYBVtE>|m)VQ*GoRENyPu5V=E6-H*nZCv~{nG`hsn5UE&CMIz z3MdXu5qW~kzvD(;RKH{UIgo!bjDQ7`1fP8H!R|+oAAPdF_xRx#p6Ci)_nU2s$GT*> z-;L$_-JIe@>016i?g6^yj*VNT)NQ}j;F+!^#rx!GRgffznu?lUF;xL%G|}EXk6lYt zxtS?eWK}uc!~!~&MzL7&f|#KThs`D94N5OtzS~{e{?(sM8oT3uJXH|a?bP@A*20`g zMw!2t~mVgma(1vc-wNHSf24Z!}v+yD&YZP!K9?f6ZeVz~g? zIAVkfh&<$GXj7uo;+Yldzo5*D%(^M&GAjo6zL7#A>1-NT=+gu7_6($R2~KwPgb8+B~cJ7K_{MahLDan#lvW%*rX;QT+ z%jHUWOAxC0l*uzzD$V5p=3aefQZJ8|#oZ73y6dp49un^>|Y64DufW$bT$Hz+Hg6(Q+GY-?tgfXKC3kuE;v^ z>yQI!Q1OSUO@$I&HDugbnt}UZn7HXDIY-C=z&SvQs=&vL8rWkM2aZ%^qb!S+%7&yP zO~mMQpq}r7=U6-^6DHeA=MX4|n?g=8n@1 z2mRil*X(s#?bc4z>JZZ3$C3UJh1Ikn z=lFc-Z22s?ymCbTYDy4*bgh5hjKva_aIK`1acgzyCX*AOfUbr{ratLb@d$M?O2w|?f| zeO~TB@MKkiZ<>Tk0SDC#Sn0?}*JyctA-1TR85fvj@M5f@sYz&27x30+Kp=lTxSajN z=cv%;ExsEOI!c&ebkU`SHOPHv2=RZJLR=-pXHn|kc)d{g4Y{-LDccxe8&ad;rc)DV zjmqAge=5RUkS?~W#f@rtb4#dHw%*(>3xZTtHbuF-zAmRg#}q|9YPbuSl`npbQ<9E& zaOeF8_dfXW&lP&xPq?EA)~PPj&p!35UUO7&e;` z!Tzfp_Io)sq#s;BoF*2I9r);4!DGPV)w4XQSwZ2rcCvbWd@SRJeVT&&8Z@w&nJ^R( z>#tMLYH4iCDm1RTp;(x6L^6(qM0X87qO5LI)?O>Cn!3HQS*l9KN~!#YrVCfg+e*bK z3W}nF8BRA62YzI`3%Hdp9`19jIiekvk@#Uh8isZ_OCH9?HcH;3{YB#%^=t?wYwdm* zdCf*=2Q8{w4+nwK_I85E4|@G>r`Ky|^!oiAy?(N|Ox$*wjtky1CMSLYJm>EQ5nn>9 zLW&$JgEgBlh$&xO!=Oh~jS9I}iYDPhQI!or72gy^T|0+$W-BG)Jv6Y+ zWlT7yAWa;o0qs&lDb$D6Epaqa09T@p)TMW6zZ1X62Mb-fCJ%l@=ZcvMV#{UdpOt+5Ns#gk@U04r0n@tVjQ^v^!S&{$O{G~PV{eSW-lZW;YtYtDvyk=+`$a@vX! z_bY&V32?u{y=dpf8rQ`uBYO{WjO$OG0Y}pXID!_d?494Xt}@$N!`*~b8H}UBylAkQ z*ac{D5~On4eKu!VL7bUpY9iQLa-UIraX%u~0&H&J{mA<;a)Z}z%hvAvgp!fCL|A<&J?&s7zAy$X?e44+n&v9Jr}?L)QWlzfin|<-w#RsG6H3HfU#I6^95q`;cUA<77?{mG- zD_1lXH*3VIfP$2Dtff=ifPvg79ga7U?~Kof1z}B1HT9}UGb&Ak_Fz?Ur7h|dKySX7 zl6r6N$^Iw%dtg2fAAgFb7VPS$WHa}`teI-=!=$7> zOGADOF#o-ay29*M6CLMz(73os-B6gtBwvVJC*JOB800>I%u3`-G55((k;`fcbd#m2 z6WP}yYcQ!)lb<7ol*SG z8-ghar~U-h_%s?0Q-icH4e7XV$7O~Sj`k8$5R=Yd9 z``+8DyQhyj^7`jlv#WhuRhgApSw~(~T~+<`c4uej>gl_Co;a+K3plm!Ve#7AAR_^a2K98;d%4+<3e<2EBGu{;ln$RySMLbMWc~F9*^K>INsP8 zkH_#k9A9>ATyz0jh0S~6{a<;hK-bP2g@V-GSbDkexbPk9P~np|3xzE>Pyae$xVc%c z)f%-%+k}7hR-@szn=RX^6%Jl46ut@9ljEXXEFKq+PvpgC$H&VN2^UYEh2!FQP(3~- z^6}CVyqBws)sw}iPoGvvxB#H2I)aq^XaqmMh3NnNwO?!0M&W}P z@_NH=+ilBk*PVvrv|F{7(<*%YTA@%R$K>e=IjJ6#;v{)=I9WO5~l#8-ZQiP%+a~c7tgf)ezGSOsNlx28SRYM0n6;6luvLpe} ziY}{~s%XlZ!s`lA2)iLP10PhiTGdotMfsLwnTRsTc%FR2|D7+}*9L66;k7NlWBI=8 z2i>mUH9bG_gD?t$5FR7^4MRU{!>b^Ib5S1-MED!FLwFs)Keqb)e$)>_eC*Q$J-7xg z=|>doUyQ-tL;2>jVBZ9=?@+L_^4)4%R=aLlPP^gSb%yoaqP_U!Wl`01P`V)DAhnvx zOFAw@SVw_Y3=nDpZvjs=5p08Aqp=74hbY5a)C!;}RBfWm0qfbiyp>?h$5`_(0&5WqN9iK4~`EmuibN3Mwcg8zdVKYTY&XWj$k+2RvqBB+_u>; z?Pg&az9RsAenN^xIjYK+m+>d3)$YqLE6cCn6qNGP8)Zo)%axmJuL78>((B^uvMO?~ z$yEt-_RXTe7Zt)SR!Zucq`b|QI9<3=Rx6b+NlHm3WknNL$~Os-6(WdJy8IsMa%&>q z&AA}=_CN05-r9fg(dfa>!S2ToZ{H0cA02+O)8Fpz9c*t#dtckx-`l%=Z|l*${oTzE zAKpK>yR-k5&HI~2Tfu{ahYyE4`}g;DAM77)-+6F*cXw}pcWXO^{LJ!ehSTvowf9<{ z*Rh<2bt?+}M$d4KUa!-BH;B4kFKF5w*A0Shz1<0#f&YHXZbu!h6Si8ndY(P+DUSA*g#-Q@U zu4P)*2mNRmMPRdset)qA;Sh>mTgjCkZl1d~}dXv{B!BF`M z5tJoyP33R!${LYGeF>lkqp%w*YguHi5ixNyxo} zo&Ar4JHg$Do0|{s+`fOf{qXjqqut$ycXvNJ*xlK^KYnzyv-SAk(Zk)X{g1Zq9y|&j z?cd$q**^N{U~Bv6;NYY2;nC*9!|j8^-Ti~%=JwIv{{Ef){hgfz@@dHqJ-@CswPvT= zaeyDef8OeV4EI{S8klsi{a!l^d@$@iFzrov>{WYF7hXs8(CdPEf4>n%ZY%Uaro-Uf zP6ti67e#*7d?%2^bE7#_(E0Ap=#HOmv2l<@pE6Ja5Ng7)#azC zS{tzb_j1j5vt2jq4mf0{>9(#6Hj&91&?=3)sVKTC-&_RhX2>$HphE`WR*|O32w|0y zF`WSGHn`=YmV&Jr#L#3_f~ysP9X?|iU<%N~Qwc8uKc5Bme1`j5u@b}Wb=o$FVbH93 zUgY;asD(hk{h&=rc?6t0gw+ih^M+F)YUrqI!v#^gdSKc^A4s|1r}VtvN5&4EoUSO! zfPI)_xc@E~?mwC1cY{;|EjMfRcHK6?XfR?vFV;^bki0|;C;m1CT2K9Lwp8|{89KWS zQ1-#!HW+h9cDsd^J#K}cU9N)q1G)uA@*G96} z^8uW-vdaFlAEsdcs~GGf09&Rk?a!oO{|=Ds-05HVF9d z$q{hi_#sGH$C|@Tmpwa52QSX`rI|cY)|X&!lJdG;EBp)ilHYU#gK8m-tI3y+tK|gMGK*W~#Bxa{L_y?bPE{p|pke1Rl0wl2 z!IyOo{A;mzv%I<_@Zgj)15U#=B3O$lWj;b>K1J730WZysxwrYr=Jtd8d%L^)N3ixb z4-R&Ab`KAajvnpp9UVP>d~|sD_~2;oXcPSF?R&Q$eDcxOBXG(O*~0VN2L}gvPIt}< zJ3YS@tq)lwb$xv_>JKQo(P)aH4S|`BM(Y-?b)Uv!GN@++{MY5swI*QxojI7>=ybQt zR-;|7wZM?G1j(Gb{8{vbQfZLI>a@Gfq*{-OIn(62HWe&KU9Yn9EOf3VFEtiD*R_P| ztDr(=adZ1RY4ds4M&{`pK zi7pmb-Vp>tLU7cHJkz9?0{9v-1zH3!MV1MpSL6#d6M81pI*qSLK=)_n!d^5X4<~lJ z@7lF)1ZH-8ZN-Jw=OOa4bCbc z$TxXG1`{rnMOlUKp0e39gJ~}a>?!XniX&cqn!aYv7<&=>WkKqRwR=J62T?B!qM&Do znE7DYaCq+3Jo>#}r&DXSyPnr`txm`9bzI*ME(H6J0@!~r7iV;{a*a{l-Dg1kLq^cniBQ2pk)y- ze-$ua1k7KZV1970f4I9d`snbZuY9=w(ca;Q5B3i}y5nzeKRg_Pqjmf4-uC9f-Gi?_ zINUvYbZ2w@-uBM+{`S#VqDRr4&Ao>oh6jfqKR9^&9fyYxH+Ocnwm#Y3-@CsJUwX8^ z_wl`l4|ZvF!y+b0LKn1j5OhCiM(@A>epru!nj40;d&~FT;M+ij+h!PaL6X1Rin>u? zdGDH_twH{4xBOczFY;UOg^^{4s4m=&=l6Pz9)2LeBO#y#t{h_iVM5uyGo@^kNXt)x zs6Wj~l9+7^eXwTBp^tSIYMEQFA1|ni!crN`bz%fepX5fgn1z9I2a_!eNX%8tieYX{ z#r%lIYj8rLE*Oz@Fx`T#gCEW-DhH<&SuM|^Z06P7?&q{Fn>2K2;;M$zD1f7Hig0RAxY)(4~YQHx6W_fMhCLM?yh#>Hsc;EvT?({0&?s5 zq9)3=*|aPmKHWI3xxU^T4*Ss{@`tc4(xR7K8DHtUQo)=pelr>(el-J5{u2#y$N^zyK zx*`%$l6XOqIFQwqm5Kyx8SV+1#ZZ-1L8u7Ji_1lRWqDcRL{;DwL69dkgEPZjFC6P; zFbKne2P>oji$2J6e*_Ps2wwET7j4#?R$vzkRkC}4_3z6~McY85>a^~_HqH7~!B(`_6?IXqh(zXq_DBkN zv%;z>EE14oUcv$rnHYjfu)G8$ovbJVNIEZy3Wm2Uz_1lbrQVeW-=k9p%g_*h41&m@ z31%0W%&s1bF$lbB6n+rck>}fC0Al2JBhLw46WkGWL?a)C4V;q*i$XvEEvyH@S}rP# z^lVZE;x&VQ>W6b6M$V$fh1G_I^%fp{udPA?J#i|Qp%hu9QTU~uVK5K#VS{-;7dg!Jg z*oZF{<$>c8AgW*HAKx*n+rqJ% zKx7@$iEBTu0JbW{MVmpM(9cv4v{Du${94+VKsWSLXIeI;617fKvl zp)G;0bCZUrMO_6oDyqO?;XVKjT^5nNVqglKmW#VBUU)JgY`xbGdS0&sa_oCSx8=K` zZ-UF=gDc~75jPhDO(@SD3U=s5zSl!o+aTF>iY=T3<^`TE1~L3j5V=8eKlGelwp{bO zQ%gE=SpLoo-qgg*QN7W$Fn<(hhUW|~pO9(*8dYV!3aCRK!;VB@83GVxk|u-+Uy;fs z02+hMvY|^f^eBrMk~DOK6X4e1i$oA~R0CMTt(Q5ONOGAga~D_ey>P+KM2cztFUje{ z^Ca6l0@E1v;tSw$Kwo0rHT{AB3~b-;f{~@i=;}r5gY^L@h-g5u{`o1ag;QF52!j1v zb68WI@}|1kcIu9GRip{kwi;`kq3It@q2WedV|_F%`WP2+C9dksEP|0Zxw=Z-9xw+= zz6!ryJ(8m#LX{Gqq#-VE&5;v4wKCq&cjsh|z5+;pPj2W5rP?+zM`$%ot9E6yRibJ{ z6~MH@D@h>==w1FD4!j?*-I&Q2WL+WX1Yx?6Xex*Ifh_AXUc$?SC4kvo!Z?!#wqB)9 zE?gnaKsyt3df{k$GzZ{=b?$+!?)gZ`eDD1ZW(h*7hS6?gKLx9vr%&J%QTHf{%{N<= zsUbNFk&#WW4BEdb2W>yi{%6syw;LebE!%RyV%s&R$%?t=1$+Di^FP=cry@JU3O=Lm@+X0*r%Rzl8xxIEBdFn_;1ch9ihekvpGR0 z_y;uHv0STdH=Gs|?Rm-BGnP7%K%P}Bl4m9K3Pp-0fCg~_qd%J-Pl|i+*bp0^ zZ3hdS+p1uGMmYrh#}f!Jb>LPXK0F(zPUK zPlpmvAqI9G2e~NPGG-7}Y-u;Bv}>$%&j8n$S?r`%Pp8yO*JMmH8j_+3`s~6z>)K!q z0C~BU8(GuMco}_>Ju$25gOM~~a^d(?0l>eLQ>BT5{l{_y+oo#VvRxOfw;8ui%`4W& zPoF3X^^vtCVL+cjNvDL%ph7L6vR7Fp2-W0fzZL~w*79ra zuUAwnAF4BC>{Jcr;PiR;$cQ121tFB@4aXqe8$juY>lv{B-JFQZM>DxN*7nrRn%lN4 zCobZeSFWqY08{y_YD=dCi`MhdVjewinPD0`k>5^|%j5jTt&kCTa5LhmKE4 z>WEfqZH%U%rWj8lKUYD|WKpnxAqM+*0NCOw$^M@}vVT{OWLs#o?WWakwXBBA*x6h% z{j7K_FDP`DLE>a9acyNP&qh|BSxFx&2GOT9H+>0T0|s8eD@c3?sIn>oZJ!18^-`slGV`4l-D_;H1wBkpFn&f|Yt@MB?p5|o%RaFIc4itX1^a~u&&~($Ijs!X-!B&sP1FQiUtd9r7vtXa{yReDo z>p5UIX%$z)Y}ML!-K{sT3bv%sL15@~#c<08b+sAT8m+LNfGxxdyu#w4RjR-xoicI} z+;Y_4k}4317s|zzl_iX$DY;zyIuzrcN6(UgTJ}i15?pEVL2}hPI~)ds0gksB415fv zW1)hH0oMVO>|c$+ewJ2tOeFhP0qj4N1Gdw)Ffv|iV*Ryk+LOwTRFBJ4j~CC^<4>Q3 z3vUr#y1|JRUgSCM^-_iBh0-}S8dqDVDmxnWuwJ{@n9)h|G5}u!;9q_bz<3LWia8X&v+$&SGHvxXa%Q>*$|8{Q8Jy>qL4qUD7T6NPY{OBu%!VdxXWbqiA zrK6RIuN-SsB86jSuh${(exO^jRdD{p+b_pK!Ks2h0z1Cyucc z7+_v+)|)J%HXmPm_UvhK0dv*_SUOP&F{)y0rVUkL&q)v^&XF)1uIm%7miS(}L_Ia; zuT$fXWI$FVRa9n9Hn={)a?rfs8V?5WvmPsKs?683n*3rnEVn%}9$A)&s3F*=`<{`Y ze|vr@L!%AMyxDeb*REd~>vIOrWO#WTG1v8JUQTnRcop-~S&s(e<%qR{1VH&b3^vF^ z=z_J8`|`Y%DZKxB%+IHlc^$Cc$gO0+3T&$mf^9b%O|ziF#We(c8u92dh@&#cUJG83 zC4E6v$~T0?itq-nip#Hxf!SygYA#Q?XCUYy*q=w{d87M3OJwEeyx`B>M02U#&;*!MaQH1ldLPqFPC@$_?*C% z0A#m*&kSq#tem=Q6zW%Aq%Lo=#APpjT*oK&Kp)`=UaZF}ntGjid3w>ZaRnX~#fnMV zhsh|xAHXCxpKYT0r!!_)I9SrqsMpzWShHpp4uF||6PP(lHi)l$k}KJRiDaLUXD4B? zxJVWRxgrWTC5?MceEs#Mg`4Fi>Fs4f(q5PK(oLBxE|p6{xqOqayu*=FaaCQFNm0Cr zvY%d{#BGGUU+*{jracjEClB&hUIgU5(2s&<5cyT_R@F33r|#EHuNU0%BHQ%*TGR8r zUa#YK8||PC+S&5KTf2y`pI(_}n%|exOoM%M|7i~7YzS4OR!1+~Y&BUhbq?7ckKslj z#Rd2uq0(TvPNQmy45k)H7lyzw&>|Qd4YWvQu3V|`SknudTbjyof;T3}8sC`4s6hz0{1Hy@#9 z7~ttax7P`JjI90MQ)s_jc%0UG|Fa9wHXVw#*-T?D3F2}V@hC;Scye+wh4^CeQ@P%k zUZ-L%btkMlGjPweNj?Xs_|l8Qkqia<{V|D}*0-3m`hlDX7)?+%n~i$A)o7W80!ZFH zxct&AN~SDoY4Iryc*5`7invl-U3sVY=1Os;SbU>cs;sQ8zFjOWSITll;>to91W6L4 z3NZ{&&nxBSwVP;K7vET0Tv=7s6|Eu_ivqW}wt8cgTj5qpV2w-s87vL}i)Xo$P7=VY zKz(HJl`1kOEYoRF1}y;;($upy75(#^e>l;@|c2v<$I^I|2R1{ zg+S5CT^dgRx$$nlm@Rd1;vvna6H+(T%yLGd7c*N8(1e zpr4F=!MVrq98St_edgds-Q>x~j=IRlLFhf-4Wrfv!Mj1D=ipf7D9m!GZ=Aw>TE71t zVE$WkF}Le&*Jg87EhiT6`SHfyHd^q*ssX6woT>((g`L?7AFAz{(oT!E4W<*=)du^B z!9It{=j-2HbAb#$pGCnwiNXFOssAz=2Jp&Tg~ET3tITb{x#rL!&Ss+l>~{Oi#W_DuHjQ9nM$~h#Q{25#*y}7Wlg5m zTH%#kpJjSYN%%UTZo8M!>IoLZemny+0$*?l{IyPVacFQ;b32(%9fE%C(5#RbuoBBb zbn(o9Oh-h5#Y?B0mvgajDHic~+xc*)QXJrag;+uyki2Rt9m;`$VS`!Q)U4{U;0dha zPNP$AnT^F_4|z=#B}w2kSq0@=!9AsP(Ri!4bhA{MSzdX~OzRoSbWWr*7vSxnfY+v2)ytC7^)T>)%a z5jCo1l~~Jav$d?2YFQ)JvN|1wdrHe9lO_hyWf@quDsb}hg$=9M1MKI$MwC(yH)yme z8n!ZE|M8r{X-w{Xm_O>&Y`5x89S7`~E=$(W3w2anz>WotIiXcHn;h|Csd`QYUp zQ+yjF)CmuhE3_wnD+MmQhS|EuKarzd|NaFSv`oaHQM<+%(@)UQmcpk zD5H%H#+EjwNYhk@%G@ zuCeWVaG12lT+mZh5yqq{LT9p0!bus=*jGMtM3F$gv$vwg1? z}87g%EJq5MsCu;0$gB3;16F>lA{?-@Kpx zP94DIvA;a6j-*p1A}hm z)Qnbh6Wd!_78YDMnCYb$%niZf6L5Et(6fa^+|aay?q$MaDGOAYZZzV9X>Q73C)sgB zO)t``GkMLd_K}S5rf}H#CVw7g&7XtTrPr*DCDtPrDH~-X-#?sF zGdHW%oOLNStyUeU3pgz&9uG4Yl_5nC?YKRWft@GU+9|LTF;2nObS;tVYAn}U>=Gi~ zQLDLVe=9qeF29}++Gl*eI~Yb0b(s4hh^fWO|*D|c5*w=BeNCxHFDTLgT?0eB_+^*_KjpW^+|9Br@Gyl zwJEFPm?+wfdZX}7xc1=HD`k{VrvfEK6a;~AGO!^*lFF4bS1NJ5C}0#@lSqYd5^y59 zOp5SdQWOP_5Q(qw?{K^TEK8OIsZ7cQXtPk11d&^j#dPnd&Hm#DSd%wZ0zFSrthckj zy|uNqyScZ$v%j^o^Wfow&HMK^A8l{#?eFg#?(9C?-Q3yP+uM7vdvAAlcYl8$j_mF{ zdidzuHy>>sZ5{3HZ0$VQeXzU#@L=oS&erzkoxSZlv#Kx$biVP>gXbaq(!N7G3I-8K zYJW5wL<3eo77aVY0UR3+yTifI4?G8(kcV)R$p(WnQO%5tW#T{du$B}ABCzzCeooTkC|*qa;-$_g{X0`>`(nW!wdok30S)(2 zTRTDZk^xtC{rWhEs56HM>@Cx9ah31#`>w(*@k@L&Tk%lw=AN zPGmIn>aCQC(GknUz)NH%&jbgSgFDPA9K9`YvU2fFaD6Lkxu_~-&=nO!;SI6K;r^T# z&scupSjT&P<*msU2H9zw>HDzndCBDTs9OWi+Y22h@ZB)*+fmqwx=z~#XSt>AJFr{cGL@_(8CJ5Hij~=*e>j}oiJ$hk~x|E2{2*M!;Aa0TeOF>Q4zXcI02F# z$tc$;3#kWb=;yL z3~0p43s@*Cgc~3mW1L7~iaI=biJT@I_N5`q4A_4m$CCc%U`c-@cMIeOJhÏjl2 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≫zDrQRS_uC8gd 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

Fy( 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%0DBKK>FiZ}p$~_niPXyXo3ABvubgos< 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

)x`SL? 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 z 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 &RA4?|-p&oa7h_)qc_6k{q@NPNW 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�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

~ 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
G)jMvz-D3P~X{0xBgn}aB4%4K}O{(Bi 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∋S-fY~4V<;381d$+)pgQ0rm1y2%9KP2R90pRA}ynSm%$-)yx^i=tjohd(baRY~nG83mt8V8##NO%P!c zhSrl;^Rv}>h9!H9MTss zu20{vEekj7R=e=K3v{-|GHlw*3jt4htht+9rB*HS<;v1}4U97!oCm1Hk|0Rxs#@i- z8($V+&z5;6sa2F>nJZ#U~ zx%0AVao!{NQnw!jE`DtU1G>A8U_(+++LG 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 N0{{R300000001_GD6;?n literal 0 HcmV?d00001 diff --git a/src/test/resources/htsjdk/samtools/cram/fieldarith.sam b/src/test/resources/htsjdk/samtools/cram/fieldarith.sam deleted file mode 100644 index 180d1e8ff..000000000 --- a/src/test/resources/htsjdk/samtools/cram/fieldarith.sam +++ /dev/null @@ -1,15 +0,0 @@ -@SQ SN:one LN:1000 -@SQ SN:two LN:500 -@CO For each SAM record that has each listed aux field, performs these tests: -@CO XQ is the expected result for bam_cigar2qlen() -@CO XR is the expected result for bam_cigar2rlen() -@CO XE is the expected result for bam_endpos() -@CO (Note that these are all zero-based, while POS is one-based in SAM) -r1 0 one 50 20 8M * 0 0 ATGCATGC qqqqqqqq XQ:i:8 XR:i:8 XE:i:57 -r2 0 one 100 20 50M * 0 0 ATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCAT qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq XQ:i:50 XR:i:50 XE:i:149 -unmapped 5 two 200 0 * two 200 0 ATGCATGC qqqqqqqq XQ:i:0 XR:i:0 XE:i:200 -hascigar 5 two 200 0 6M2S two 200 0 ATGCATGC qqqqqqqq XQ:i:8 XR:i:6 XE:i:200 -s1 0 one 300 20 2M * 0 0 AT qq XQ:i:2 XR:i:2 XE:i:301 -su1 4 * 0 0 * * 0 0 AT qq XQ:i:0 XR:i:0 XE:i:0 -su2 5 two 400 0 * two 400 0 AT qq XQ:i:0 XR:i:0 XE:i:400 -su3 4 one 500 0 2M * 0 0 AT qq XQ:i:2 XR:i:2 XE:i:500 diff --git a/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.dict b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.dict new file mode 100644 index 000000000..cfab32082 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.dict @@ -0,0 +1,2 @@ +@HD VN:1.0 SO:unsorted +@SQ SN:20 LN:9000 M5:46ad963a88a95089707c4639ad196126 UR:file:///Users/cmn/projects/cram/final/hum.20.fasta diff --git a/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta new file mode 100644 index 000000000..27ed99408 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta @@ -0,0 +1,151 @@ +>20 +TTGTCCCTGTATCATTTACTGAAAAGACTCTTCTTTTCCCCATTAGATGGTCTTGAAACG +TTGTTAAAATTCAACTGACCATAGGTGTATTGGTTTATTTCTGTACTCTTAGTAGATTCC +ATTGACCTATATCTCTATCCTTATGCCAGTACCACAGTTTTGTTTACTACAGCTTTGTAG +TAAATTTTGAACTCTAAAGTGTTAGTTCTCTAACTTTGTTTGTTTTTCAAGAGTGTTTTG +ACTCTTCTTACTGCATCCCTTGCATTTCCATATGGACTTTATAATCAGCCATGTCAACTT +CTGCAAGAAAGACAGCTAGGATTTTGATAAGGATTGTGTTGAATCTGTAGTCCAATTTTG +GGAATACTACCATGTTAACATCGTCTTCCCATCCATGCACGTGCAATAGCTTACCATTTA +TTTGGTCTTCCTCAATTTCTGTCAACAATGATTTGTAGTTTTCAGTTGCAAGTCTTGCAC +TTCTTTTGTTAAACTTTTTCCAAATATTTATTCTTCTTAATTACATAATTCTCATAATTA +ATAAAATTCTGAAATTTTCTTAATTTCATTTTTGTGGCCATGCACTTTAAAACTTGCCTT +TTAACAAGACTTCCAGATGATTCTTGTGAACATTAAGTTTGTGAAGTGCTTCTCTATTGA +CAAACTGAACTCACGTGACATTCCACACAGCACTGGCAAATTCTGTCCCGTAACTCCGCT +AGCTCTCCAACAAGAAGTGACTTGACGCAGCCCAAGGTTACTACTTAACACAATGAATTA +AATGTTTTTAAATAAGAGGAAGCAATAAGATTCTAAAGGCTTTTCTGTTTTAATTTTCAT +GCAATGGAAAACTGGTATTAAATATCTATTTAATTAGGAGGAAACTACAATGCTGACTTT +TGTCTGAATTATGTAGATAAGTGATTCATTTGAAACAATTATTTTGATAATTGTCAATTA +TCCATTTCATTTTAATGCATTTTTTATTCTTTTTTCAAAAATAGCAACAATTACAACAGT +TAAACCTTATAATGAATATGTTTCCTAAACCCTGTTCTACTTTCTGGTTCCAGATCTGAC +ACCAATTACCTTTCTGATTTTGGACAAACCACTTAATATTTGTAACTTACAATTACTTCA +ACTGAATAATAAAAGAATTGGACTAGATTTCTCCAACATCTCTCTCTTTTGGCTTTATGT +TAGATAATGCTAAATTTTCATCATATCCAAACATGCTATATAATTTTATGAACTGTTACA +GAGTCAGACATAAGCAATATAAAGTATGATTCTGAATAAAGGCTGTGAAGTCTCGTTTTT +CATACATAATTACAGGAACCGATCAAATTCAATAAAGCATTTATAGTCATGACAATATAT +TCTCTTAACTTGCAATGTGGTTTTAGGAAACAATGAAACAAATTAAACAAATGCATGATT +CCTTAAATTTTGTTGACTCGGGATTTAGTTACATATAAGACTCTTTTCCCCCAGCCAGAT +TAACCCTGCTCTGTATATGTAATACGCATCTTCCCCCCAAATTTCACAATAATAATTTTA +TGAAAATCAGTATGTAAGTTGCTGCATTGGCATGAAAGGCTACTAATGTGATATCTTTCA +AAGATAAAGTGAACGTTTTTGTAAAAAGCCACCACACTGGAGGCATTTAACAATATTCAT +CTTCCTATGGCATTACTACCCTAGATGTACTTTGCAATATTAATCAAGCCTTGTCTTACT +TCAGGCTTCCTAGAAATAGAGCACAAGGCAGGGATTCTTTCTTGCTCGGTGATTTATTGA +GGAAGTGCTCTCAGAAGAAATTGGTAAGATGCTAAGGAGAACAGCAAAGGACAGGAGAAG +GGGGCTGAGCAGAGATGGGGATCTAACTGGAATCTGGCCTTTGCTGATTACCAGAGCAGC +TCTGGGTGCGAATGGTGAAGTGCTGTTCTATTGACAAACTCCACTCAAGTGACATTCCAT +GCAGCACTGCCAAATTCTGTCCCATAACTCTGCTAGCTCTTCAACAAGAAGTAACTTGAG +GCAGCACAGTTACAGAAAACAGCACAAGAACCAAGGATACTAAACAGTGACTTAAATGTT +TTTAGATAAGAGGAAGCAATGAGATACTATAGGGTTTTAATTTCTGTGCAATGGAAAACT +AGTATTAAATATTTATTAATTAGGAGGAAACGACAATGCTGACTTCTGTCTGCATTATAG +ACAAAATTGAGTTCTCCAACCATGAGGCAAGGTGCTGGCTTTTTGTACCCCTGCACTATT +CATTTGATGGGTGAGTGCACTGTAACTTCCAGACGTCTCTTGGTAGGTGGGCCCCACTGG +CCAAGGGCATTTCCCGGGAGAGTGAACGGCTGTGAGCTACTAGCAGTAGCAGTCACAGCA +GCTGGGAGGAAACACACTGGTCAGGTAAAGGGCCAGCATCTACCAAGGTCCAGTTCCACA +ATTAGTGGAGAAATATTTAATAACATTATTTTTGAAAATAATTAGGCCATATGACTTTGA +CACTTTTTCTCTAGCAAAGTGACTAAAGAGAAGCAGGTTTTTAACTGGGTTTTTATTTCT +GTTGTGTCTCTAGCCAAAGCCATGCTGATGTTTGATAGTTTTTTTTTTTCTTTTTTTAAG +AGATGGGGTTTCACCATGTTGGCCATGCTGGTCGTGAACTCCTGACCTCAATGGCCTCCC +AAACTGTTGGGATTACAGACGTGAGCCACCACGCCCAGCCAGAATTTTTTTTCCTAAAGA +ATAGAACAACATTATAAAATTTTAGGCATTAAGGACAAATTTTATTTTATGATTTTCATT +TCGGTGAGTCACAAGATATTCAACACAAAATGAAACTTCACAAAATTTCACAAAATGAAA +AATTCATTTCAGATTCAATATCCACCTCCATCATCCATATTCATTCTTCCAATGTCTCAG +GCCAGAAATTTGGAGTATGTGGCTTCTCCACCTCACACAATTTGCCCTAACTTTAATATA +TACTCAGAAATTACTGGCTTTTCACTGTTTCTATGATTCCCATGTGTAATATACAATACT +CACCATGCATACAATAATACAATAATTCTGTGTCACAACCACACCTAAATTGGTAAGTTT +ATAAGGTTATAAGCTGAGAGGTTTTGCTGATCTTGGCTGAGCTCAGCTGGGCAGGTCTTC +CGGTCTTGGCTGGGGTTCACTGACACACAAGCAGCTGACAGTTGGCTGATCTAGGATGGC +CTCAGCTGGGATGACAGGCTGTTTCCTCACCTTCCAGCAGGCAAGCCAGTCCCAAGAAAG +AGAAGGGTGAAACATGGAGGCCATTAATTGAGCCCATCGCATGAAACGCATCTGTGACAC +CATCACCGTCTGATATCAACTCTCACCTGGATGTTTGCAATCGCCTAACTTTTCTCCTTT +CATTCACTATGCTGCCTTACAAACCTATTCTCCACAAATCAGCTAGAGCAAACCTTTTAA +ATCCTAAGTAGAATGCTACCATTCCTCTGCTCAAACTACTGGAGAGGAGATGCCACAGTC +TTTACTATGGTCTTCAAGACCCTATGGGAGGTAGCCCTGTCTTACAACCATAGCCTCCTA +CCACTGCCCCAACATGCACACTGAGTTCCGGCCACAGCATCTGCTTCCTCTTTATTGATC +TGCCAAGGACATGAATGACTCAGAATCTCAAACTTATTCTTCCCTCAGAATCCACTGAGC +CTACTCCCTCTTTTTCTCAAATGGCAACTTCTCAGAGATGCCTTCTCTGGCTAACGTATA +TGTACTAAAACCTCCATCCAGCACTCTATGTCATCCTTACTATGGTTTATTTTTCTTCAT +AGCACCTATCAATTGGTGAAGTATTAAATATGCATTTTTGTTTGTATGCATCTCTCTCCC +CGCACCAGTATGTGAACTCCACCAGACTGGAGAGCGTGTTTATTTTGTTCACTGCTGTAA +CTCCAGTGTCTAGAACAGTGCCTGGCACACAGTAGGTGTTTAATAATGATTTGTTAAGCT +AAGCCAATGAATAAATATTCTTTTGCCCATGAGATGATGTTAAAAATTTTTCAATTATTC +ATAACTGCCTGTAAGGAATGAATGTTAGTGAATTACTGTGATACCAATGAAAGTTAAATG +ATGCAATGAAGATTGGGTGGATCACAAGGTCAGGAGTTTGAGACCATCCTGGCCAATATG +GTGAAACCCCGTCTCTACTAAAAATACCAAAATTAGCTGGGCATCGTGGCAGGACCCTGT +AGTCCCAGCTACTCAGGAGGCTGAGGCAGGAGAATCGCTTGAACCCAGGAGGCGGAGGTT +GCAGTGAGCCGAGATCACGCCACTGCACTCCAGCCTGGCCGACAGAGTGAGATTCTGTCT +CCAAAAAAAAAAAAAAAAAAGATGTAAATATTATTATTGTTTGCCATCACCCTTATGTGC +TGTCTTGGCTTTATGTCGTGCTTAGATCTTTTCCAGGTGCAATTTGAATTGATGCAACTA +ATGGAAACTACAACAGAAGTTCATATTGCCCTATTGTATTACACTATTTGACTCATCTTT +CAGTATCACCAGGTGCATAAGGAAAATTTCAAAAGTAAATCAAGAGAAAGAAATATGATC +GCAGCTTAACACAATGTAAATTTATTATTTGTACTTTTTGTCTAAATGGTTTGCCTAAAA +GACTGAAAGACATTTTATATTAGTTAGAATACTTGAGGATAATAACATAAAAACTTTCCT +TTCCAACTTGTTTATAAAAGGAAATCTTCACTGTTTTGAACATCAGTTATTTTAAACTTT +TAAGTTGTTAGCACAGCAAAAGCAACAAAATTCTAAGTGCAGTAATCACTTTACTGCGTG +GTCATATGAAATCAAGGCAATGTTATGAGTATTACTGGAAAGCTGGACAGAGTAACGGGA +AAAGTGACTAAAACTATGCAAAACTATGCAAAACTAAGCAGATTGTGTCTCTAGAGTATT +TCCCATCTCAAGTTTAGTTATTTACTAATTTGGCAACATCTGACCTATCTTTAATTGTGA +GAAAATAAACAAACACATAAGCCAACTCTCAGAATATGGTTATACATAGGTGTAGCCTAT +GACTTTGAATGTATTTGTTTGAATAGCGTAAAACAAAATAAAAATAAAATCTTGTTACAG +TGCAAGAAACGGCAGTCATCAAACTAAGATGAGGCAAGTGTCATGAAGTATGAAAATATG +GTACCTGAATTCTATTTATTAGAAAGTCTTCACTGAGCTGAGCATGTTTTTTTTAACAAA +TTCAATTACTGATTTGAATATTTATTATACTTAATTATTGCAGCCATGAAAAGAGGTGCT +GGCTGAGGCTGCATTTAATAAAAACATTTAATCAGCTTGAGGTTAGTAAACCATTTAATT +TGTTTTTTCATGAAGATTTAACTTCTAGAATAATTTCATTTATGTATTTTTAGGTATAGC +CCTAGATTCTGGTCTACATAGTATACAAATCATTTTAGAATGACACTAGGTTATTTCAAC +TGCTTTTCTACAGAAGTGTTAAATAAGGGAGTAAAGTGTTGGCTTTTTCCATAATTGAAA +TAAATGCACAATGAGCAGTAACATCCTGATTTCACTGCTATTTTGTTTAATCAACATAAT +GATGTAGATTTACTCTGTATATATATGGAAGAGTGAAAGAAGGTTGGGAAGGAATAACTA +TCAATTAATATAGGTGATATAGTAGTTATTTTTGCAAATCAACTATAATTTCTGAATGGA +TATTCAGACCATATTTACATTACATAGAAGAGGCACACACCAAAAGATTTAACAAATGTG +CCAAATATTGGTGAATATTTAGTTAGGTACCAAAAGGATGTTGTATAAATTAGGATGCTT +TCAACCATAAGAGACTCATCTCAAAAATGGCTTGAAAATGTGGGGAATTTTTATCTCAGT +GTGAAGTTAAAGGTAGGGCAAGTCCAAATCAATTCAAGTTAATTAAAGCCCCAAGTTCTT +GGAATTTGCTCTGCTAGTCTTAGCTGGTTAGCCTTTGTCCTGAAGTTTGTAACTTCATGA +CCAGAAGATGATCGCAATATTTTCTAAGTATAAAGCCTGAAGGTGTAAACCAGGTAGTCT +CAGCAAAACCAATGATGCATGGTCACCTTTCCCTTAGTTGACAAATACTTGCCTTCCAGA +TACTTTGAATCAGAATGGGCATTTTAACTAAGATCCAGTGCAACTAAAGGAATAATCAAA +CAAATGATAAAATAATTATTCTGAGCTGAAAACACAAGTCTGAATATTGGAAGAGGTTCC +TGATTTCCAGGCAGAATAGATAAGCAAAGATATCAACCTAAACACATCCTGGTTATAGTC +TAAAATTTAGAGAATAAATGGGAAAAGATTATGAACTTTAAGGAAGAAATAACAACTTAC +ACATAAAACAAAAAGGAAAAACTATCTGTTGACAGATTTATCACCTGCTAGATAAGAGCA +GAATAACCATTCACTAGGAGAAAATGGGAGAAGCTAGAAGATACTGGAGTAATATTTATA +GAGTACTGAAGAAAAAACAAAAAACAGAAATTCAATACACAGCTAAGATATCATTTACCT +GTAAATGATACATGGTAAAATAAAGGGTAAAATAAGGATATTTGCAGATTCACAAGGAGA +AGTTAGCCTTCACATACTCAGCTGAGGAAAATCCTAGAGACAAAACTCTAACAAAAACAA +ACTAGCCGGGTGCAGTGGTGCAGGCCTGTAGTTCCAGCTACTCAAGAGGCTGAAGTAAGA +GGATCACTGGAGCCCAGGGGTTCTGGGCTGTACTGTGCTATGCTGATCAGGTGTCCACAC +TTAGCTCGCCATCAATATGATGACCTCCCAGGTGGGAAATGGAGCAGGTGAAAACTCCCA +TGCTGATCAGTAGTGGGATCATGTCTGTGAATAATCACTGCACTCCAACTGGGAAACATA +GCAAGACCCCATCTCTAAAAAACAAACAAACAAACAAACAAACAAACAAAAATGTTTCAG +ACAAATGTCAAGATAGAGTAAAAGAAGAAAGTACTGAAGGCCCTTCAACATAAATTGGAT +CAGATAATAAAAATAATAGCAAAGTTCTTTTCATGCTGTATCCTTAATTCTTCACCATAA +TCTTAGGAAGTGAATGTATTAATTATCTTTTGCTATATAACAAATTACTCCCAAAACTTG +GCGGCTTAAAACAACAAATATTATTTCACAATTTCTGTGGGTCAAGAATTTGGAAGTAGT +GACTCTGGCTCAGGGTCTCATTTAAGGTGGTAGTTCAGGATGCCAGTCAGGGCTGCAGGC +ACTGAGGCTGCTTCCTCAATGGCCCACTCACATGGCTGTTGGCTGGAGGCCTCTCTTTCT +CACCACACGGGCCTCTTCATAGGACTGCCCGAGTGTCCTTACAGCGTGGCAGCTGGCTTC +CCCCAGAGTGAACATTCTGAGAGAGAGAGAAAGAGAGAGTTAGAAGGCATGCTGTCACTT +CCACTGTATCCCATTCACCAGAAGTGAGAGACTAAATTCACCAAACAGAAAAGTGAAGGA +GAATGAGGCTCCAGTTTTTTGGGTGAGAGTTGAAGAATGTATGGATATTTTGAACAACCA +CAATATAATTCTTCTTTTCACAGAAGCACAAAAAAATTTATTTAACTTGTCCAAGTTTAC +ACAGTTAGCAAGCAACACCTTTGAGAAAAAAATCCATGTAGTCTGATACAAGCACCCAAA +CTCATAACCACAATGTGAATCTAACTGCTTTTCAATTAAAAAAGAAAGAAAGATTCCCTT +CAAATCTGGCATATGCATTCACATGGAGCATTCATACTGCCAGTGACAGTACCATAGTTA +TATGGAATTAGAAGTTCTAACTTATCTTGGCCAAACTAAAGACTTAGGGCTGGGTAGAAG +GTTGGAGGGATGTAAGGTCATTCTCAAGATCTCATCTAGGAGAAGAAAACAAAATGGGGA +AGTAGAAGACAAAATGCTTTTTTAGGTTGGGAAAGGACTGGGAGAATCAAGCATCTAGAA +ATGGGCACAAAGAGTTACCTTATTTTATTTAAAAGAAAATAAATGTTTGACTATTAATGC +CTGAGAACGGAAGGTGATTATTAATGAGATGAAAAAGTTAATCAGATTCTCCAAGTTAGG +AGGGACTTGAAGACCAAATTGATAAAAATAAAAAAAAAGATGTCATAGTAGAATAATCTA +GATAATAAGCAATCAATGAGACTGAAAAAATAAAATCAAGTATATCATTTGTTACACTAA +ATATTAATATACCAGATTCTCTCATTAAAAAAACAGAGAAAGTCAAATTGGATTAAATAA +GAACAAAAAGTTAGCTATATAGTATTTATCAGAAACATTCTTATAAACAAATTGATAATG +AAAGATTAAAAATAAGAGATTTGAGGCAAGGCAAGCAAAAAGAAATAAATGTTAAACAAG +GAGAAATTAAAGGCTACGGACATTACCTAAGGAAAAGGATGACATAGAGTTACAGTGGCA +AAAGTTAGGAAGCAGATGACATAAATCTATATGCACAAACAGTATGGCCACAAAATACAT +TAATTAAAAATTACTAGAAATATAAGATGACTTTGATTAAAATACACTGATTACAAGGGA +TTTAACATATAAAAATTAGGCTGATGTGGTAAATTTAAATATAATCAAATATTTAGGAAA +ATAGAACAACACAACAAAGTTGATTACATATATTCATTTTCCAGATAGTATACTTTATGC +CTATGAAATAGTTCTTAAAATCAATTATATATGGCCGGGTGCAGTGGCCCACGCCTGTAA +TCCCAGCACTTTGGGAGGCCAAGGCAGGTGGATCACGAGGTCAGGAGATCGAGACCATCC +TGGCTAACACAGTGAAACCCCGTCTCTACTAAAAATACAAAAAAAAAAAAAATTAGCTGG +GCGTGGTGGCTGGAACCTGTAGTCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATGGCGT diff --git a/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta.fai b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta.fai new file mode 100644 index 000000000..77ced1f80 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/cram/human_g1k_v37.20.subset.fasta.fai @@ -0,0 +1 @@ +20 9000 4 60 61 diff --git a/src/test/resources/htsjdk/samtools/cram/test.dict b/src/test/resources/htsjdk/samtools/cram/test.dict deleted file mode 100644 index dfb98d63a..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test.dict +++ /dev/null @@ -1,2 +0,0 @@ -@HD VN:1.4 SO:unsorted -@SQ SN:Sheila LN:20 M5:7ddd8a4b4f2c1dec43476a738b1a9b72 UR:file:/Users/edwardk/Documents/htsjdk/testdata/htsjdk/samtools/cram/auxf.fa diff --git a/src/test/resources/htsjdk/samtools/cram/test.fa b/src/test/resources/htsjdk/samtools/cram/test.fa deleted file mode 100644 index 11d25dda6..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test.fa +++ /dev/null @@ -1,2 +0,0 @@ ->Sheila -GCTAGCTCAGAAAAAAAAAA diff --git a/src/test/resources/htsjdk/samtools/cram/test.fa.fai b/src/test/resources/htsjdk/samtools/cram/test.fa.fai deleted file mode 100644 index f3cdedb55..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test.fa.fai +++ /dev/null @@ -1 +0,0 @@ -Sheila 20 8 20 21 diff --git a/src/test/resources/htsjdk/samtools/cram/test2.dict b/src/test/resources/htsjdk/samtools/cram/test2.dict deleted file mode 100644 index dfb98d63a..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test2.dict +++ /dev/null @@ -1,2 +0,0 @@ -@HD VN:1.4 SO:unsorted -@SQ SN:Sheila LN:20 M5:7ddd8a4b4f2c1dec43476a738b1a9b72 UR:file:/Users/edwardk/Documents/htsjdk/testdata/htsjdk/samtools/cram/auxf.fa diff --git a/src/test/resources/htsjdk/samtools/cram/test2.fa b/src/test/resources/htsjdk/samtools/cram/test2.fa deleted file mode 100644 index 11d25dda6..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test2.fa +++ /dev/null @@ -1,2 +0,0 @@ ->Sheila -GCTAGCTCAGAAAAAAAAAA diff --git a/src/test/resources/htsjdk/samtools/cram/test2.fa.fai b/src/test/resources/htsjdk/samtools/cram/test2.fa.fai deleted file mode 100644 index f3cdedb55..000000000 --- a/src/test/resources/htsjdk/samtools/cram/test2.fa.fai +++ /dev/null @@ -1 +0,0 @@ -Sheila 20 8 20 21 From 18508d4f0ba565c96c69a2249e09a89b231e6f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 10 Feb 2017 17:07:15 +0100 Subject: [PATCH 077/137] Make constructors public in CustomGzipOutputStream (#798) --- src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java b/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java index cb3652ed5..16f99501c 100644 --- a/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/CustomGzipOutputStream.java @@ -11,13 +11,13 @@ * @author Tim Fennell */ public class CustomGzipOutputStream extends GZIPOutputStream { - CustomGzipOutputStream(final OutputStream outputStream, final int bufferSize, final int compressionLevel) throws + public CustomGzipOutputStream(final OutputStream outputStream, final int bufferSize, final int compressionLevel) throws IOException { super(outputStream, bufferSize); this.def.setLevel(compressionLevel); } - CustomGzipOutputStream(final OutputStream outputStream, final int compressionLevel) throws IOException { + public CustomGzipOutputStream(final OutputStream outputStream, final int compressionLevel) throws IOException { super(outputStream); this.def.setLevel(compressionLevel); } From 8b9d5d5e2bead77fa1ea7d7178439d8df24f19a0 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Fri, 10 Feb 2017 16:43:05 -0500 Subject: [PATCH 078/137] Changed parameter names and doc to be consistent in use of "offset" and "position". --- src/main/java/htsjdk/samtools/SAMRecord.java | 91 ++++++++++++++++------------ 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 9f03b2312..8e61d150d 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -630,38 +630,44 @@ public int getUnclippedEnd() { /** - * @param offset 1-based location within the unclipped sequence or 0 if there is no position. - *

* Non static version of the static function with the same name. - * @return 1-based inclusive reference position of the unclipped sequence at a given offset, + * + * @param position 1-based location within the unclipped sequence + * @return 1-based reference position of the unclipped sequence at a given read position, + * or 0 if there is no position. */ - public int getReferencePositionAtReadPosition(final int offset) { - return getReferencePositionAtReadPosition(this, offset); + public int getReferencePositionAtReadPosition(final int position) { + return getReferencePositionAtReadPosition(this, position); } /** - * @param rec record to use - * @param offset 1-based location within the unclipped sequence - * @return 1-based inclusive reference position of the unclipped sequence at a given offset, - * or 0 if there is no position. + * Returns the 1-based reference position for the provided 1-based position in read. + * * For example, given the sequence NNNAAACCCGGG, cigar 3S9M, and an alignment start of 1, - * and a (1-based)offset 10 (start of GGG) it returns 7 (1-based offset starting after the soft clip. + * and a (1-based) position of 10 (start of GGG) it returns 7 (1-based position starting after + * the soft clip. + * * For example: given the sequence AAACCCGGGTTT, cigar 4M1D6M, an alignment start of 1, - * an offset of 4 returns reference position 4, an offset of 5 returns reference position 6. + * a position of 4, returns reference position 4, a position of 5 returns reference position 6. + * * Another example: given the sequence AAACCCGGGTTT, cigar 4M1I6M, an alignment start of 1, - * an offset of 4 returns reference position 4, an offset of 5 returns 0. + * a position of 4 returns reference position 4, an position of 5 returns 0. + * + * @param rec record to use + * @param position 1-based location within the unclipped sequence + * @return 1-based reference position of the unclipped sequence at a given read position, + * or 0 if there is no position. */ - public static int getReferencePositionAtReadPosition(final SAMRecord rec, final int offset) { - - if (offset == 0) return 0; + public static int getReferencePositionAtReadPosition(final SAMRecord rec, final int position) { + if (position == 0) return 0; for (final AlignmentBlock alignmentBlock : rec.getAlignmentBlocks()) { - if (CoordMath.getEnd(alignmentBlock.getReadStart(), alignmentBlock.getLength()) < offset) { + if (CoordMath.getEnd(alignmentBlock.getReadStart(), alignmentBlock.getLength()) < position) { continue; - } else if (offset < alignmentBlock.getReadStart()) { + } else if (position < alignmentBlock.getReadStart()) { return 0; } else { - return alignmentBlock.getReferenceStart() + offset - alignmentBlock.getReadStart(); + return alignmentBlock.getReferenceStart() + position - alignmentBlock.getReadStart(); } } return 0; // offset not located in an alignment block @@ -669,8 +675,9 @@ public static int getReferencePositionAtReadPosition(final SAMRecord rec, final /** + * Returns the 1-based position in the read of the 1-based reference position provided. + * * @param pos 1-based reference position - * return the offset * @return 1-based (to match getReferencePositionAtReadPosition behavior) inclusive position into the * unclipped sequence at a given reference position, or 0 if there is no such position. * @@ -681,37 +688,43 @@ public int getReadPositionAtReferencePosition(final int pos) { } /** + * Non-static version of static function with the same name. See examples below. + * * @param pos 1-based reference position - * @param returnLastBaseIfDeleted if positive, and reference position matches a deleted base in the read, function will - * return the offset + * @param returnLastBaseIfDeleted if positive, and reference position matches a deleted base in the read, + * function will return the offset * @return 1-based (to match getReferencePositionAtReadPosition behavior) inclusive position into the - * unclipped sequence at a given reference position, - * or 0 if there is no such position. If returnLastBaseIfDeleted is true deletions are assumed to "live" on the last read base - * in the preceding block. - * - * Non-static version of static function with the same name. See examples below. + * unclipped sequence at a given reference position, or 0 if there is no such position. If + * returnLastBaseIfDeleted is true deletions are assumed to "live" on the last read base + * in the preceding block. */ public int getReadPositionAtReferencePosition(final int pos, final boolean returnLastBaseIfDeleted) { return getReadPositionAtReferencePosition(this, pos, returnLastBaseIfDeleted); } /** - * @param rec record to use - * @param pos 1-based reference position - * @param returnLastBaseIfDeleted if positive, and reference position matches a deleted base in the read, function will - * return the offset - * @return 1-based (to match getReferencePositionAtReadPosition behavior) inclusive position into the - * unclipped sequence at a given reference position, - * or 0 if there is no such position. If returnLastBaseIfDeleted is true deletions are assumed to "live" on the last read base - * in the preceding block. + * Returns the 1-based position in the read of the provided reference position, or 0 if no + * such position exists. + * * For example, given the sequence NNNAAACCCGGG, cigar 3S9M, and an alignment start of 1, - * and a (1-based)pos of 7 (start of GGG) it returns 10 (1-based offset including the soft clip. + * and a (1-based) pos of 7 (start of GGG) it returns 10 (1-based position including the soft clip). + * * For example: given the sequence AAACCCGGGT, cigar 4M1D6M, an alignment start of 1, - * a reference position of 4 returns offset of 4, a reference of 5 also returns an offset 4 (using "left aligning") if returnLastBaseIfDeleted - * and 0 otherwise. + * a reference position of 4 returns read position 4, a reference position of 5 also returns a read + * position of 4 if returnLastBaseIfDeleted and 0 otherwise. + * * For example: given the sequence AAACtCGGGTT, cigar 4M1I6M, an alignment start of 1, - * a position 4 returns an offset 5, a position of 5 returns 6 (the inserted base is the 5th offset), a position of 11 returns 0 since - * that position in the reference doesn't overlap the read at all. + * a position 4 returns a position of 5, a position of 5 returns 6 (the inserted base is the 5th read position), + * a position of 11 returns 0 since that position in the reference doesn't overlap the read at all. + * + * @param rec record to use + * @param pos 1-based reference position + * @param returnLastBaseIfDeleted if positive, and reference position matches a deleted base in the read, + * function will return the position of the last non-deleted base + * @return 1-based (to match getReferencePositionAtReadPosition behavior) inclusive position into the + * unclipped sequence at a given reference position, or 0 if there is no such position. If + * returnLastBaseIfDeleted is true deletions are assumed to "live" on the last read base + * in the preceding block. * */ public static int getReadPositionAtReferencePosition(final SAMRecord rec, final int pos, final boolean returnLastBaseIfDeleted) { From 55bf01b28e3e6a321f2249475b9ca6b5459ed91a Mon Sep 17 00:00:00 2001 From: Tom White Date: Fri, 17 Feb 2017 16:40:30 +0000 Subject: [PATCH 079/137] Make BAMFileReader and some related classes public, and expose (#786) methods for iterating over a part of a BAM file (needed for Hadoop-BAM, which processes BAM files in parallel). Also, add BAMFileSpan#removeContentsAfter method to mirror removeContentsBefore. --- src/main/java/htsjdk/samtools/BAMFileReader.java | 53 ++++++++++++---- src/main/java/htsjdk/samtools/BAMFileSpan.java | 46 +++++++++++++- src/main/java/htsjdk/samtools/SamReader.java | 6 +- src/test/java/htsjdk/samtools/BAMFileSpanTest.java | 70 ++++++++++++++++++++++ 4 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/BAMFileSpanTest.java diff --git a/src/main/java/htsjdk/samtools/BAMFileReader.java b/src/main/java/htsjdk/samtools/BAMFileReader.java index c2f9d4ea3..3026eaeda 100644 --- a/src/main/java/htsjdk/samtools/BAMFileReader.java +++ b/src/main/java/htsjdk/samtools/BAMFileReader.java @@ -40,7 +40,7 @@ /** * Class for reading and querying BAM files. */ -class BAMFileReader extends SamReader.ReaderImplementation { +public class BAMFileReader extends SamReader.ReaderImplementation { // True if reading from a File rather than an InputStream private boolean mIsSeekable = false; @@ -869,25 +869,56 @@ private void assertIntervalsOptimized(final QueryInterval[] intervals) { } } - private CloseableIterator createIndexIterator(final QueryInterval[] intervals, - final boolean contained) { - - assertIntervalsOptimized(intervals); - - // Hit the index to determine the chunk boundaries for the required data. + /** + * Use the index to determine the chunk boundaries for the required intervals. + * @param intervals the intervals to restrict reads to + * @param fileIndex the BAM index to use + * @return file pointer pairs corresponding to chunk boundaries + */ + public static BAMFileSpan getFileSpan(QueryInterval[] intervals, BAMIndex fileIndex) { final BAMFileSpan[] inputSpans = new BAMFileSpan[intervals.length]; - final BAMIndex fileIndex = getIndex(); for (int i = 0; i < intervals.length; ++i) { final QueryInterval interval = intervals[i]; final BAMFileSpan span = fileIndex.getSpanOverlapping(interval.referenceIndex, interval.start, interval.end); inputSpans[i] = span; } - final long[] filePointers; + final BAMFileSpan span; if (inputSpans.length > 0) { - filePointers = BAMFileSpan.merge(inputSpans).toCoordinateArray(); + span = BAMFileSpan.merge(inputSpans); } else { - filePointers = null; + span = null; } + return span; + } + + private CloseableIterator createIndexIterator(final QueryInterval[] intervals, + final boolean contained) { + + assertIntervalsOptimized(intervals); + + BAMFileSpan span = getFileSpan(intervals, getIndex()); + + // Create an iterator over the above chunk boundaries. + final BAMFileIndexIterator iterator = new BAMFileIndexIterator(span == null ? null : span.toCoordinateArray()); + + // Add some preprocessing filters for edge-case reads that don't fit into this + // query type. + return new BAMQueryFilteringIterator(iterator, new BAMQueryMultipleIntervalsIteratorFilter(intervals, contained)); + } + + /** + * Prepare to iterate through SAMRecords that match the given intervals. + * @param intervals the intervals to restrict reads to + * @param contained if true, return records that are strictly + * contained in the intervals, otherwise return records that overlap + * @param filePointers file pointer pairs corresponding to chunk boundaries for the + * intervals + */ + public CloseableIterator createIndexIterator(final QueryInterval[] intervals, + final boolean contained, + final long[] filePointers) { + + assertIntervalsOptimized(intervals); // Create an iterator over the above chunk boundaries. final BAMFileIndexIterator iterator = new BAMFileIndexIterator(filePointers); diff --git a/src/main/java/htsjdk/samtools/BAMFileSpan.java b/src/main/java/htsjdk/samtools/BAMFileSpan.java index 193e44376..485f69dcf 100644 --- a/src/main/java/htsjdk/samtools/BAMFileSpan.java +++ b/src/main/java/htsjdk/samtools/BAMFileSpan.java @@ -115,15 +115,55 @@ public SAMFileSpan removeContentsBefore(final SAMFileSpan fileSpan) { validateSorted(); final BAMFileSpan trimmedChunkList = new BAMFileSpan(); + final long chunkStart = bamFileSpan.chunks.get(0).getChunkStart(); for(final Chunk chunkToTrim: chunks) { - if(chunkToTrim.getChunkEnd() > chunkToTrim.getChunkStart()) { - if(chunkToTrim.getChunkStart() >= bamFileSpan.chunks.get(0).getChunkStart()) { + if(chunkToTrim.getChunkEnd() > chunkStart) { + if(chunkToTrim.getChunkStart() >= chunkStart) { // This chunk from the list is completely beyond the start of the filtering chunk. trimmedChunkList.add(chunkToTrim.clone()); } else { // This chunk from the list partially overlaps the filtering chunk and must be trimmed. - trimmedChunkList.add(new Chunk(bamFileSpan.chunks.get(0).getChunkStart(),chunkToTrim.getChunkEnd())); + trimmedChunkList.add(new Chunk(chunkStart,chunkToTrim.getChunkEnd())); + } + } + } + return trimmedChunkList; + } + + /** + * Creates a new file span by removing all chunks after the given file span ends. + * If a chunk in the chunk list starts before and ends after the given + * chunk, the second portion of the chunk will be deleted. + * @param fileSpan The filespan after which to eliminate. + * @return A new BAMFileSpan which contains the portion of the chunk list before the + * given chunk. + */ + public SAMFileSpan removeContentsAfter(final SAMFileSpan fileSpan) { + if(fileSpan == null) + return clone(); + + if(!(fileSpan instanceof BAMFileSpan)) + throw new SAMException("Unable to compare "); + + final BAMFileSpan bamFileSpan = (BAMFileSpan)fileSpan; + + if(bamFileSpan.isEmpty()) + return clone(); + + validateSorted(); + + final BAMFileSpan trimmedChunkList = new BAMFileSpan(); + final long chunkEnd = bamFileSpan.chunks.get(bamFileSpan.chunks.size() - 1).getChunkEnd(); + for(final Chunk chunkToTrim: chunks) { + if(chunkToTrim.getChunkStart() < chunkEnd) { + if(chunkToTrim.getChunkEnd() <= chunkEnd) { + // This chunk from the list is completely before the end of the filtering chunk. + trimmedChunkList.add(chunkToTrim.clone()); + } + else { + // This chunk from the list partially overlaps the filtering chunk and must be trimmed. + trimmedChunkList.add(new Chunk(chunkToTrim.getChunkStart(),chunkEnd)); } } } diff --git a/src/main/java/htsjdk/samtools/SamReader.java b/src/main/java/htsjdk/samtools/SamReader.java index 2f1b2f9dd..0c551a045 100644 --- a/src/main/java/htsjdk/samtools/SamReader.java +++ b/src/main/java/htsjdk/samtools/SamReader.java @@ -381,7 +381,11 @@ public PrimitiveSamReaderToSamReaderAdapter(final PrimitiveSamReader p, final Sa this.resource = resource; } - PrimitiveSamReader underlyingReader() { + /** + * Access the underlying {@link PrimitiveSamReader} used by this adapter. + * @return the {@link PrimitiveSamReader} used by this adapter. + */ + public PrimitiveSamReader underlyingReader() { return p; } diff --git a/src/test/java/htsjdk/samtools/BAMFileSpanTest.java b/src/test/java/htsjdk/samtools/BAMFileSpanTest.java new file mode 100644 index 000000000..4fc39b294 --- /dev/null +++ b/src/test/java/htsjdk/samtools/BAMFileSpanTest.java @@ -0,0 +1,70 @@ +package htsjdk.samtools; + +import java.util.Arrays; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class BAMFileSpanTest { + @Test(dataProvider = "testRemoveContentsBeforeProvider") + public void testRemoveContentsBefore(BAMFileSpan originalSpan, BAMFileSpan cutoff, + BAMFileSpan expectedSpan) { + // only start value in cutoff is used + Assert.assertEquals( + ((BAMFileSpan) originalSpan.removeContentsBefore(cutoff)).getChunks(), + expectedSpan.getChunks()); + } + + @DataProvider(name = "testRemoveContentsBeforeProvider") + private Object[][] testRemoveContentsBeforeProvider() { + return new Object[][] { + { span(chunk(6,10), chunk(11,15)), null, span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(), span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(6,0)), span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(7,0)), span(chunk(7,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(9,0)), span(chunk(9,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(10,0)), span(chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(11,0)), span(chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(12,0)), span(chunk(12,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(15,0)), span() }, + { span(chunk(6,10), chunk(11,15)), span(chunk(16,0)), span() }, + { span(chunk(6,10), chunk(11,15)), span(chunk(6,10), chunk(7,16)), span(chunk(6, 10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(16,17), chunk(18,19)), span() }, + }; + } + + @Test(dataProvider = "testRemoveContentsAfterProvider") + public void testRemoveContentsAfter(BAMFileSpan originalSpan, BAMFileSpan cutoff, + BAMFileSpan expectedSpan) { + // only end value in cutoff is used + Assert.assertEquals( + ((BAMFileSpan) originalSpan.removeContentsAfter(cutoff)).getChunks(), + expectedSpan.getChunks()); + } + + @DataProvider(name = "testRemoveContentsAfterProvider") + private Object[][] testRemoveContentsAfterProvider() { + return new Object[][] { + { span(chunk(6,10), chunk(11,15)), null, span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(), span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,6)), span() }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,7)), span(chunk(6,7)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,9)), span(chunk(6,9)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,10)), span(chunk(6,10)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,11)), span(chunk(6,10)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,12)), span(chunk(6,10), chunk(11,12)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,15)), span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,16)), span(chunk(6,10), chunk(11,15)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,6), chunk(7,10)), span(chunk(6, 10)) }, + { span(chunk(6,10), chunk(11,15)), span(chunk(0,6), chunk(7,16)), span(chunk(6, 10), chunk(11,15)) }, + }; + } + + private BAMFileSpan span(Chunk... chunks) { + return new BAMFileSpan(Arrays.asList(chunks)); + } + + private Chunk chunk(long start, long end) { + return new Chunk(start, end); + } +} From cabe78f67663fce537241f3c3a337bc9459a681e Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Tue, 21 Feb 2017 14:54:40 -0500 Subject: [PATCH 080/137] add a coverage drop threshold to limit codecov failure spam (#803) * add a coverage drop threshold to limit codecov failure spam * making codcov.yml a hidden file --- codecov.yml => .codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename codecov.yml => .codecov.yml (94%) diff --git a/codecov.yml b/.codecov.yml similarity index 94% rename from codecov.yml rename to .codecov.yml index 98fb05f4c..7167553f5 100644 --- a/codecov.yml +++ b/.codecov.yml @@ -10,7 +10,7 @@ coverage: project: default: target: auto - threshold: null + threshold: .01 branches: null patch: From 912c28bec415c430b43515652ccaf13222b07e7b Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Tue, 21 Feb 2017 18:15:13 -0500 Subject: [PATCH 081/137] automatically adding @Override annotations (#729) added the @Override annotation everywhere it was missing using autoinspection --- .../java/htsjdk/samtools/AbstractBAMFileIndex.java | 3 +++ .../java/htsjdk/samtools/AsyncSAMFileWriter.java | 2 ++ src/main/java/htsjdk/samtools/BAMFileReader.java | 19 +++++++++++++++++++ src/main/java/htsjdk/samtools/BAMFileSpan.java | 4 ++++ src/main/java/htsjdk/samtools/BAMFileWriter.java | 4 ++++ src/main/java/htsjdk/samtools/BAMIndex.java | 1 + src/main/java/htsjdk/samtools/BAMIndexWriter.java | 1 + src/main/java/htsjdk/samtools/BAMRecord.java | 1 + src/main/java/htsjdk/samtools/BAMRecordCodec.java | 5 +++++ src/main/java/htsjdk/samtools/Bin.java | 1 + src/main/java/htsjdk/samtools/BinList.java | 4 ++++ .../java/htsjdk/samtools/BinaryBAMIndexWriter.java | 3 +++ .../java/htsjdk/samtools/BinningIndexContent.java | 4 ++++ .../java/htsjdk/samtools/CachingBAMFileIndex.java | 4 ++++ src/main/java/htsjdk/samtools/Chunk.java | 2 ++ .../htsjdk/samtools/ComparableSamRecordIterator.java | 1 + .../htsjdk/samtools/CoordinateSortedPairInfoMap.java | 5 +++++ .../htsjdk/samtools/DefaultSAMRecordFactory.java | 2 ++ .../java/htsjdk/samtools/DiskBasedBAMFileIndex.java | 2 ++ .../java/htsjdk/samtools/DuplicateSetIterator.java | 4 ++++ .../htsjdk/samtools/MergingSamRecordIterator.java | 7 +++++++ src/main/java/htsjdk/samtools/QueryInterval.java | 1 + src/main/java/htsjdk/samtools/SAMFileHeader.java | 2 ++ src/main/java/htsjdk/samtools/SAMFileWriter.java | 1 + src/main/java/htsjdk/samtools/SAMFileWriterImpl.java | 4 ++++ src/main/java/htsjdk/samtools/SAMProgramRecord.java | 2 ++ .../java/htsjdk/samtools/SAMReadGroupRecord.java | 2 ++ .../samtools/SAMRecordCoordinateComparator.java | 2 ++ .../samtools/SAMRecordDuplicateComparator.java | 2 ++ .../samtools/SAMRecordQueryNameComparator.java | 2 ++ .../java/htsjdk/samtools/SAMRecordSetBuilder.java | 5 +++++ src/main/java/htsjdk/samtools/SAMSequenceRecord.java | 2 ++ src/main/java/htsjdk/samtools/SAMTextReader.java | 20 ++++++++++++++++++++ src/main/java/htsjdk/samtools/SAMTextWriter.java | 4 ++++ .../java/htsjdk/samtools/SamFileHeaderMerger.java | 3 +++ src/main/java/htsjdk/samtools/SamFileValidator.java | 15 +++++++++++++++ src/main/java/htsjdk/samtools/SamPairUtil.java | 3 +++ src/main/java/htsjdk/samtools/SamReader.java | 6 ++++++ src/main/java/htsjdk/samtools/TextTagCodec.java | 3 +++ .../java/htsjdk/samtools/TextualBAMIndexWriter.java | 3 +++ .../samtools/cram/encoding/ByteArrayLenEncoding.java | 2 ++ .../cram/encoding/ByteArrayStopEncoding.java | 2 ++ .../cram/encoding/ExternalByteArrayEncoding.java | 2 ++ .../samtools/cram/encoding/ExternalByteEncoding.java | 2 ++ .../cram/encoding/ExternalIntegerEncoding.java | 2 ++ .../samtools/cram/encoding/ExternalLongEncoding.java | 2 ++ .../cram/encoding/GolombRiceIntegerCodec.java | 1 + .../samtools/cram/encoding/huffman/HuffmanTree.java | 1 + .../cram/encoding/readfeatures/BaseQualityScore.java | 1 + .../cram/encoding/readfeatures/HardClip.java | 2 ++ .../cram/encoding/readfeatures/InsertBase.java | 2 ++ .../cram/encoding/readfeatures/Insertion.java | 2 ++ .../samtools/cram/encoding/readfeatures/Padding.java | 2 ++ .../cram/encoding/readfeatures/ReadBase.java | 1 + .../samtools/cram/encoding/readfeatures/RefSkip.java | 2 ++ .../cram/encoding/readfeatures/SoftClip.java | 2 ++ .../cram/encoding/readfeatures/Substitution.java | 2 ++ .../htsjdk/samtools/cram/io/CountingInputStream.java | 8 ++++++++ .../samtools/cram/io/DefaultBitInputStream.java | 4 ++++ .../samtools/cram/io/DefaultBitOutputStream.java | 4 ++++ .../htsjdk/samtools/cram/ref/ReferenceSource.java | 1 + src/main/java/htsjdk/samtools/fastq/FastqReader.java | 4 ++++ src/main/java/htsjdk/samtools/fastq/FastqWriter.java | 1 + .../java/htsjdk/samtools/filter/AggregateFilter.java | 2 ++ .../java/htsjdk/samtools/filter/AlignedFilter.java | 2 ++ .../htsjdk/samtools/filter/DuplicateReadFilter.java | 2 ++ .../filter/FailsVendorReadQualityFilter.java | 2 ++ .../htsjdk/samtools/filter/FilteringSamIterator.java | 4 ++++ .../java/htsjdk/samtools/filter/IntervalFilter.java | 2 ++ .../samtools/filter/IntervalKeepPairFilter.java | 2 ++ .../samtools/filter/NotPrimaryAlignmentFilter.java | 2 ++ .../java/htsjdk/samtools/filter/ReadNameFilter.java | 2 ++ .../samtools/filter/SecondaryAlignmentFilter.java | 2 ++ .../filter/SecondaryOrSupplementaryFilter.java | 2 ++ .../htsjdk/samtools/filter/SolexaNoiseFilter.java | 2 ++ src/main/java/htsjdk/samtools/filter/TagFilter.java | 2 ++ .../samtools/filter/WholeReadClippedFilter.java | 1 + .../java/htsjdk/samtools/metrics/StringHeader.java | 1 + .../java/htsjdk/samtools/metrics/VersionHeader.java | 1 + .../reference/AbstractFastaSequenceFile.java | 4 ++++ .../htsjdk/samtools/reference/FastaSequenceFile.java | 3 +++ .../samtools/reference/FastaSequenceIndex.java | 1 + .../samtools/reference/IndexedFastaSequenceFile.java | 6 ++++++ .../samtools/reference/ReferenceSequenceFile.java | 1 + .../reference/ReferenceSequenceFileWalker.java | 1 + .../seekablestream/SeekableBufferedStream.java | 6 ++++++ .../samtools/seekablestream/SeekableFTPStream.java | 4 ++++ .../samtools/seekablestream/SeekableFileStream.java | 7 +++++++ .../samtools/seekablestream/SeekableHTTPStream.java | 7 +++++++ .../samtools/seekablestream/SeekableStream.java | 2 ++ src/main/java/htsjdk/samtools/sra/SRALazyRecord.java | 1 + .../htsjdk/samtools/util/AbstractAsyncWriter.java | 2 ++ .../java/htsjdk/samtools/util/AbstractLocusInfo.java | 2 ++ .../htsjdk/samtools/util/AbstractLocusIterator.java | 5 +++++ src/main/java/htsjdk/samtools/util/AsciiWriter.java | 3 +++ .../util/AsyncBlockCompressedInputStream.java | 1 + src/main/java/htsjdk/samtools/util/BinaryCodec.java | 1 + .../samtools/util/BlockCompressedInputStream.java | 5 +++++ .../samtools/util/BlockCompressedOutputStream.java | 1 + .../htsjdk/samtools/util/BufferedLineReader.java | 4 ++++ .../java/htsjdk/samtools/util/CloseableIterator.java | 1 + .../htsjdk/samtools/util/DelegatingIterator.java | 4 ++++ .../java/htsjdk/samtools/util/DiskBackedQueue.java | 3 +++ .../htsjdk/samtools/util/EdgingRecordAndOffset.java | 2 ++ .../java/htsjdk/samtools/util/FastLineReader.java | 1 + .../samtools/util/FileAppendStreamLRUCache.java | 2 ++ src/main/java/htsjdk/samtools/util/IOUtil.java | 1 + src/main/java/htsjdk/samtools/util/Interval.java | 1 + src/main/java/htsjdk/samtools/util/IntervalList.java | 2 ++ .../util/IntervalListReferenceSequenceMask.java | 4 ++++ src/main/java/htsjdk/samtools/util/IntervalTree.java | 13 +++++++++++++ .../java/htsjdk/samtools/util/IntervalTreeMap.java | 19 +++++++++++++++++++ src/main/java/htsjdk/samtools/util/Iso8601Date.java | 1 + src/main/java/htsjdk/samtools/util/LineReader.java | 1 + .../java/htsjdk/samtools/util/LocusComparator.java | 1 + src/main/java/htsjdk/samtools/util/LocusImpl.java | 2 ++ .../samtools/util/Md5CalculatingInputStream.java | 9 +++++++++ .../samtools/util/Md5CalculatingOutputStream.java | 5 +++++ src/main/java/htsjdk/samtools/util/PeekIterator.java | 3 +++ .../java/htsjdk/samtools/util/PeekableIterator.java | 4 ++++ .../htsjdk/samtools/util/PositionalOutputStream.java | 4 ++++ .../samtools/util/QualityEncodingDetector.java | 3 +++ .../util/SamRecordIntervalIteratorFactory.java | 4 ++++ .../java/htsjdk/samtools/util/SortingCollection.java | 14 ++++++++++++++ .../htsjdk/samtools/util/SortingLongCollection.java | 1 + .../java/htsjdk/samtools/util/StringLineReader.java | 4 ++++ .../util/WholeGenomeReferenceSequenceMask.java | 4 ++++ src/main/java/htsjdk/tribble/FeatureReader.java | 1 + src/main/java/htsjdk/tribble/SimpleFeature.java | 3 +++ src/main/java/htsjdk/tribble/TribbleException.java | 1 + src/main/java/htsjdk/tribble/bed/FullBEDFeature.java | 1 + .../java/htsjdk/tribble/bed/SimpleBEDFeature.java | 10 ++++++++++ .../java/htsjdk/tribble/index/AbstractIndex.java | 9 +++++++++ .../htsjdk/tribble/index/DynamicIndexCreator.java | 2 ++ src/main/java/htsjdk/tribble/index/IndexFactory.java | 3 +++ .../java/htsjdk/tribble/index/interval/Interval.java | 1 + .../tribble/index/interval/IntervalIndexCreator.java | 2 ++ .../tribble/index/interval/IntervalTreeIndex.java | 6 ++++++ .../htsjdk/tribble/index/linear/LinearIndex.java | 7 +++++++ .../tribble/index/linear/LinearIndexCreator.java | 2 ++ .../java/htsjdk/tribble/readers/AsciiLineReader.java | 2 ++ src/main/java/htsjdk/tribble/readers/LineReader.java | 1 + .../tribble/readers/LongLineBufferedReader.java | 8 ++++++++ .../tribble/readers/PositionalBufferedStream.java | 3 +++ .../tribble/readers/TabixIteratorLineReader.java | 2 ++ .../java/htsjdk/tribble/readers/TabixReader.java | 1 + src/main/java/htsjdk/tribble/util/HTTPHelper.java | 5 +++++ .../tribble/util/LittleEndianOutputStream.java | 2 ++ src/main/java/htsjdk/tribble/util/TabixUtils.java | 1 + .../java/htsjdk/variant/variantcontext/Allele.java | 1 + .../htsjdk/variant/variantcontext/FastGenotype.java | 1 + .../java/htsjdk/variant/variantcontext/JEXLMap.java | 12 ++++++++++++ .../variant/variantcontext/VariantContext.java | 2 ++ .../variant/variantcontext/VariantJEXLContext.java | 3 +++ .../writer/AsyncVariantContextWriter.java | 2 ++ .../variantcontext/writer/BCF2FieldWriter.java | 1 + .../writer/IndexingVariantContextWriter.java | 3 +++ .../writer/SortingVariantContextWriter.java | 1 + .../writer/SortingVariantContextWriterBase.java | 1 + .../variantcontext/writer/VariantContextWriter.java | 1 + .../java/htsjdk/variant/vcf/AbstractVCFCodec.java | 3 +++ src/main/java/htsjdk/variant/vcf/VCF3Codec.java | 2 ++ src/main/java/htsjdk/variant/vcf/VCFCodec.java | 1 + .../htsjdk/variant/vcf/VCFCompoundHeaderLine.java | 2 ++ src/main/java/htsjdk/variant/vcf/VCFFileReader.java | 6 ++++-- src/main/java/htsjdk/variant/vcf/VCFHeaderLine.java | 1 + .../htsjdk/variant/vcf/VCFHeaderLineTranslator.java | 2 ++ .../java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java | 2 ++ .../MergingSamRecordIteratorGroupCollisionTest.java | 16 ++++++++++++++++ .../java/htsjdk/samtools/SamReaderFactoryTest.java | 3 +++ src/test/java/htsjdk/samtools/SamReaderTest.java | 2 ++ .../java/htsjdk/samtools/ValidateSamFileTest.java | 6 ++++++ .../util/BlockCompressedOutputStreamTest.java | 1 + .../htsjdk/samtools/util/DiskBackedQueueTest.java | 3 +++ .../htsjdk/samtools/util/SortingCollectionTest.java | 10 ++++++++++ 175 files changed, 583 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java b/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java index 6bf28ef29..724e73c62 100644 --- a/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java +++ b/src/main/java/htsjdk/samtools/AbstractBAMFileIndex.java @@ -88,6 +88,7 @@ protected AbstractBAMFileIndex(final File file, final SAMSequenceDictionary dict /** * Close this index and release any associated resources. */ + @Override public void close() { mIndexBuffer.close(); } @@ -170,6 +171,7 @@ public int getNumberOfReferences() { * @return The file offset of the first record in the last linear bin, or -1 * if there are no elements in linear bins (i.e. no mapped reads). */ + @Override public long getStartOfLastLinearBin() { seek(4); @@ -206,6 +208,7 @@ public long getStartOfLastLinearBin() { * @param reference the reference of interest * @return meta data for the reference */ + @Override public BAMIndexMetaData getMetaData(final int reference) { seek(4); diff --git a/src/main/java/htsjdk/samtools/AsyncSAMFileWriter.java b/src/main/java/htsjdk/samtools/AsyncSAMFileWriter.java index ab5b8d0b1..1a860f29b 100644 --- a/src/main/java/htsjdk/samtools/AsyncSAMFileWriter.java +++ b/src/main/java/htsjdk/samtools/AsyncSAMFileWriter.java @@ -48,11 +48,13 @@ public void setProgressLogger(final ProgressLoggerInterface progress) { * Adds an alignment to the queue to be written. Will re-throw any exception that was received when * writing prior record(s) to the underlying SAMFileWriter. */ + @Override public void addAlignment(final SAMRecord alignment) { write(alignment); } /** Returns the SAMFileHeader from the underlying SAMFileWriter. */ + @Override public SAMFileHeader getFileHeader() { return this.underlyingWriter.getFileHeader(); } diff --git a/src/main/java/htsjdk/samtools/BAMFileReader.java b/src/main/java/htsjdk/samtools/BAMFileReader.java index 3026eaeda..1d9110ad6 100644 --- a/src/main/java/htsjdk/samtools/BAMFileReader.java +++ b/src/main/java/htsjdk/samtools/BAMFileReader.java @@ -341,6 +341,7 @@ static long findVirtualOffsetOfFirstRecord(final File bam) throws IOException { * If true, writes the source of every read into the source SAMRecords. * @param enabled true to write source information into each SAMRecord. */ + @Override void enableFileSource(final SamReader reader, final boolean enabled) { this.mReader = enabled ? reader : null; } @@ -349,6 +350,7 @@ void enableFileSource(final SamReader reader, final boolean enabled) { * If true, uses the caching version of the index reader. * @param enabled true to use the caching version of the reader. */ + @Override protected void enableIndexCaching(final boolean enabled) { if(mIndex != null) throw new SAMException("Unable to turn on index caching; index file has already been loaded."); @@ -360,6 +362,7 @@ protected void enableIndexCaching(final boolean enabled) { * This is slower but more scalable when accessing large numbers of BAM files sequentially. * @param enabled True to use memory mapping, false to use regular I/O. */ + @Override protected void enableIndexMemoryMapping(final boolean enabled) { if (mIndex != null) { throw new SAMException("Unable to change index memory mapping; index file has already been loaded."); @@ -381,6 +384,7 @@ protected void enableIndexMemoryMapping(final boolean enabled) { /** * @return true if ths is a BAM file, and has an index */ + @Override public boolean hasIndex() { return mIsSeekable && ((mIndexFile != null) || (mIndexStream != null)); } @@ -389,6 +393,7 @@ public boolean hasIndex() { * Retrieves the index for the given file type. Ensure that the index is of the specified type. * @return An index of the given type. */ + @Override public BAMIndex getIndex() { if(!hasIndex()) throw new SAMException("No index is available for this BAM file."); @@ -425,6 +430,7 @@ public void close() { mIndex = null; } + @Override public SAMFileHeader getFileHeader() { return mFileHeader; } @@ -432,10 +438,12 @@ public SAMFileHeader getFileHeader() { /** * Set error-checking level for subsequent SAMRecord reads. */ + @Override void setValidationStringency(final ValidationStringency validationStringency) { this.mValidationStringency = validationStringency; } + @Override public ValidationStringency getValidationStringency() { return this.mValidationStringency; } @@ -448,6 +456,7 @@ public ValidationStringency getValidationStringency() { * getIterator() begins its iteration where the last one left off. That is the best that can be * done in that situation. */ + @Override public CloseableIterator getIterator() { if (mStream == null) { throw new IllegalStateException("File reader is closed"); @@ -552,6 +561,7 @@ public SAMFileSpan getFilePointerSpanningReads() { * @return Iterator for the matching SAMRecords * @see QueryInterval#optimizeIntervals(QueryInterval[]) */ + @Override public CloseableIterator query(final QueryInterval[] intervals, final boolean contained) { if (mStream == null) { throw new IllegalStateException("File reader is closed"); @@ -582,6 +592,7 @@ public SAMFileSpan getFilePointerSpanningReads() { * @param start Alignment start sought. * @return Iterator for the matching SAMRecords. */ + @Override public CloseableIterator queryAlignmentStart(final String sequence, final int start) { if (mStream == null) { throw new IllegalStateException("File reader is closed"); @@ -608,6 +619,7 @@ public SAMFileSpan getFilePointerSpanningReads() { * * @return Iterator for the matching SAMRecords. */ + @Override public CloseableIterator queryUnmapped() { if (mStream == null) { throw new IllegalStateException("File reader is closed"); @@ -710,6 +722,7 @@ private static SAMSequenceRecord readSequenceRecord(final BinaryCodec stream, fi private boolean isClosed = false; + @Override public void close() { if (!isClosed) { if (mCurrentIterator != null && this != mCurrentIterator) { @@ -724,6 +737,7 @@ protected void assertOpen() { if (isClosed) throw new AssertionError("Iterator has been closed"); } + @Override public void remove() { throw new UnsupportedOperationException("Not supported: remove"); } @@ -770,11 +784,13 @@ public SAMRecord next() { } } + @Override public boolean hasNext() { assertOpen(); return (mNextRecord != null); } + @Override public SAMRecord next() { assertOpen(); final SAMRecord result = mNextRecord; @@ -947,6 +963,7 @@ public static BAMFileSpan getFileSpan(QueryInterval[] intervals, BAMIndex fileIn advance(); } + @Override SAMRecord getNextRecord() throws IOException { // Advance to next file block if necessary @@ -989,6 +1006,7 @@ public BAMQueryFilteringIterator(final CloseableIterator iterator, /** * Returns true if a next element exists; false otherwise. */ + @Override public boolean hasNext() { assertOpen(); return mNextRecord != null; @@ -998,6 +1016,7 @@ public boolean hasNext() { * Gets the next record from the given iterator. * @return The next SAM record in the iterator. */ + @Override public SAMRecord next() { if(!hasNext()) throw new NoSuchElementException("BAMQueryFilteringIterator: no next element available"); diff --git a/src/main/java/htsjdk/samtools/BAMFileSpan.java b/src/main/java/htsjdk/samtools/BAMFileSpan.java index 485f69dcf..d99760d2a 100644 --- a/src/main/java/htsjdk/samtools/BAMFileSpan.java +++ b/src/main/java/htsjdk/samtools/BAMFileSpan.java @@ -78,6 +78,7 @@ public BAMFileSpan(final List chunks) { * Does this chunk list map to any position within the BAM file? * @return True iff the ChunkList points to any data within the BAM. */ + @Override public boolean isEmpty() { return chunks.isEmpty(); } @@ -86,6 +87,7 @@ public boolean isEmpty() { * Deep clone the given chunk list. * @return A copy of the chunk list. */ + @Override public BAMFileSpan clone() { final BAMFileSpan clone = new BAMFileSpan(); for(final Chunk chunk: chunks) @@ -100,6 +102,7 @@ public BAMFileSpan clone() { * @param fileSpan The filespan before which to eliminate. * @return A new BAMFileSpan which contains the portion of the chunk list after the given chunk. */ + @Override public SAMFileSpan removeContentsBefore(final SAMFileSpan fileSpan) { if(fileSpan == null) return clone(); @@ -174,6 +177,7 @@ public SAMFileSpan removeContentsAfter(final SAMFileSpan fileSpan) { * Gets a file span over the data immediately following this span. * @return The a pointer to data immediately following this span. */ + @Override public SAMFileSpan getContentsFollowing() { if(chunks.isEmpty()) throw new SAMException("Unable to get the file pointer following this one: no data present."); diff --git a/src/main/java/htsjdk/samtools/BAMFileWriter.java b/src/main/java/htsjdk/samtools/BAMFileWriter.java index f6a474e2d..fc766ae7d 100644 --- a/src/main/java/htsjdk/samtools/BAMFileWriter.java +++ b/src/main/java/htsjdk/samtools/BAMFileWriter.java @@ -115,6 +115,7 @@ private BAMIndexer createBamIndex(final String path) { } } + @Override protected void writeAlignment(final SAMRecord alignment) { prepareToWriteAlignments(); @@ -135,10 +136,12 @@ protected void writeAlignment(final SAMRecord alignment) { } } + @Override protected void writeHeader(final String textHeader) { writeHeader(outputBinaryCodec, getFileHeader(), textHeader); } + @Override protected void finish() { outputBinaryCodec.close(); try { @@ -151,6 +154,7 @@ protected void finish() { } /** @return absolute path, or null if this writer does not correspond to a file. */ + @Override protected String getFilename() { return outputBinaryCodec.getOutputFileName(); } diff --git a/src/main/java/htsjdk/samtools/BAMIndex.java b/src/main/java/htsjdk/samtools/BAMIndex.java index 3663df9d0..62c69c79c 100644 --- a/src/main/java/htsjdk/samtools/BAMIndex.java +++ b/src/main/java/htsjdk/samtools/BAMIndex.java @@ -63,5 +63,6 @@ /** * Close the index and release any associated resources. */ + @Override void close(); } diff --git a/src/main/java/htsjdk/samtools/BAMIndexWriter.java b/src/main/java/htsjdk/samtools/BAMIndexWriter.java index b036b684d..aafcb5fbf 100644 --- a/src/main/java/htsjdk/samtools/BAMIndexWriter.java +++ b/src/main/java/htsjdk/samtools/BAMIndexWriter.java @@ -49,6 +49,7 @@ /** * Any necessary processing at the end of the file */ + @Override public void close(); } \ No newline at end of file diff --git a/src/main/java/htsjdk/samtools/BAMRecord.java b/src/main/java/htsjdk/samtools/BAMRecord.java index c45566f08..672e802c3 100644 --- a/src/main/java/htsjdk/samtools/BAMRecord.java +++ b/src/main/java/htsjdk/samtools/BAMRecord.java @@ -113,6 +113,7 @@ protected BAMRecord(final SAMFileHeader header, /** * Force all the lazily-initialized attributes to be decoded. */ + @Override protected void eagerDecode() { getReadName(); getCigar(); diff --git a/src/main/java/htsjdk/samtools/BAMRecordCodec.java b/src/main/java/htsjdk/samtools/BAMRecordCodec.java index dc1ca8196..5b0a408f6 100644 --- a/src/main/java/htsjdk/samtools/BAMRecordCodec.java +++ b/src/main/java/htsjdk/samtools/BAMRecordCodec.java @@ -49,6 +49,7 @@ public BAMRecordCodec(final SAMFileHeader header, final SAMRecordFactory factory this.samRecordFactory = factory; } + @Override public BAMRecordCodec clone() { // Do not clone the references to codecs, as they must be distinct for each instance. return new BAMRecordCodec(this.header, this.samRecordFactory); @@ -56,6 +57,7 @@ public BAMRecordCodec clone() { /** Sets the output stream that records will be written to. */ + @Override public void setOutputStream(final OutputStream os) { this.binaryCodec.setOutputStream(os); } @@ -67,6 +69,7 @@ public void setOutputStream(final OutputStream os, final String filename) { } /** Sets the input stream that records will be read from. */ + @Override public void setInputStream(final InputStream is) { this.binaryCodec.setInputStream(is); } @@ -85,6 +88,7 @@ public void setInputStream(final InputStream is, final String filename) { * * @param alignment Record to be written. */ + @Override public void encode(final SAMRecord alignment) { // Compute block size, as it is the first element of the file representation of SAMRecord final int readLength = alignment.getReadLength(); @@ -171,6 +175,7 @@ public void encode(final SAMRecord alignment) { * @return null if no more records. Should throw exception if EOF is encountered in the middle of * a record. */ + @Override public SAMRecord decode() { int recordLength = 0; try { diff --git a/src/main/java/htsjdk/samtools/Bin.java b/src/main/java/htsjdk/samtools/Bin.java index 1ac572400..f199d0a87 100644 --- a/src/main/java/htsjdk/samtools/Bin.java +++ b/src/main/java/htsjdk/samtools/Bin.java @@ -105,6 +105,7 @@ public boolean containsChunks() { * @param other Other bin to which this bin should be compared. * @return -1 if this < other, 0 if this == other, 1 if this > other. */ + @Override public int compareTo(final Bin other) { if(other == null) throw new ClassCastException("Cannot compare to a null object"); diff --git a/src/main/java/htsjdk/samtools/BinList.java b/src/main/java/htsjdk/samtools/BinList.java index e7107d44f..2111ba403 100644 --- a/src/main/java/htsjdk/samtools/BinList.java +++ b/src/main/java/htsjdk/samtools/BinList.java @@ -60,6 +60,7 @@ protected BinList(final int referenceSequence, final BitSet bins) { * Gets an iterator over all selected bins. * @return An iterator over all selected bins. */ + @Override public Iterator iterator() { return new BinIterator(); } @@ -95,6 +96,7 @@ public BinIterator() { * Are there more bins in this set, waiting to be returned? * @return True if more bins are remaining. */ + @Override public boolean hasNext() { return nextBin >= 0; } @@ -103,6 +105,7 @@ public boolean hasNext() { * Gets the next bin in the provided BinList. * @return the next available bin in the BinList. */ + @Override public Bin next() { if(!hasNext()) throw new NoSuchElementException("This BinIterator is currently empty"); @@ -111,6 +114,7 @@ public Bin next() { return new Bin(referenceSequence,currentBin); } + @Override public void remove() { throw new UnsupportedOperationException("Unable to remove from a bin iterator"); } diff --git a/src/main/java/htsjdk/samtools/BinaryBAMIndexWriter.java b/src/main/java/htsjdk/samtools/BinaryBAMIndexWriter.java index 35a22f7ac..5719aecf5 100644 --- a/src/main/java/htsjdk/samtools/BinaryBAMIndexWriter.java +++ b/src/main/java/htsjdk/samtools/BinaryBAMIndexWriter.java @@ -78,6 +78,7 @@ public BinaryBAMIndexWriter(final int nRef, final OutputStream output) { /** * Write this content as binary output */ + @Override public void writeReference(final BAMIndexContent content) { if (content == null) { @@ -147,6 +148,7 @@ public void writeReference(final BAMIndexContent content) { * * @param count */ + @Override public void writeNoCoordinateRecordCount(final Long count) { codec.writeLong(count == null ? 0 : count); } @@ -154,6 +156,7 @@ public void writeNoCoordinateRecordCount(final Long count) { /** * Any necessary processing at the end of the file */ + @Override public void close() { codec.close(); } diff --git a/src/main/java/htsjdk/samtools/BinningIndexContent.java b/src/main/java/htsjdk/samtools/BinningIndexContent.java index 9e32601c2..124353e27 100644 --- a/src/main/java/htsjdk/samtools/BinningIndexContent.java +++ b/src/main/java/htsjdk/samtools/BinningIndexContent.java @@ -171,6 +171,7 @@ int getNumberOfNonNullBins() { /** * @return An iterator over all non-empty bins. */ + @Override public Iterator iterator() { return new BinIterator(); } @@ -190,6 +191,7 @@ public BinIterator() { * * @return True if more bins are remaining. */ + @Override public boolean hasNext() { while (nextBin <= maxBinNumber) { if (getBin(nextBin) != null) return true; @@ -203,6 +205,7 @@ public boolean hasNext() { * * @return the next available bin in the BinList. */ + @Override public Bin next() { if (!hasNext()) throw new NoSuchElementException("This BinIterator is currently empty"); @@ -211,6 +214,7 @@ public Bin next() { return result; } + @Override public void remove() { throw new UnsupportedOperationException("Unable to remove from a bin iterator"); } diff --git a/src/main/java/htsjdk/samtools/CachingBAMFileIndex.java b/src/main/java/htsjdk/samtools/CachingBAMFileIndex.java index 8010ce59e..5597832c2 100644 --- a/src/main/java/htsjdk/samtools/CachingBAMFileIndex.java +++ b/src/main/java/htsjdk/samtools/CachingBAMFileIndex.java @@ -61,6 +61,7 @@ public CachingBAMFileIndex(final File file, final SAMSequenceDictionary dictiona * in a range that can be scanned to find SAMRecords that overlap the given positions. * May return null if there is no content overlapping the region. */ + @Override public BAMFileSpan getSpanOverlapping(final int referenceIndex, final int startPos, final int endPos) { final BAMIndexContent queryResults = getQueryResults(referenceIndex); @@ -80,6 +81,7 @@ public BAMFileSpan getSpanOverlapping(final int referenceIndex, final int startP * @param endPos 1-based end of the desired interval, inclusive * @return a list of bins that contain relevant data. */ + @Override public BinList getBinsOverlapping(final int referenceIndex, final int startPos, final int endPos) { final BitSet regionBins = GenomicIndexUtil.regionToBins(startPos, endPos); if (regionBins == null) { @@ -93,6 +95,7 @@ public BinList getBinsOverlapping(final int referenceIndex, final int startPos, * @param bin The bin over which to perform an overlapping query. * @return The file pointers */ + @Override public BAMFileSpan getSpanOverlapping(final Bin bin) { if(bin == null) return null; @@ -138,6 +141,7 @@ public BAMFileSpan getSpanOverlapping(final Bin bin) { * @param referenceIndex The reference to load. CachingBAMFileIndex only stores index data for entire references. * @return The index information for this reference. */ + @Override protected BAMIndexContent getQueryResults(final int referenceIndex) { // WeakHashMap is a bit weird in that its lookups are done via equals() equality, but expirations must be // handled via == equality. This implementation jumps through a few hoops to make sure that == equality still diff --git a/src/main/java/htsjdk/samtools/Chunk.java b/src/main/java/htsjdk/samtools/Chunk.java index 0d77b0cd3..dbe27c64d 100644 --- a/src/main/java/htsjdk/samtools/Chunk.java +++ b/src/main/java/htsjdk/samtools/Chunk.java @@ -38,6 +38,7 @@ public Chunk(final long start, final long end) { mChunkEnd = end; } + @Override public Chunk clone() { return new Chunk(mChunkStart,mChunkEnd); } @@ -58,6 +59,7 @@ protected void setChunkEnd(final long value) { mChunkEnd = value; } + @Override public int compareTo(final Chunk chunk) { int result = Long.signum(mChunkStart - chunk.mChunkStart); if (result == 0) { diff --git a/src/main/java/htsjdk/samtools/ComparableSamRecordIterator.java b/src/main/java/htsjdk/samtools/ComparableSamRecordIterator.java index 06186a1d0..cb2da892c 100644 --- a/src/main/java/htsjdk/samtools/ComparableSamRecordIterator.java +++ b/src/main/java/htsjdk/samtools/ComparableSamRecordIterator.java @@ -63,6 +63,7 @@ public SamReader getReader() { * @param that another iterator to compare to * @return a negative, 0 or positive number as described in the Comparator interface */ + @Override public int compareTo(final ComparableSamRecordIterator that) { if (this.comparator.getClass() != that.comparator.getClass()) { throw new IllegalStateException("Attempt to compare two ComparableSAMRecordIterators that " + diff --git a/src/main/java/htsjdk/samtools/CoordinateSortedPairInfoMap.java b/src/main/java/htsjdk/samtools/CoordinateSortedPairInfoMap.java index d892d655a..37c200cc5 100644 --- a/src/main/java/htsjdk/samtools/CoordinateSortedPairInfoMap.java +++ b/src/main/java/htsjdk/samtools/CoordinateSortedPairInfoMap.java @@ -202,6 +202,7 @@ public int sizeInRam() { * or removed from map when iteration is in progress, nor may a second iteration be started. * Iterator must be closed in order to allow normal access to the map. */ + @Override public CloseableIterator> iterator() { if (iterationInProgress) throw new IllegalStateException("Cannot be called when iteration is in progress"); iterationInProgress = true; @@ -238,11 +239,13 @@ private void createIteratorForMapInRam() { currentReferenceIterator = mapInRam.entrySet().iterator(); } + @Override public void close() { closed = true; iterationInProgress = false; } + @Override public boolean hasNext() { if (closed) throw new IllegalStateException("Iterator has been closed"); if (currentReferenceIterator != null && !currentReferenceIterator.hasNext()) @@ -250,6 +253,7 @@ public boolean hasNext() { return currentReferenceIterator != null; } + @Override public Map.Entry next() { if (closed) throw new IllegalStateException("Iterator has been closed"); if (!hasNext()) throw new NoSuchElementException(); @@ -258,6 +262,7 @@ public boolean hasNext() { return ret; } + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java b/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java index 7e3848e3d..707cc6ec1 100644 --- a/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java +++ b/src/main/java/htsjdk/samtools/DefaultSAMRecordFactory.java @@ -14,6 +14,7 @@ public static DefaultSAMRecordFactory getInstance() { } /** Create a new SAMRecord to be filled in */ + @Override public SAMRecord createSAMRecord(final SAMFileHeader header) { return new SAMRecord(header); } @@ -23,6 +24,7 @@ public SAMRecord createSAMRecord(final SAMFileHeader header) { * any value other than NO_ALIGNMENT_REFERENCE_INDEX, the values must be resolvable against the sequence * dictionary in the header argument. */ + @Override public BAMRecord createBAMRecord (final SAMFileHeader header, final int referenceSequenceIndex, final int alignmentStart, diff --git a/src/main/java/htsjdk/samtools/DiskBasedBAMFileIndex.java b/src/main/java/htsjdk/samtools/DiskBasedBAMFileIndex.java index b5d6f597a..1eddddde3 100644 --- a/src/main/java/htsjdk/samtools/DiskBasedBAMFileIndex.java +++ b/src/main/java/htsjdk/samtools/DiskBasedBAMFileIndex.java @@ -56,6 +56,7 @@ public DiskBasedBAMFileIndex(final File file, final SAMSequenceDictionary dictio * positions. The last position in each pair is a virtual file pointer to the first SAMRecord beyond * the range that may contain the indicated SAMRecords. */ + @Override public BAMFileSpan getSpanOverlapping(final int referenceIndex, final int startPos, final int endPos) { final BAMIndexContent queryResults = query(referenceIndex,startPos,endPos); @@ -69,6 +70,7 @@ public BAMFileSpan getSpanOverlapping(final int referenceIndex, final int startP return new BAMFileSpan(chunkList); } + @Override protected BAMIndexContent getQueryResults(final int reference){ throw new UnsupportedOperationException(); // todo: there ought to be a way to support this using the first startPos for the reference and the last diff --git a/src/main/java/htsjdk/samtools/DuplicateSetIterator.java b/src/main/java/htsjdk/samtools/DuplicateSetIterator.java index 9a0c6f108..f3b9b072a 100644 --- a/src/main/java/htsjdk/samtools/DuplicateSetIterator.java +++ b/src/main/java/htsjdk/samtools/DuplicateSetIterator.java @@ -120,6 +120,7 @@ public void setScoringStrategy(final DuplicateScoringStrategy.ScoringStrategy sc this.comparator.setScoringStrategy(scoringStrategy); } + @Override public DuplicateSet next() { DuplicateSet duplicateSet = null; @@ -161,12 +162,15 @@ public DuplicateSet next() { return duplicateSet; } + @Override public void close() { wrappedIterator.close(); } + @Override public boolean hasNext() { return (!duplicateSet.isEmpty() || wrappedIterator.hasNext()); } // Does nothing! + @Override public void remove() { } } diff --git a/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java b/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java index a294752de..45d002e3e 100644 --- a/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java +++ b/src/main/java/htsjdk/samtools/MergingSamRecordIterator.java @@ -107,6 +107,7 @@ private void startIterationIfRequired() { /** * Close down all open iterators. */ + @Override public void close() { // Iterators not in the priority queue have already been closed; only close down the iterators that are still in the priority queue. for (CloseableIterator iterator : pq) @@ -114,12 +115,14 @@ public void close() { } /** Returns true if any of the underlying iterators has more records, otherwise false. */ + @Override public boolean hasNext() { startIterationIfRequired(); return !this.pq.isEmpty(); } /** Returns the next record from the top most iterator during merging. */ + @Override public SAMRecord next() { startIterationIfRequired(); @@ -163,6 +166,7 @@ private void addIfNotEmpty(final ComparableSamRecordIterator iterator) { } /** Unsupported operation. */ + @Override public void remove() { throw new UnsupportedOperationException("MergingSAMRecorderIterator.remove()"); } @@ -176,10 +180,12 @@ private SAMRecordComparator getComparator() { // For unsorted build a fake comparator that compares based on object ID if (this.sortOrder == SAMFileHeader.SortOrder.unsorted) { return new SAMRecordComparator() { + @Override public int fileOrderCompare(final SAMRecord lhs, final SAMRecord rhs) { return System.identityHashCode(lhs) - System.identityHashCode(rhs); } + @Override public int compare(final SAMRecord lhs, final SAMRecord rhs) { return fileOrderCompare(lhs, rhs); } @@ -206,6 +212,7 @@ public SAMFileHeader getMergedHeader() { private class MergedSequenceDictionaryCoordinateOrderComparator extends SAMRecordCoordinateComparator implements Serializable { private static final long serialVersionUID = 1L; + @Override public int fileOrderCompare(final SAMRecord samRecord1, final SAMRecord samRecord2) { final int referenceIndex1 = getReferenceIndex(samRecord1); final int referenceIndex2 = getReferenceIndex(samRecord2); diff --git a/src/main/java/htsjdk/samtools/QueryInterval.java b/src/main/java/htsjdk/samtools/QueryInterval.java index bdfb52c37..581e0f648 100644 --- a/src/main/java/htsjdk/samtools/QueryInterval.java +++ b/src/main/java/htsjdk/samtools/QueryInterval.java @@ -29,6 +29,7 @@ public QueryInterval(final int referenceIndex, final int start, final int end) { } + @Override public int compareTo(final QueryInterval other) { int comp = this.referenceIndex - other.referenceIndex; if (comp != 0) return comp; diff --git a/src/main/java/htsjdk/samtools/SAMFileHeader.java b/src/main/java/htsjdk/samtools/SAMFileHeader.java index 47543c2a6..41eed4a4c 100644 --- a/src/main/java/htsjdk/samtools/SAMFileHeader.java +++ b/src/main/java/htsjdk/samtools/SAMFileHeader.java @@ -56,6 +56,7 @@ public static final Set STANDARD_TAGS = new HashSet(Arrays.asList(VERSION_TAG, SORT_ORDER_TAG, GROUP_ORDER_TAG)); + @Override Set getStandardTags() { return STANDARD_TAGS; } @@ -353,6 +354,7 @@ public int hashCode() { return result; } + @Override public final SAMFileHeader clone() { final SAMTextHeaderCodec codec = new SAMTextHeaderCodec(); codec.setValidationStringency(ValidationStringency.SILENT); diff --git a/src/main/java/htsjdk/samtools/SAMFileWriter.java b/src/main/java/htsjdk/samtools/SAMFileWriter.java index fe99591f0..24936a0c1 100644 --- a/src/main/java/htsjdk/samtools/SAMFileWriter.java +++ b/src/main/java/htsjdk/samtools/SAMFileWriter.java @@ -46,5 +46,6 @@ /** * Must be called to flush or file will likely be defective. */ + @Override void close(); } diff --git a/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java b/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java index 130ecea4a..5e0ecdb45 100644 --- a/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java +++ b/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java @@ -75,6 +75,7 @@ public static int getDefaultMaxRecordsInRam() { * Sets the progress logger used by this implementation. Setting this lets this writer emit log * messages as SAM records in a SortingCollection are being written to disk. */ + @Override public void setProgressLogger(final ProgressLoggerInterface progress) { this.progressLogger = progress; } @@ -153,6 +154,7 @@ public void setHeader(final SAMFileHeader header) } } + @Override public SAMFileHeader getFileHeader() { return header; } @@ -180,6 +182,7 @@ private SAMRecordComparator makeComparator() { * @throws IllegalArgumentException if the record's reference or mate reference indices cannot be * resolved against the writer's header using the current reference and mate reference names */ + @Override public void addAlignment(final SAMRecord alignment) { alignment.setHeaderStrict(header); // re-establish the record header and resolve reference indices @@ -206,6 +209,7 @@ private void assertPresorted(final SAMRecord alignment) { /** * Must be called or else file will likely be defective. */ + @Override public final void close() { if (!isClosed) { diff --git a/src/main/java/htsjdk/samtools/SAMProgramRecord.java b/src/main/java/htsjdk/samtools/SAMProgramRecord.java index 3bbecf90d..91d0dac44 100644 --- a/src/main/java/htsjdk/samtools/SAMProgramRecord.java +++ b/src/main/java/htsjdk/samtools/SAMProgramRecord.java @@ -57,6 +57,7 @@ public SAMProgramRecord(final String id, SAMProgramRecord srcProgramRecord) { } } + @Override public String getId() { return getProgramGroupId(); } @@ -126,6 +127,7 @@ public int hashCode() { return result; } + @Override Set getStandardTags() { return STANDARD_TAGS; } diff --git a/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java b/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java index fd81852a0..bae3c4f62 100644 --- a/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java +++ b/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java @@ -74,6 +74,7 @@ public SAMReadGroupRecord(final String id, final SAMReadGroupRecord srcProgramRe } } + @Override public String getId() { return getReadGroupId(); } public String getReadGroupId() { return mReadGroupId; } @@ -158,6 +159,7 @@ public int hashCode() { return mReadGroupId.hashCode(); } + @Override Set getStandardTags() { return STANDARD_TAGS; } diff --git a/src/main/java/htsjdk/samtools/SAMRecordCoordinateComparator.java b/src/main/java/htsjdk/samtools/SAMRecordCoordinateComparator.java index e8887bc46..fe054b40b 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordCoordinateComparator.java +++ b/src/main/java/htsjdk/samtools/SAMRecordCoordinateComparator.java @@ -43,6 +43,7 @@ public class SAMRecordCoordinateComparator implements SAMRecordComparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(final SAMRecord samRecord1, final SAMRecord samRecord2) { int cmp = fileOrderCompare(samRecord1, samRecord2); if (cmp != 0) { @@ -83,6 +84,7 @@ private int compareInts(int i1, int i2) { * * @return negative if samRecord1 < samRecord2, 0 if equal, else positive */ + @Override public int fileOrderCompare(final SAMRecord samRecord1, final SAMRecord samRecord2) { if (null == samRecord1.getHeader() || null == samRecord2.getHeader()) { diff --git a/src/main/java/htsjdk/samtools/SAMRecordDuplicateComparator.java b/src/main/java/htsjdk/samtools/SAMRecordDuplicateComparator.java index 4ed2bb52d..436ba3c0a 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordDuplicateComparator.java +++ b/src/main/java/htsjdk/samtools/SAMRecordDuplicateComparator.java @@ -220,6 +220,7 @@ private boolean pairedEndAndBothMapped(final SAMRecord record) { * If both reads are paired and both ends mapped, always prefer the first end over the second end. This is needed to * properly choose the first end for optical duplicate identification when both ends are mapped to the same position etc. */ + @Override public int compare(final SAMRecord samRecord1, final SAMRecord samRecord2) { populateTransientAttributes(samRecord1, samRecord2); int cmp; @@ -357,6 +358,7 @@ public int duplicateSetCompare(final SAMRecord samRecord1, final SAMRecord samRe /** * Less stringent than duplicateSetCompare, such that two records are equal enough such that their ordering in a sorted SAM file would be arbitrary. */ + @Override public int fileOrderCompare(final SAMRecord samRecord1, final SAMRecord samRecord2) { return fileOrderCompare(samRecord1, samRecord2, false, true); } diff --git a/src/main/java/htsjdk/samtools/SAMRecordQueryNameComparator.java b/src/main/java/htsjdk/samtools/SAMRecordQueryNameComparator.java index 7fd97f5b5..d2f7cdea9 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordQueryNameComparator.java +++ b/src/main/java/htsjdk/samtools/SAMRecordQueryNameComparator.java @@ -31,6 +31,7 @@ public class SAMRecordQueryNameComparator implements SAMRecordComparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(final SAMRecord samRecord1, final SAMRecord samRecord2) { int cmp = fileOrderCompare(samRecord1, samRecord2); if (cmp != 0) { @@ -75,6 +76,7 @@ public int compare(final SAMRecord samRecord1, final SAMRecord samRecord2) { * * @return negative if samRecord1 < samRecord2, 0 if equal, else positive */ + @Override public int fileOrderCompare(final SAMRecord samRecord1, final SAMRecord samRecord2) { return compareReadNames(samRecord1.getReadName(), samRecord2.getReadName()); } diff --git a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java index 2af91c30f..60aae473a 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java +++ b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java @@ -193,16 +193,21 @@ public void addRecord(final SAMRecord record) { } /** Returns a CloseableIterator over the collection of SAMRecords. */ + @Override public CloseableIterator iterator() { return new CloseableIterator() { private final Iterator iterator = records.iterator(); + @Override public void close() { /** Do nothing. */} + @Override public boolean hasNext() { return this.iterator.hasNext(); } + @Override public SAMRecord next() { return this.iterator.next(); } + @Override public void remove() { this.iterator.remove(); } }; } diff --git a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java index 6bca979cc..8e7ccf295 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java @@ -194,10 +194,12 @@ public int hashCode() { return mSequenceName != null ? mSequenceName.hashCode() : 0; } + @Override Set getStandardTags() { return STANDARD_TAGS; } + @Override public final SAMSequenceRecord clone() { final SAMSequenceRecord ret = new SAMSequenceRecord(this.mSequenceName, this.mSequenceLength); ret.mSequenceIndex = this.mSequenceIndex; diff --git a/src/main/java/htsjdk/samtools/SAMTextReader.java b/src/main/java/htsjdk/samtools/SAMTextReader.java index 3968f1cc9..62f871752 100644 --- a/src/main/java/htsjdk/samtools/SAMTextReader.java +++ b/src/main/java/htsjdk/samtools/SAMTextReader.java @@ -79,22 +79,27 @@ public SAMTextReader(final InputStream stream, final File file, final Validation * * @param enabled true to write source information into each SAMRecord. */ + @Override public void enableFileSource(final SamReader reader, final boolean enabled) { this.mParentReader = enabled ? reader : null; } + @Override void enableIndexCaching(final boolean enabled) { throw new UnsupportedOperationException("Cannot enable index caching for a SAM text reader"); } + @Override void enableIndexMemoryMapping(final boolean enabled) { throw new UnsupportedOperationException("Cannot enable index memory mapping for a SAM text reader"); } + @Override void enableCrcChecking(final boolean enabled) { // Do nothing - this has no meaning for SAM reading } + @Override void setSAMRecordFactory(final SAMRecordFactory factory) { this.samRecordFactory = factory; } @@ -104,14 +109,17 @@ void setSAMRecordFactory(final SAMRecordFactory factory) { return SamReader.Type.SAM_TYPE; } + @Override public boolean hasIndex() { return false; } + @Override public BAMIndex getIndex() { throw new UnsupportedOperationException(); } + @Override public void close() { if (mReader != null) { try { @@ -122,14 +130,17 @@ public void close() { } } + @Override public SAMFileHeader getFileHeader() { return mFileHeader; } + @Override public ValidationStringency getValidationStringency() { return validationStringency; } + @Override public void setValidationStringency(final ValidationStringency stringency) { this.validationStringency = stringency; } @@ -141,6 +152,7 @@ public void setValidationStringency(final ValidationStringency stringency) { * * @return Iterator of SAMRecords in file order. */ + @Override public CloseableIterator getIterator() { if (mReader == null) { throw new IllegalStateException("File reader is closed"); @@ -158,6 +170,7 @@ public void setValidationStringency(final ValidationStringency stringency) { * @param fileSpan The file span. * @return An iterator over the given file span. */ + @Override public CloseableIterator getIterator(final SAMFileSpan fileSpan) { throw new UnsupportedOperationException("Cannot directly iterate over regions within SAM text files."); } @@ -167,6 +180,7 @@ public void setValidationStringency(final ValidationStringency stringency) { * * @return An pointer to the first read in the file. */ + @Override public SAMFileSpan getFilePointerSpanningReads() { throw new UnsupportedOperationException("Cannot retrieve file pointers within SAM text files."); } @@ -186,10 +200,12 @@ public SAMFileSpan getFilePointerSpanningReads() { /** * Unsupported for SAM text files. */ + @Override public CloseableIterator queryAlignmentStart(final String sequence, final int start) { throw new UnsupportedOperationException("Cannot query SAM text files"); } + @Override public CloseableIterator queryUnmapped() { throw new UnsupportedOperationException("Cannot query SAM text files"); } @@ -220,14 +236,17 @@ private RecordIterator() { } } + @Override public void close() { SAMTextReader.this.close(); } + @Override public boolean hasNext() { return mCurrentLine != null; } + @Override public SAMRecord next() { if (!hasNext()) { throw new IllegalStateException("Cannot call next() on exhausted iterator"); @@ -239,6 +258,7 @@ public SAMRecord next() { } } + @Override public void remove() { throw new UnsupportedOperationException("Not supported: remove"); } diff --git a/src/main/java/htsjdk/samtools/SAMTextWriter.java b/src/main/java/htsjdk/samtools/SAMTextWriter.java index 0786d670c..70dd4a229 100644 --- a/src/main/java/htsjdk/samtools/SAMTextWriter.java +++ b/src/main/java/htsjdk/samtools/SAMTextWriter.java @@ -122,6 +122,7 @@ public SAMTextWriter(final OutputStream stream, final SamFlagField samFlagFieldO * * @param alignment SAMRecord. */ + @Override public void writeAlignment(final SAMRecord alignment) { try { out.write(alignment.getReadName()); @@ -188,6 +189,7 @@ static synchronized String getSAMString(final SAMRecord alignment) { * * @param textHeader String containing the text to write. */ + @Override public void writeHeader(final String textHeader) { try { out.write(textHeader); @@ -199,6 +201,7 @@ public void writeHeader(final String textHeader) { /** * Do any required flushing here. */ + @Override public void finish() { try { out.close(); @@ -212,6 +215,7 @@ public void finish() { * * @return Output filename, or null if there isn't one. */ + @Override public String getFilename() { if (file == null) { return null; diff --git a/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java b/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java index b3f588caa..d3cf16ada 100644 --- a/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java +++ b/src/main/java/htsjdk/samtools/SamFileHeaderMerger.java @@ -98,6 +98,7 @@ //HeaderRecordFactory that creates SAMReadGroupRecord instances. private static final HeaderRecordFactory READ_GROUP_RECORD_FACTORY = new HeaderRecordFactory() { + @Override public SAMReadGroupRecord createRecord(final String id, final SAMReadGroupRecord srcReadGroupRecord) { return new SAMReadGroupRecord(id, srcReadGroupRecord); } @@ -105,6 +106,7 @@ public SAMReadGroupRecord createRecord(final String id, final SAMReadGroupRecord //HeaderRecordFactory that creates SAMProgramRecord instances. private static final HeaderRecordFactory PROGRAM_RECORD_FACTORY = new HeaderRecordFactory() { + @Override public SAMProgramRecord createRecord(final String id, final SAMProgramRecord srcProgramRecord) { return new SAMProgramRecord(id, srcProgramRecord); } @@ -112,6 +114,7 @@ public SAMProgramRecord createRecord(final String id, final SAMProgramRecord src //comparator used to sort lists of program group and read group records private static final Comparator RECORD_ID_COMPARATOR = new Comparator() { + @Override public int compare(final AbstractSAMHeaderRecord o1, final AbstractSAMHeaderRecord o2) { return o1.getId().compareTo(o2.getId()); } diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index e40bfe94f..c774b6fd9 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -750,6 +750,7 @@ private void validateMateFields(final PairEndInfo end1, final PairEndInfo end2, PairEndInfo remove(int mateReferenceIndex, String key); + @Override CloseableIterator> iterator(); } @@ -757,14 +758,17 @@ private void validateMateFields(final PairEndInfo end1, final PairEndInfo end2, private final CoordinateSortedPairInfoMap onDiskMap = new CoordinateSortedPairInfoMap(maxTempFiles, new Codec()); + @Override public void put(int mateReferenceIndex, String key, PairEndInfo value) { onDiskMap.put(mateReferenceIndex, key, value); } + @Override public PairEndInfo remove(int mateReferenceIndex, String key) { return onDiskMap.remove(mateReferenceIndex, key); } + @Override public CloseableIterator> iterator() { return onDiskMap.iterator(); } @@ -773,14 +777,17 @@ public PairEndInfo remove(int mateReferenceIndex, String key) { private DataInputStream in; private DataOutputStream out; + @Override public void setOutputStream(final OutputStream os) { this.out = new DataOutputStream(os); } + @Override public void setInputStream(final InputStream is) { this.in = new DataInputStream(is); } + @Override public void encode(final String key, final PairEndInfo record) { try { out.writeUTF(key); @@ -802,6 +809,7 @@ public void encode(final String key, final PairEndInfo record) { } } + @Override public Map.Entry decode() { try { final String key = in.readUTF(); @@ -838,31 +846,38 @@ public void encode(final String key, final PairEndInfo record) { private static class InMemoryPairEndInfoMap implements PairEndInfoMap { private final Map map = new HashMap(); + @Override public void put(int mateReferenceIndex, String key, PairEndInfo value) { if (mateReferenceIndex != value.mateReferenceIndex) throw new IllegalArgumentException("mateReferenceIndex does not agree with PairEndInfo"); map.put(key, value); } + @Override public PairEndInfo remove(int mateReferenceIndex, String key) { return map.remove(key); } + @Override public CloseableIterator> iterator() { final Iterator> it = map.entrySet().iterator(); return new CloseableIterator>() { + @Override public void close() { // do nothing } + @Override public boolean hasNext() { return it.hasNext(); } + @Override public Map.Entry next() { return it.next(); } + @Override public void remove() { it.remove(); } diff --git a/src/main/java/htsjdk/samtools/SamPairUtil.java b/src/main/java/htsjdk/samtools/SamPairUtil.java index ee1707bd5..4849850ec 100644 --- a/src/main/java/htsjdk/samtools/SamPairUtil.java +++ b/src/main/java/htsjdk/samtools/SamPairUtil.java @@ -424,6 +424,7 @@ public SetMateInfoIterator(final Iterator iterator, final boolean set */ public long getNumMateCigarsAdded() { return this.numMateCigarsAdded; } + @Override public boolean hasNext() { return (!records.isEmpty() || super.hasNext()); } @@ -495,12 +496,14 @@ private void advance() { } } + @Override public SAMRecord next() { advance(); if (records.isEmpty()) throw new IllegalStateException("Unexpectedly found an empty record list"); return this.records.poll(); } + @Override public SAMRecord peek() { advance(); if (records.isEmpty()) throw new IllegalStateException("Unexpectedly found an empty record list"); diff --git a/src/main/java/htsjdk/samtools/SamReader.java b/src/main/java/htsjdk/samtools/SamReader.java index 0c551a045..08f93ec17 100644 --- a/src/main/java/htsjdk/samtools/SamReader.java +++ b/src/main/java/htsjdk/samtools/SamReader.java @@ -164,6 +164,7 @@ public String toString() { * Only a single open iterator on a SAM or BAM file may be extant at any one time. If you want to start * a second iteration, the first one must be closed first. */ + @Override public SAMRecordIterator iterator(); /** @@ -558,6 +559,7 @@ public AssertingIterator(final CloseableIterator iterator) { wrappedIterator = iterator; } + @Override public SAMRecordIterator assertSorted(final SAMFileHeader.SortOrder sortOrder) { if (sortOrder == null || sortOrder == SAMFileHeader.SortOrder.unsorted) { @@ -569,6 +571,7 @@ public SAMRecordIterator assertSorted(final SAMFileHeader.SortOrder sortOrder) { return this; } + @Override public SAMRecord next() { final SAMRecord result = wrappedIterator.next(); if (comparator != null) { @@ -591,10 +594,13 @@ public SAMRecord next() { return result; } + @Override public void close() { wrappedIterator.close(); } + @Override public boolean hasNext() { return wrappedIterator.hasNext(); } + @Override public void remove() { wrappedIterator.remove(); } } diff --git a/src/main/java/htsjdk/samtools/TextTagCodec.java b/src/main/java/htsjdk/samtools/TextTagCodec.java index 60363e160..40dc8ac73 100644 --- a/src/main/java/htsjdk/samtools/TextTagCodec.java +++ b/src/main/java/htsjdk/samtools/TextTagCodec.java @@ -158,14 +158,17 @@ public String encodeUntypedTag(final String tagName, final Object value) { final String stringVal = numFields == TextTagCodec.NUM_TAG_FIELDS ? fields[2] : ""; final Object val = convertStringToObject(type, stringVal); return new Map.Entry() { + @Override public String getKey() { return key; } + @Override public Object getValue() { return val; } + @Override public Object setValue(final Object o) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/htsjdk/samtools/TextualBAMIndexWriter.java b/src/main/java/htsjdk/samtools/TextualBAMIndexWriter.java index d79027069..da418fd2d 100644 --- a/src/main/java/htsjdk/samtools/TextualBAMIndexWriter.java +++ b/src/main/java/htsjdk/samtools/TextualBAMIndexWriter.java @@ -68,6 +68,7 @@ private void writeHeader() { /** * Write this content as human-readable text */ + @Override public void writeReference(final BAMIndexContent content) { final int reference = content.getReferenceSequence(); @@ -172,6 +173,7 @@ private void writeNullContent(final int reference) { * * @param noCoordinateCount the count of records seen with no coordinate positions in the start coordinate */ + @Override public void writeNoCoordinateRecordCount(final Long noCoordinateCount) { pw.println("No Coordinate Count=" + noCoordinateCount); } @@ -179,6 +181,7 @@ public void writeNoCoordinateRecordCount(final Long noCoordinateCount) { /** * Any necessary processing at the end of the file */ + @Override public void close() { pw.close(); } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayLenEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayLenEncoding.java index 0c76a5b6e..0c4557793 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayLenEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayLenEncoding.java @@ -60,6 +60,7 @@ public static EncodingParams toParam(final EncodingParams lenParams, return new EncodingParams(ID, byteArrayOutputStream.toByteArray()); } + @Override public byte[] toByteArray() { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { @@ -78,6 +79,7 @@ public static EncodingParams toParam(final EncodingParams lenParams, return byteArrayOutputStream.toByteArray(); } + @Override public void fromByteArray(final byte[] data) { final ByteBuffer buffer = ByteBuffer.wrap(data); diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayStopEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayStopEncoding.java index c46d96754..c62334d6a 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayStopEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ByteArrayStopEncoding.java @@ -56,6 +56,7 @@ public static EncodingParams toParam(final byte stopByte, final int externalId) return new EncodingParams(ID, e.toByteArray()); } + @Override public byte[] toByteArray() { final ByteBuffer buf = ByteBuffer.allocate(1024); buf.order(ByteOrder.LITTLE_ENDIAN); @@ -69,6 +70,7 @@ public static EncodingParams toParam(final byte stopByte, final int externalId) return array; } + @Override public void fromByteArray(final byte[] data) { final ByteBuffer buf = ByteBuffer.wrap(data); buf.order(ByteOrder.LITTLE_ENDIAN); diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteArrayEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteArrayEncoding.java index 2fc707c5f..107a484e1 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteArrayEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteArrayEncoding.java @@ -38,10 +38,12 @@ public static EncodingParams toParam(final int contentId) { return new EncodingParams(encodingId, e.toByteArray()); } + @Override public byte[] toByteArray() { return ITF8.writeUnsignedITF8(contentId); } + @Override public void fromByteArray(final byte[] data) { contentId = ITF8.readUnsignedITF8(data); } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteEncoding.java index 0fed72059..75a63ccd6 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ExternalByteEncoding.java @@ -38,10 +38,12 @@ public static EncodingParams toParam(final int contentId) { return new EncodingParams(encodingId, externalByteEncoding.toByteArray()); } + @Override public byte[] toByteArray() { return ITF8.writeUnsignedITF8(contentId); } + @Override public void fromByteArray(final byte[] data) { contentId = ITF8.readUnsignedITF8(data); } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerEncoding.java index a7c573668..1f0ecba69 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerEncoding.java @@ -38,10 +38,12 @@ public static EncodingParams toParam(final int contentId) { return new EncodingParams(encodingId, externalIntegerEncoding.toByteArray()); } + @Override public byte[] toByteArray() { return ITF8.writeUnsignedITF8(contentId); } + @Override public void fromByteArray(final byte[] data) { contentId = ITF8.readUnsignedITF8(data); } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ExternalLongEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/ExternalLongEncoding.java index 402cea888..b3ba54ef6 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ExternalLongEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ExternalLongEncoding.java @@ -38,10 +38,12 @@ public static EncodingParams toParam(final int contentId) { return new EncodingParams(encodingId, externalLongEncoding.toByteArray()); } + @Override public byte[] toByteArray() { return ITF8.writeUnsignedITF8(contentId); } + @Override public void fromByteArray(final byte[] data) { contentId = ITF8.readUnsignedITF8(data); } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/GolombRiceIntegerCodec.java b/src/main/java/htsjdk/samtools/cram/encoding/GolombRiceIntegerCodec.java index e5962a152..579f28b77 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/GolombRiceIntegerCodec.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/GolombRiceIntegerCodec.java @@ -38,6 +38,7 @@ public GolombRiceIntegerCodec(final int offset, final int log2m) { mask = ~(~0 << log2m); } + @Override public final Integer read(final BitInputStream bitInputStream) throws IOException { int unary = 0; diff --git a/src/main/java/htsjdk/samtools/cram/encoding/huffman/HuffmanTree.java b/src/main/java/htsjdk/samtools/cram/encoding/huffman/HuffmanTree.java index 43500c4d3..bd4316d23 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/huffman/HuffmanTree.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/huffman/HuffmanTree.java @@ -24,6 +24,7 @@ frequency = freq; } + @Override public int compareTo(@SuppressWarnings("NullableProblems") final HuffmanTree tree) { return frequency - tree.frequency; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/BaseQualityScore.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/BaseQualityScore.java index 41a69d27f..07ee30502 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/BaseQualityScore.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/BaseQualityScore.java @@ -44,6 +44,7 @@ public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/HardClip.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/HardClip.java index 3c3c7ad04..0e5678bb4 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/HardClip.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/HardClip.java @@ -41,10 +41,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/InsertBase.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/InsertBase.java index 597041337..d4a611e8d 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/InsertBase.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/InsertBase.java @@ -42,10 +42,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Insertion.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Insertion.java index e0182c312..2055ba0fd 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Insertion.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Insertion.java @@ -42,10 +42,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Padding.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Padding.java index 85e90fdf0..f9a201f2d 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Padding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Padding.java @@ -42,10 +42,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/ReadBase.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/ReadBase.java index 73ae20818..f56d6775a 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/ReadBase.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/ReadBase.java @@ -46,6 +46,7 @@ public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/RefSkip.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/RefSkip.java index 1b99f0969..e9e5ae37e 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/RefSkip.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/RefSkip.java @@ -42,10 +42,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/SoftClip.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/SoftClip.java index b142595dd..7eaac6727 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/SoftClip.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/SoftClip.java @@ -51,10 +51,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Substitution.java b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Substitution.java index b2ed5de62..bc84b5aa8 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Substitution.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/readfeatures/Substitution.java @@ -58,10 +58,12 @@ public byte getOperator() { return operator; } + @Override public int getPosition() { return position; } + @Override public void setPosition(final int position) { this.position = position; } diff --git a/src/main/java/htsjdk/samtools/cram/io/CountingInputStream.java b/src/main/java/htsjdk/samtools/cram/io/CountingInputStream.java index b5e564206..41cb22aef 100644 --- a/src/main/java/htsjdk/samtools/cram/io/CountingInputStream.java +++ b/src/main/java/htsjdk/samtools/cram/io/CountingInputStream.java @@ -37,42 +37,50 @@ public int read() throws IOException { return delegate.read(); } + @Override public int read(@SuppressWarnings("NullableProblems") final byte[] b) throws IOException { final int read = delegate.read(b); count += read; return read; } + @Override public int read(@SuppressWarnings("NullableProblems") final byte[] b, final int off, final int length) throws IOException { final int read = delegate.read(b, off, length); count += read; return read; } + @Override public long skip(final long n) throws IOException { final long skipped = delegate.skip(n); count += skipped; return skipped; } + @Override public int available() throws IOException { return delegate.available(); } + @Override public void close() throws IOException { if (delegate != null) delegate.close(); } + @Override public void mark(final int readLimit) { delegate.mark(readLimit); } + @Override public void reset() throws IOException { delegate.reset(); count = 0; } + @Override public boolean markSupported() { return delegate.markSupported(); } diff --git a/src/main/java/htsjdk/samtools/cram/io/DefaultBitInputStream.java b/src/main/java/htsjdk/samtools/cram/io/DefaultBitInputStream.java index 519cf9da3..fef9e2b08 100644 --- a/src/main/java/htsjdk/samtools/cram/io/DefaultBitInputStream.java +++ b/src/main/java/htsjdk/samtools/cram/io/DefaultBitInputStream.java @@ -41,6 +41,7 @@ public DefaultBitInputStream(final InputStream in) { this.throwEOF = true; } + @Override public final boolean readBit() throws IOException { if (--nofBufferedBits >= 0) return ((byteBuffer >>> nofBufferedBits) & 1) == 1; @@ -55,6 +56,7 @@ public final boolean readBit() throws IOException { return ((byteBuffer >>> 7) & 1) == 1; } + @Override public final int readBits(int n) throws IOException { if (n == 0) return 0; @@ -77,6 +79,7 @@ private static int rightBits(final int n, final int x) { return x & ((1 << n) - 1); } + @Override public final long readLongBits(int n) throws IOException { if (n > 64) throw new RuntimeException("More then 64 bits are requested in one read from bit stream."); @@ -108,6 +111,7 @@ public final long readLongBits(int n) throws IOException { return x | (byteBuffer >>> nofBufferedBits); } + @Override public void reset() { nofBufferedBits = 0; byteBuffer = 0; diff --git a/src/main/java/htsjdk/samtools/cram/io/DefaultBitOutputStream.java b/src/main/java/htsjdk/samtools/cram/io/DefaultBitOutputStream.java index 2d702ee16..95d6789a8 100644 --- a/src/main/java/htsjdk/samtools/cram/io/DefaultBitOutputStream.java +++ b/src/main/java/htsjdk/samtools/cram/io/DefaultBitOutputStream.java @@ -53,6 +53,7 @@ public String toString() { + Integer.toBinaryString(bufferByte).substring(0, bufferedNumberOfBits); } + @Override public void write(final long bitContainer, final int nofBits) throws IOException { if (nofBits == 0) return; @@ -95,6 +96,7 @@ void write_int_LSB_0(final int value, final int nofBitsToWrite) throws IOExcepti } } + @Override public void write(final int bitContainer, final int nofBits) throws IOException { write_int_LSB_0(bitContainer, nofBits); } @@ -109,6 +111,7 @@ private void writeByte(final int value) throws IOException { } } + @Override public void write(byte bitContainer, final int nofBits) throws IOException { if (nofBits < 0 || nofBits > 8) throw new IOException("Expecting 0 to 8 bits."); @@ -145,6 +148,7 @@ public void write(final boolean bit) throws IOException { write(bit ? (byte) 1 : (byte) 0, 1); } + @Override public void write(final boolean bit, final long repeat) throws IOException { for (long i = 0; i < repeat; i++) write(bit); diff --git a/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java b/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java index e73fb4155..b162c9412 100644 --- a/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java +++ b/src/main/java/htsjdk/samtools/cram/ref/ReferenceSource.java @@ -133,6 +133,7 @@ public void clearCache() { return bases; } + @Override public synchronized byte[] getReferenceBases(final SAMSequenceRecord record, final boolean tryNameVariants) { { // check cache by sequence name: diff --git a/src/main/java/htsjdk/samtools/fastq/FastqReader.java b/src/main/java/htsjdk/samtools/fastq/FastqReader.java index 8086dfaee..7988712f3 100755 --- a/src/main/java/htsjdk/samtools/fastq/FastqReader.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqReader.java @@ -128,8 +128,10 @@ private FastqRecord readNextRecord() { } } + @Override public boolean hasNext() { return nextRecord != null; } + @Override public FastqRecord next() { if (!hasNext()) { throw new NoSuchElementException("next() called when !hasNext()"); @@ -139,6 +141,7 @@ public FastqRecord next() { return rec; } + @Override public void remove() { throw new UnsupportedOperationException("Unsupported operation"); } /** @@ -146,6 +149,7 @@ public FastqRecord next() { * start iteration from the beginning of the file. Developers should probably not call iterator() * directly. It is provided so that this class can be used in Java for-each loop. */ + @Override public Iterator iterator() { return this; } public int getLineNumber() { return line ; } diff --git a/src/main/java/htsjdk/samtools/fastq/FastqWriter.java b/src/main/java/htsjdk/samtools/fastq/FastqWriter.java index e37aec57d..3b2a1b688 100644 --- a/src/main/java/htsjdk/samtools/fastq/FastqWriter.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqWriter.java @@ -9,5 +9,6 @@ */ public interface FastqWriter extends Closeable { void write(final FastqRecord rec); + @Override void close(); } diff --git a/src/main/java/htsjdk/samtools/filter/AggregateFilter.java b/src/main/java/htsjdk/samtools/filter/AggregateFilter.java index f396c593f..62b804b79 100644 --- a/src/main/java/htsjdk/samtools/filter/AggregateFilter.java +++ b/src/main/java/htsjdk/samtools/filter/AggregateFilter.java @@ -51,6 +51,7 @@ public AggregateFilter(final List filters) { * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches at least one filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { for (final SamRecordFilter filter : filters) { if (filter.filterOut(record)) { @@ -68,6 +69,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { for (final SamRecordFilter filter : filters) { if (filter.filterOut(first, second)) { diff --git a/src/main/java/htsjdk/samtools/filter/AlignedFilter.java b/src/main/java/htsjdk/samtools/filter/AlignedFilter.java index c70453d00..cebdc0b95 100644 --- a/src/main/java/htsjdk/samtools/filter/AlignedFilter.java +++ b/src/main/java/htsjdk/samtools/filter/AlignedFilter.java @@ -45,6 +45,7 @@ public AlignedFilter(final boolean includeAligned) { * * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { if (includeAligned) { if (!record.getReadUnmappedFlag()) { @@ -68,6 +69,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { if (includeAligned) { diff --git a/src/main/java/htsjdk/samtools/filter/DuplicateReadFilter.java b/src/main/java/htsjdk/samtools/filter/DuplicateReadFilter.java index c79b3ccfd..2fe773f11 100644 --- a/src/main/java/htsjdk/samtools/filter/DuplicateReadFilter.java +++ b/src/main/java/htsjdk/samtools/filter/DuplicateReadFilter.java @@ -34,6 +34,7 @@ * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { return record.getDuplicateReadFlag(); } @@ -46,6 +47,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { throw new UnsupportedOperationException("Paired DuplicateReadFilter filter not implemented!"); } diff --git a/src/main/java/htsjdk/samtools/filter/FailsVendorReadQualityFilter.java b/src/main/java/htsjdk/samtools/filter/FailsVendorReadQualityFilter.java index 7c6825cba..661286df3 100644 --- a/src/main/java/htsjdk/samtools/filter/FailsVendorReadQualityFilter.java +++ b/src/main/java/htsjdk/samtools/filter/FailsVendorReadQualityFilter.java @@ -38,6 +38,7 @@ * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { return record.getReadFailsVendorQualityCheckFlag(); } @@ -50,6 +51,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // if either fails, exclude them both return (first.getReadFailsVendorQualityCheckFlag() || second.getReadFailsVendorQualityCheckFlag()); diff --git a/src/main/java/htsjdk/samtools/filter/FilteringSamIterator.java b/src/main/java/htsjdk/samtools/filter/FilteringSamIterator.java index 7ac1c0aaa..a70156ad6 100644 --- a/src/main/java/htsjdk/samtools/filter/FilteringSamIterator.java +++ b/src/main/java/htsjdk/samtools/filter/FilteringSamIterator.java @@ -87,6 +87,7 @@ public FilteringSamIterator(final Iterator iterator, final SamRecordF * * @return true if the iteration has more elements. Otherwise returns false. */ + @Override public boolean hasNext() { return next != null; } @@ -98,6 +99,7 @@ public boolean hasNext() { * @throws java.util.NoSuchElementException * */ + @Override public SAMRecord next() { if (next == null) { throw new NoSuchElementException("Iterator has no more elements."); @@ -112,10 +114,12 @@ public SAMRecord next() { * * @throws UnsupportedOperationException */ + @Override public void remove() { throw new UnsupportedOperationException("Remove() not supported by FilteringSamIterator"); } + @Override public void close() { CloserUtil.close(iterator); } diff --git a/src/main/java/htsjdk/samtools/filter/IntervalFilter.java b/src/main/java/htsjdk/samtools/filter/IntervalFilter.java index ff3620ae9..ef5c98a3f 100644 --- a/src/main/java/htsjdk/samtools/filter/IntervalFilter.java +++ b/src/main/java/htsjdk/samtools/filter/IntervalFilter.java @@ -65,6 +65,7 @@ public IntervalFilter(final List intervals, final SAMFileHeader samHea * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { while (currentInterval != null && (currentSequenceIndex < record.getReferenceIndex() || @@ -93,6 +94,7 @@ private void advanceInterval() { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // This can never be implemented because if the bam is coordinate sorted, // which it has to be for this filter, it will never get both the first and second reads together diff --git a/src/main/java/htsjdk/samtools/filter/IntervalKeepPairFilter.java b/src/main/java/htsjdk/samtools/filter/IntervalKeepPairFilter.java index 5a7961bbb..c4e01aae2 100644 --- a/src/main/java/htsjdk/samtools/filter/IntervalKeepPairFilter.java +++ b/src/main/java/htsjdk/samtools/filter/IntervalKeepPairFilter.java @@ -65,6 +65,7 @@ public IntervalKeepPairFilter(final List intervals) { * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { if (record.isSecondaryOrSupplementary()) { return true; @@ -102,6 +103,7 @@ private boolean hasOverlaps(final String refSequence, final int start, final int * * @return true if both SAMRecords do not overlap the interval list */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { return filterOut(first) && filterOut(second); } diff --git a/src/main/java/htsjdk/samtools/filter/NotPrimaryAlignmentFilter.java b/src/main/java/htsjdk/samtools/filter/NotPrimaryAlignmentFilter.java index 0f2364c92..cda45e045 100644 --- a/src/main/java/htsjdk/samtools/filter/NotPrimaryAlignmentFilter.java +++ b/src/main/java/htsjdk/samtools/filter/NotPrimaryAlignmentFilter.java @@ -35,6 +35,7 @@ * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { return record.getNotPrimaryAlignmentFlag(); } @@ -47,6 +48,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // if either fails, exclude them both return (first.getNotPrimaryAlignmentFlag() || second.getNotPrimaryAlignmentFlag()); diff --git a/src/main/java/htsjdk/samtools/filter/ReadNameFilter.java b/src/main/java/htsjdk/samtools/filter/ReadNameFilter.java index e4b2a20d9..94a4397a8 100644 --- a/src/main/java/htsjdk/samtools/filter/ReadNameFilter.java +++ b/src/main/java/htsjdk/samtools/filter/ReadNameFilter.java @@ -79,6 +79,7 @@ public ReadNameFilter(final Set readNameFilterSet, final boolean include * * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { if (includeReads) { if (readNameFilterSet.contains(record.getReadName())) { @@ -101,6 +102,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the pair of records matches filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { if (includeReads) { if (readNameFilterSet.contains(first.getReadName()) && diff --git a/src/main/java/htsjdk/samtools/filter/SecondaryAlignmentFilter.java b/src/main/java/htsjdk/samtools/filter/SecondaryAlignmentFilter.java index d91212d40..22741ae0d 100644 --- a/src/main/java/htsjdk/samtools/filter/SecondaryAlignmentFilter.java +++ b/src/main/java/htsjdk/samtools/filter/SecondaryAlignmentFilter.java @@ -9,11 +9,13 @@ /** * Returns true if the read is marked as secondary. */ + @Override public boolean filterOut(final SAMRecord record) { return record.getNotPrimaryAlignmentFlag(); } /** * Returns true if either read is marked as secondary. */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { return first.getNotPrimaryAlignmentFlag() || second.getNotPrimaryAlignmentFlag(); } diff --git a/src/main/java/htsjdk/samtools/filter/SecondaryOrSupplementaryFilter.java b/src/main/java/htsjdk/samtools/filter/SecondaryOrSupplementaryFilter.java index ae57fd9d3..b7d21d157 100644 --- a/src/main/java/htsjdk/samtools/filter/SecondaryOrSupplementaryFilter.java +++ b/src/main/java/htsjdk/samtools/filter/SecondaryOrSupplementaryFilter.java @@ -13,6 +13,7 @@ * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { return record.isSecondaryOrSupplementary(); } @@ -25,6 +26,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // if either fails, exclude them both return first.isSecondaryOrSupplementary() || second.isSecondaryOrSupplementary(); diff --git a/src/main/java/htsjdk/samtools/filter/SolexaNoiseFilter.java b/src/main/java/htsjdk/samtools/filter/SolexaNoiseFilter.java index bfb31d6d4..ce169ef83 100644 --- a/src/main/java/htsjdk/samtools/filter/SolexaNoiseFilter.java +++ b/src/main/java/htsjdk/samtools/filter/SolexaNoiseFilter.java @@ -40,6 +40,7 @@ * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord record) { final byte[] sequence = record.getReadBases(); for (final byte base : sequence) { @@ -59,6 +60,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // only filter out the pair if both first and second reads have all As return (filterOut(first) && filterOut(second)); diff --git a/src/main/java/htsjdk/samtools/filter/TagFilter.java b/src/main/java/htsjdk/samtools/filter/TagFilter.java index 5182e836c..00ca8a46c 100644 --- a/src/main/java/htsjdk/samtools/filter/TagFilter.java +++ b/src/main/java/htsjdk/samtools/filter/TagFilter.java @@ -66,6 +66,7 @@ public TagFilter(String tag, List values) { * @param record the SAMRecord to evaluate * @return true if the SAMRecord matches the filter, otherwise false */ + @Override public boolean filterOut(SAMRecord record) { return values.contains(record.getAttribute(tag)); } @@ -78,6 +79,7 @@ public boolean filterOut(SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // both first and second must have the tag in order for it to be filtered out return values.contains(first.getAttribute(tag)) && values.contains(second.getAttribute(tag)); diff --git a/src/main/java/htsjdk/samtools/filter/WholeReadClippedFilter.java b/src/main/java/htsjdk/samtools/filter/WholeReadClippedFilter.java index 2a1566ce0..6df3c4454 100644 --- a/src/main/java/htsjdk/samtools/filter/WholeReadClippedFilter.java +++ b/src/main/java/htsjdk/samtools/filter/WholeReadClippedFilter.java @@ -57,6 +57,7 @@ public boolean filterOut(final SAMRecord record) { * * @return true if the SAMRecords matches the filter, otherwise false */ + @Override public boolean filterOut(final SAMRecord first, final SAMRecord second) { // if either fails, exclude them both return (filterOut(first) || filterOut(second)); diff --git a/src/main/java/htsjdk/samtools/metrics/StringHeader.java b/src/main/java/htsjdk/samtools/metrics/StringHeader.java index ced159598..949dd4153 100644 --- a/src/main/java/htsjdk/samtools/metrics/StringHeader.java +++ b/src/main/java/htsjdk/samtools/metrics/StringHeader.java @@ -43,6 +43,7 @@ public StringHeader(String value) { setValue(value); } + @Override public void parse(String in) { value = in.trim(); } public String toString() { return value; } diff --git a/src/main/java/htsjdk/samtools/metrics/VersionHeader.java b/src/main/java/htsjdk/samtools/metrics/VersionHeader.java index ae0845502..82093aaa5 100644 --- a/src/main/java/htsjdk/samtools/metrics/VersionHeader.java +++ b/src/main/java/htsjdk/samtools/metrics/VersionHeader.java @@ -37,6 +37,7 @@ private String versionedItem; private String versionString; + @Override public void parse(String in) { String[] fields = in.split("\t"); this.versionedItem = fields[0]; diff --git a/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java b/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java index badcf1987..736107bb1 100644 --- a/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/AbstractFastaSequenceFile.java @@ -115,6 +115,7 @@ protected Path getPath() { * Returns the list of sequence records associated with the reference sequence if found * otherwise null. */ + @Override public SAMSequenceDictionary getSequenceDictionary() { return this.sequenceDictionary; } @@ -130,14 +131,17 @@ public String toString() { } /** default implementation -- override if index is supported */ + @Override public boolean isIndexed() {return false;} /** default implementation -- override if index is supported */ + @Override public ReferenceSequence getSequence( String contig ) { throw new UnsupportedOperationException(); } /** default implementation -- override if index is supported */ + @Override public ReferenceSequence getSubsequenceAt( String contig, long start, long stop ) { throw new UnsupportedOperationException("Index does not appear to exist for " + getAbsolutePath() + ". samtools faidx can be used to create an index"); } diff --git a/src/main/java/htsjdk/samtools/reference/FastaSequenceFile.java b/src/main/java/htsjdk/samtools/reference/FastaSequenceFile.java index 72c0583bb..744d79773 100644 --- a/src/main/java/htsjdk/samtools/reference/FastaSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/FastaSequenceFile.java @@ -62,10 +62,12 @@ public FastaSequenceFile(final Path path, final boolean truncateNamesAtWhitespac /** * It's good to call this to free up memory. */ + @Override public void close() { in.close(); } + @Override public ReferenceSequence nextSequence() { this.sequenceIndex += 1; @@ -83,6 +85,7 @@ public ReferenceSequence nextSequence() { return new ReferenceSequence(name, this.sequenceIndex, bases); } + @Override public void reset() { this.sequenceIndex = -1; this.in.close(); diff --git a/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java index e314fccbe..9ae9f1db9 100644 --- a/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java +++ b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java @@ -184,6 +184,7 @@ public FastaSequenceIndexEntry getIndexEntry( String contigName ) { * Creates an iterator which can iterate through all entries in a fasta index. * @return iterator over all fasta index entries. */ + @Override public Iterator iterator() { return sequenceEntries.values().iterator(); } diff --git a/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java b/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java index 60cc3b1b7..5a8703381 100644 --- a/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java @@ -111,6 +111,7 @@ public IndexedFastaSequenceFile(final Path path) throws FileNotFoundException { this(path, new FastaSequenceIndex((findRequiredFastaIndexFile(path)))); } + @Override public boolean isIndexed() {return true;} private static File findFastaIndex(File fastaFile) { @@ -190,6 +191,7 @@ protected static void sanityCheckDictionaryAgainstIndex(final String fastaFile, * Retrieves the sequence dictionary for the fasta file. * @return sequence dictionary of the fasta. */ + @Override public SAMSequenceDictionary getSequenceDictionary() { return sequenceDictionary; } @@ -199,6 +201,7 @@ public SAMSequenceDictionary getSequenceDictionary() { * @param contig contig whose data should be returned. * @return The full sequence associated with this contig. */ + @Override public ReferenceSequence getSequence( String contig ) { return getSubsequenceAt( contig, 1, (int)index.getIndexEntry(contig).getSize() ); } @@ -210,6 +213,7 @@ public ReferenceSequence getSequence( String contig ) { * @param stop inclusive, 1-based stop of region. * @return The partial reference sequence associated with this range. */ + @Override public ReferenceSequence getSubsequenceAt( String contig, long start, long stop ) { if(start > stop + 1) throw new SAMException(String.format("Malformed query; start point %d lies after end point %d",start,stop)); @@ -300,6 +304,7 @@ private static int readFromPosition(final SeekableByteChannel channel, final Byt * Gets the next sequence if available, or null if not present. * @return next sequence if available, or null if not present. */ + @Override public ReferenceSequence nextSequence() { if( !indexIterator.hasNext() ) return null; @@ -309,6 +314,7 @@ public ReferenceSequence nextSequence() { /** * Reset the iterator over the index. */ + @Override public void reset() { indexIterator = index.iterator(); } diff --git a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFile.java b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFile.java index e7d3c288c..49f526cbc 100644 --- a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFile.java @@ -86,5 +86,6 @@ */ public String toString(); + @Override public void close() throws IOException; } diff --git a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java index d66f0f870..6a820ebbe 100644 --- a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java +++ b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java @@ -105,6 +105,7 @@ public SAMSequenceDictionary getSequenceDictionary() { return referenceSequenceFile.getSequenceDictionary(); } + @Override public void close() throws IOException { referenceSequenceFile.close(); } diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableBufferedStream.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableBufferedStream.java index 56b4d0c9c..0c89b0166 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableBufferedStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableBufferedStream.java @@ -67,6 +67,7 @@ public SeekableBufferedStream(final SeekableStream stream) { this(stream, DEFAULT_BUFFER_SIZE); } + @Override public long length() { return wrappedStream.length(); } @@ -84,18 +85,21 @@ public long skip(final long skipLength) throws IOException { } } + @Override public void seek(final long position) throws IOException { this.position = position; wrappedStream.seek(position); bufferedStream = new ExtBufferedInputStream(wrappedStream, bufferSize); } + @Override public int read() throws IOException { int b = bufferedStream.read(); position++; return b; } + @Override public int read(final byte[] buffer, final int offset, final int length) throws IOException { int nBytesRead = bufferedStream.read(buffer, offset, length); if (nBytesRead > 0) { @@ -112,10 +116,12 @@ public int read(final byte[] buffer, final int offset, final int length) throws return nBytesRead; } + @Override public void close() throws IOException { wrappedStream.close(); } + @Override public boolean eof() throws IOException { return position >= wrappedStream.length(); } diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableFTPStream.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableFTPStream.java index 0a64a7c09..1723747d5 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableFTPStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableFTPStream.java @@ -39,10 +39,12 @@ public SeekableFTPStream(URL url, UserPasswordInput userPasswordInput) throws IO helper = new SeekableFTPStreamHelper(url, userPasswordInput); } + @Override public void seek(long position) { helper.seek(position); } + @Override public long position() { return helper.position(); } @@ -75,10 +77,12 @@ public int read(byte[] buffer, int offset, int len) throws IOException { } + @Override public void close() throws IOException { helper.close(); } + @Override public int read() throws IOException { return helper.read(); } diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableFileStream.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableFileStream.java index 38191d769..b790732a9 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableFileStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableFileStream.java @@ -48,18 +48,22 @@ public SeekableFileStream(final File file) throws FileNotFoundException { allInstances.add(this); } + @Override public long length() { return file.length(); } + @Override public boolean eof() throws IOException { return fis.length() == fis.getFilePointer(); } + @Override public void seek(final long position) throws IOException { fis.seek(position); } + @Override public long position() throws IOException { return fis.getChannel().position(); } @@ -71,6 +75,7 @@ public long skip(long n) throws IOException { return position() - initPos; } + @Override public int read(final byte[] buffer, final int offset, final int length) throws IOException { if (length < 0) { throw new IndexOutOfBoundsException(); @@ -91,6 +96,7 @@ public int read(final byte[] buffer, final int offset, final int length) throws } + @Override public int read() throws IOException { return fis.read(); } @@ -106,6 +112,7 @@ public String getSource() { } + @Override public void close() throws IOException { allInstances.remove(this); fis.close(); diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableHTTPStream.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableHTTPStream.java index 4a864b77e..640a14d98 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableHTTPStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableHTTPStream.java @@ -67,10 +67,12 @@ public SeekableHTTPStream(final URL url, Proxy proxy) { } + @Override public long position() { return position; } + @Override public long length() { return contentLength; } @@ -82,14 +84,17 @@ public long skip(long n) throws IOException { return bytesToSkip; } + @Override public boolean eof() throws IOException { return contentLength > 0 && position >= contentLength; } + @Override public void seek(final long position) { this.position = position; } + @Override public int read(byte[] buffer, int offset, int len) throws IOException { if (offset < 0 || len < 0 || (offset + len) > buffer.length) { @@ -168,11 +173,13 @@ public int read(byte[] buffer, int offset, int len) throws IOException { } + @Override public void close() throws IOException { // Nothing to do } + @Override public int read() throws IOException { byte []tmp=new byte[1]; read(tmp,0,1); diff --git a/src/main/java/htsjdk/samtools/seekablestream/SeekableStream.java b/src/main/java/htsjdk/samtools/seekablestream/SeekableStream.java index 673f08c48..45f699043 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/SeekableStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/SeekableStream.java @@ -35,8 +35,10 @@ public abstract void seek(long position) throws IOException; + @Override public abstract int read(byte[] buffer, int offset, int length) throws IOException; + @Override public abstract void close() throws IOException; public abstract boolean eof() throws IOException; diff --git a/src/main/java/htsjdk/samtools/sra/SRALazyRecord.java b/src/main/java/htsjdk/samtools/sra/SRALazyRecord.java index 4391857e6..c5067116e 100644 --- a/src/main/java/htsjdk/samtools/sra/SRALazyRecord.java +++ b/src/main/java/htsjdk/samtools/sra/SRALazyRecord.java @@ -675,6 +675,7 @@ protected SAMBinaryTagAndValue getBinaryAttributes() { return super.getBinaryAttributes(); } + @Override public boolean isUnsignedArrayAttribute(final String tag) { Short binaryTag = SAMTagUtil.getSingleton().makeBinaryTag(tag); LazyAttribute attr = lazyAttributeTags.get(binaryTag); diff --git a/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java b/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java index ef1803bce..f69578c07 100644 --- a/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java +++ b/src/main/java/htsjdk/samtools/util/AbstractAsyncWriter.java @@ -66,6 +66,7 @@ public void write(final T item) { * Attempts to finish draining the queue and then calls synchronouslyClose() to allow implementation * to do any one time clean up. */ + @Override public void close() { checkAndRethrow(); @@ -110,6 +111,7 @@ private final void checkAndRethrow() { * synchronous writer. */ private class WriterRunnable implements Runnable { + @Override public void run() { try { //The order of the two conditions is important, see https://github.com/samtools/htsjdk/issues/564 diff --git a/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java b/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java index 4e020071d..d699dce8f 100644 --- a/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java +++ b/src/main/java/htsjdk/samtools/util/AbstractLocusInfo.java @@ -83,6 +83,7 @@ public void add(E recordAndOffset) { /** * @return the index of reference sequence */ + @Override public int getSequenceIndex() { return referenceSequence.getSequenceIndex(); } @@ -90,6 +91,7 @@ public int getSequenceIndex() { /** * @return 1-based reference position */ + @Override public int getPosition() { return position; } diff --git a/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java b/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java index 6ff8e835c..e35087405 100644 --- a/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java +++ b/src/main/java/htsjdk/samtools/util/AbstractLocusIterator.java @@ -182,6 +182,7 @@ public AbstractLocusIterator(final SamReader samReader, final IntervalList inter * @return iterator over all/all covered locus position in reference according to emitUncoveredLoci * value. */ + @Override public Iterator iterator() { if (samIterator != null) { throw new IllegalStateException("Cannot call iterator() more than once on " + this.getClass().getSimpleName()); @@ -202,6 +203,7 @@ public AbstractLocusIterator(final SamReader samReader, final IntervalList inter /** * Closes inner SamIterator. */ + @Override public void close() { this.samIterator.close(); } @@ -216,6 +218,7 @@ private boolean samHasMore() { * 2) there are AbstractLocusInfos in some stage of accumulation * 3) there are loci in the target mask that have yet to be accumulated (even if there are no reads covering them) */ + @Override public boolean hasNext() { if (this.samIterator == null) { iterator(); @@ -253,6 +256,7 @@ private boolean hasRemainingMaskBases() { * * @return information about next locus position in reference sequence */ + @Override public K next() { // if we don't have any completed entries to return, try and make some! while (complete.isEmpty() && samHasMore()) { @@ -475,6 +479,7 @@ protected SAMSequenceRecord getReferenceSequence(final int referenceSequenceInde return samReader.getFileHeader().getSequence(referenceSequenceIndex); } + @Override public void remove() { throw new UnsupportedOperationException("Can not remove records from a SAM file via an iterator!"); } diff --git a/src/main/java/htsjdk/samtools/util/AsciiWriter.java b/src/main/java/htsjdk/samtools/util/AsciiWriter.java index 00c6f7f1f..50b08d844 100644 --- a/src/main/java/htsjdk/samtools/util/AsciiWriter.java +++ b/src/main/java/htsjdk/samtools/util/AsciiWriter.java @@ -50,6 +50,7 @@ public AsciiWriter(final OutputStream os) { /** * flushes and closes underlying OutputStream. */ + @Override public void close() throws IOException { flush(); os.close(); @@ -58,6 +59,7 @@ public void close() throws IOException { /** * flushes underlying OutputStream */ + @Override public void flush() throws IOException { os.write(buffer, 0, numBytes); numBytes = 0; @@ -67,6 +69,7 @@ public void flush() throws IOException { /** * All other Writer methods vector through this, so this is the only one that must be overridden. */ + @Override public void write(final char[] chars, int offset, int length) throws IOException { while (length > 0) { final int charsToConvert = Math.min(length, buffer.length - numBytes); diff --git a/src/main/java/htsjdk/samtools/util/AsyncBlockCompressedInputStream.java b/src/main/java/htsjdk/samtools/util/AsyncBlockCompressedInputStream.java index cbb168d43..4f71ef581 100644 --- a/src/main/java/htsjdk/samtools/util/AsyncBlockCompressedInputStream.java +++ b/src/main/java/htsjdk/samtools/util/AsyncBlockCompressedInputStream.java @@ -47,6 +47,7 @@ public class AsyncBlockCompressedInputStream extends BlockCompressedInputStream { private static final int READ_AHEAD_BUFFERS = (int)Math.ceil(Defaults.NON_ZERO_BUFFER_SIZE / BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE); private static final Executor threadpool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),new ThreadFactory() { + @Override public Thread newThread(Runnable r) { Thread t = Executors.defaultThreadFactory().newThread(r); t.setDaemon(true); diff --git a/src/main/java/htsjdk/samtools/util/BinaryCodec.java b/src/main/java/htsjdk/samtools/util/BinaryCodec.java index 8933ee35d..fdef93196 100644 --- a/src/main/java/htsjdk/samtools/util/BinaryCodec.java +++ b/src/main/java/htsjdk/samtools/util/BinaryCodec.java @@ -587,6 +587,7 @@ public long readUInt() { /** * Close the appropriate stream */ + @Override public void close() { try { if (this.isWriting) { diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java index 70a316199..066a0c001 100755 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java @@ -189,6 +189,7 @@ public void setCheckCrcs(final boolean check) { * Note that although the next caller can read this many bytes without blocking, the available() method call itself * may block in order to fill an internal buffer if it has been exhausted. */ + @Override public int available() throws IOException { if (mCurrentBlock == null || mCurrentOffset == mCurrentBlock.mBlock.length) { readBlock(); @@ -210,6 +211,7 @@ public boolean endOfBlock() { /** * Closes the underlying InputStream or RandomAccessFile */ + @Override public void close() throws IOException { if (mFile != null) { mFile.close(); @@ -230,6 +232,7 @@ public void close() throws IOException { * @return the next byte of data, or -1 if the end of the stream is reached. */ + @Override public int read() throws IOException { return (available() > 0) ? (mCurrentBlock.mBlock[mCurrentOffset++] & 0xFF) : -1; } @@ -245,6 +248,7 @@ public int read() throws IOException { * @return the total number of bytes read into the buffer, or -1 is there is no more data because the end of * the stream has been reached. */ + @Override public int read(final byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); } @@ -316,6 +320,7 @@ public String readLine() throws IOException { * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of * the stream has been reached. */ + @Override public int read(final byte[] buffer, int offset, int length) throws IOException { final int originalLength = length; while (length > 0) { diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java index 408282f1f..4e9a59487 100644 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java @@ -296,6 +296,7 @@ public void close() throws IOException { * @param bite * @throws IOException */ + @Override public void write(final int bite) throws IOException { singleByteArray[0] = (byte)bite; write(singleByteArray); diff --git a/src/main/java/htsjdk/samtools/util/BufferedLineReader.java b/src/main/java/htsjdk/samtools/util/BufferedLineReader.java index de1115dc4..18a4d05c0 100644 --- a/src/main/java/htsjdk/samtools/util/BufferedLineReader.java +++ b/src/main/java/htsjdk/samtools/util/BufferedLineReader.java @@ -59,6 +59,7 @@ public BufferedLineReader(final InputStream is, final int bufferSize) { * * @return the line read, or null if EOF has been reached. */ + @Override public String readLine() { ++lineNumber; try { @@ -78,6 +79,7 @@ public String readLine() { /** * @return 1-based number of line most recently read */ + @Override public int getLineNumber() { return lineNumber; } @@ -87,6 +89,7 @@ public int getLineNumber() { * * @return If not eof, the next character that would be read. If eof, -1. */ + @Override public int peek() { if (peekedLine == null) { try { @@ -104,6 +107,7 @@ public int peek() { return peekedLine.charAt(0); } + @Override public void close() { peekedLine = null; try { diff --git a/src/main/java/htsjdk/samtools/util/CloseableIterator.java b/src/main/java/htsjdk/samtools/util/CloseableIterator.java index d26443e0c..fa657be22 100755 --- a/src/main/java/htsjdk/samtools/util/CloseableIterator.java +++ b/src/main/java/htsjdk/samtools/util/CloseableIterator.java @@ -45,6 +45,7 @@ */ public interface CloseableIterator extends Iterator, Closeable { /** Should be implemented to close/release any underlying resources. */ + @Override void close(); /** Consumes the contents of the iterator and returns it as a List. */ diff --git a/src/main/java/htsjdk/samtools/util/DelegatingIterator.java b/src/main/java/htsjdk/samtools/util/DelegatingIterator.java index 054352bac..9d5174a93 100644 --- a/src/main/java/htsjdk/samtools/util/DelegatingIterator.java +++ b/src/main/java/htsjdk/samtools/util/DelegatingIterator.java @@ -15,20 +15,24 @@ public DelegatingIterator(final Iterator iterator) { this.iterator = iterator; } + @Override public void close() { if (iterator instanceof CloseableIterator) { ((CloseableIterator) this.iterator).close(); } } + @Override public boolean hasNext() { return this.iterator.hasNext(); } + @Override public T next() { return this.iterator.next(); } + @Override public void remove() { this.iterator.remove(); } diff --git a/src/main/java/htsjdk/samtools/util/DiskBackedQueue.java b/src/main/java/htsjdk/samtools/util/DiskBackedQueue.java index bbf38188b..22fca1138 100644 --- a/src/main/java/htsjdk/samtools/util/DiskBackedQueue.java +++ b/src/main/java/htsjdk/samtools/util/DiskBackedQueue.java @@ -130,6 +130,7 @@ public boolean headRecordIsFromDisk() { * @return true (if add successful) * @throws IllegalStateException if the queue cannot be added to */ + @Override public boolean add(final E record) throws IllegalStateException { if (!canAdd) throw new IllegalStateException("Cannot add to DiskBackedQueue whose canAdd() method returns false"); @@ -192,6 +193,7 @@ public E peek() { /** * Return the total number of elements in the queue, both in memory and on disk */ + @Override public int size() { return (this.headRecord == null) ? 0 : (1 + this.ramRecords.size() + this.numRecordsOnDisk); } @@ -238,6 +240,7 @@ public void clear() { * * @throws Throwable */ + @Override protected void finalize() throws Throwable { this.closeIOResources(); super.finalize(); // NB: intellij wanted me to do this. Need I? I'm not extending anything diff --git a/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java index b83a169e6..85beb66f0 100644 --- a/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java +++ b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java @@ -103,6 +103,7 @@ protected StartEdgingRecordAndOffset(SAMRecord record, int offset, int length, i * @param position in the reference * @return base quality of a read base, corresponding to a given reference position */ + @Override public byte getBaseQuality(int position) { int rOffset = getRelativeOffset(position); byte[] baseQualities = record.getBaseQualities(); @@ -174,6 +175,7 @@ private int getRelativeOffset(int position) { * @param position in the reference * @return base quality of a read base, corresponding to a given reference position */ + @Override public byte getBaseQuality(int position) { return start.getBaseQuality(position); } diff --git a/src/main/java/htsjdk/samtools/util/FastLineReader.java b/src/main/java/htsjdk/samtools/util/FastLineReader.java index d802fad22..95d620267 100644 --- a/src/main/java/htsjdk/samtools/util/FastLineReader.java +++ b/src/main/java/htsjdk/samtools/util/FastLineReader.java @@ -79,6 +79,7 @@ public boolean skipNewlines() { return sawEoln; } + @Override public void close() { CloserUtil.close(in); in = null; diff --git a/src/main/java/htsjdk/samtools/util/FileAppendStreamLRUCache.java b/src/main/java/htsjdk/samtools/util/FileAppendStreamLRUCache.java index bc8bc01cd..500b93182 100644 --- a/src/main/java/htsjdk/samtools/util/FileAppendStreamLRUCache.java +++ b/src/main/java/htsjdk/samtools/util/FileAppendStreamLRUCache.java @@ -47,6 +47,7 @@ public FileAppendStreamLRUCache(final int cacheSize) { } private static class Functor implements ResourceLimitedMapFunctor { + @Override public OutputStream makeValue(final File file) { try { return IOUtil.maybeBufferOutputStream(new FileOutputStream(file, true)); @@ -65,6 +66,7 @@ public OutputStream makeValue(final File file) { } } + @Override public void finalizeValue(final File file, final OutputStream out) { try { out.flush(); diff --git a/src/main/java/htsjdk/samtools/util/IOUtil.java b/src/main/java/htsjdk/samtools/util/IOUtil.java index 97d4d9cdc..0903a09f0 100644 --- a/src/main/java/htsjdk/samtools/util/IOUtil.java +++ b/src/main/java/htsjdk/samtools/util/IOUtil.java @@ -696,6 +696,7 @@ public static void copyFile(final File input, final File output) { public static File[] getFilesMatchingRegexp(final File directory, final Pattern regexp) { return directory.listFiles( new FilenameFilter() { + @Override public boolean accept(final File dir, final String name) { return regexp.matcher(name).matches(); } diff --git a/src/main/java/htsjdk/samtools/util/Interval.java b/src/main/java/htsjdk/samtools/util/Interval.java index 779bb25c9..51e91270a 100644 --- a/src/main/java/htsjdk/samtools/util/Interval.java +++ b/src/main/java/htsjdk/samtools/util/Interval.java @@ -141,6 +141,7 @@ public static long countBases(final Collection intervals) { * Sort based on sequence.compareTo, then start pos, then end pos * with null objects coming lexically last */ + @Override public int compareTo(final Interval that) { if (that == null) return -1; // nulls last diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index 76cb5084c..9bfc718f9 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -84,6 +84,7 @@ public IntervalList(final SAMSequenceDictionary dict) { public SAMFileHeader getHeader() { return header; } /** Returns an iterator over the intervals. */ + @Override public Iterator iterator() { return this.intervals.iterator(); } /** Adds an interval to the list of intervals. */ @@ -762,6 +763,7 @@ public int hashCode() { this.header = header; } + @Override public int compare(final Interval lhs, final Interval rhs) { final int lhsIndex = this.header.getSequenceIndex(lhs.getContig()); final int rhsIndex = this.header.getSequenceIndex(rhs.getContig()); diff --git a/src/main/java/htsjdk/samtools/util/IntervalListReferenceSequenceMask.java b/src/main/java/htsjdk/samtools/util/IntervalListReferenceSequenceMask.java index 1ddd164c6..08c2dd5e1 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalListReferenceSequenceMask.java +++ b/src/main/java/htsjdk/samtools/util/IntervalListReferenceSequenceMask.java @@ -66,6 +66,7 @@ public IntervalListReferenceSequenceMask(final IntervalList intervalList) { * * @return true if the mask is set for the given sequence and position */ + @Override public boolean get(final int sequenceIndex, final int position) { ensureSequenceLoaded(sequenceIndex); return currentBitSet.get(position); @@ -76,6 +77,7 @@ public boolean get(final int sequenceIndex, final int position) { * * @return the next pos on the given sequence >= position that is set, or -1 if there are no more set positions */ + @Override public int nextPosition(final int sequenceIndex, final int position) { ensureSequenceLoaded(sequenceIndex); // nextSetBit returns the first set bit on or after the starting index, therefore position+1 @@ -108,6 +110,7 @@ private void ensureSequenceLoaded(final int sequenceIndex) { /** * @return Largest sequence index for which there are set bits. */ + @Override public int getMaxSequenceIndex() { return lastSequenceIndex; } @@ -115,6 +118,7 @@ public int getMaxSequenceIndex() { /** * @return the largest position on the last sequence index */ + @Override public int getMaxPosition() { return lastPosition; } diff --git a/src/main/java/htsjdk/samtools/util/IntervalTree.java b/src/main/java/htsjdk/samtools/util/IntervalTree.java index cd4acdb69..3efc4dfbb 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalTree.java +++ b/src/main/java/htsjdk/samtools/util/IntervalTree.java @@ -340,6 +340,7 @@ public int getIndex( final int start, final int end ) * Return an iterator over the entire tree. * @return An iterator. */ + @Override public Iterator> iterator() { return new FwdIterator(min()); @@ -1069,11 +1070,13 @@ public FwdIterator( final Node node ) mNext = node; } + @Override public boolean hasNext() { return mNext != null; } + @Override public Node next() { if ( mNext == null ) @@ -1092,6 +1095,7 @@ public boolean hasNext() return mLast; } + @Override public void remove() { if ( mLast == null ) @@ -1115,11 +1119,13 @@ public RevIterator( final Node node ) mNext = node; } + @Override public boolean hasNext() { return mNext != null; } + @Override public Node next() { if ( mNext == null ) @@ -1135,6 +1141,7 @@ public boolean hasNext() return mLast; } + @Override public void remove() { if ( mLast == null ) @@ -1160,11 +1167,13 @@ public OverlapIterator( final int start, final int end ) mEnd = end; } + @Override public boolean hasNext() { return mNext != null; } + @Override public Node next() { if ( mNext == null ) @@ -1182,6 +1191,7 @@ public boolean hasNext() return mLast; } + @Override public void remove() { if ( mLast == null ) @@ -1207,16 +1217,19 @@ public ValuesIterator( final Iterator> itr ) mItr = itr; } + @Override public boolean hasNext() { return mItr.hasNext(); } + @Override public V1 next() { return mItr.next().getValue(); } + @Override public void remove() { mItr.remove(); diff --git a/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java b/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java index ffeae9439..259308732 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java +++ b/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java @@ -60,10 +60,12 @@ public IntervalTreeMap(final Map map) { } } + @Override public void clear() { mSequenceMap.clear(); } + @Override public boolean containsKey(final Object object) { if (!(object instanceof Interval)) { return false; @@ -79,6 +81,7 @@ public boolean containsKey(final Interval key) { return (tree.find(key.getStart(), key.getEnd()) != null); } + @Override public Set> entrySet() { return mEntrySet; } @@ -95,6 +98,7 @@ public int hashCode() { return mSequenceMap.hashCode(); } + @Override public T get(final Object object) { if (!(object instanceof Interval)) { return null; @@ -114,6 +118,7 @@ public T get(final Interval key) { return node.getValue(); } + @Override public boolean isEmpty() { for (final IntervalTree tree : mSequenceMap.values()) { if (tree.size() > 0) { @@ -123,6 +128,7 @@ public boolean isEmpty() { return true; } + @Override public T put(final Interval key, final T value) { IntervalTree tree = mSequenceMap.get(key.getContig()); if (tree == null) { @@ -132,6 +138,7 @@ public T put(final Interval key, final T value) { return tree.put(key.getStart(), key.getEnd(), value); } + @Override public T remove(final Object object) { if (!(object instanceof Interval)) { return null; @@ -147,6 +154,7 @@ public T remove(final Interval key) { return tree.remove(key.getStart(), key.getEnd()); } + @Override public int size() { // Note: We should think about caching the size to avoid having to recompute it. int size = 0; @@ -214,6 +222,7 @@ public boolean containsContained(final Interval key) { private class EntrySet extends AbstractSet> { + @Override public void clear() { IntervalTreeMap.this.clear(); } @@ -225,14 +234,17 @@ public boolean contains(final Map.Entry entry) { return entry.getValue().equals(IntervalTreeMap.this.get(entry.getKey())); } + @Override public boolean isEmpty() { return IntervalTreeMap.this.isEmpty(); } + @Override public Iterator> iterator() { return new EntryIterator(); } + @Override @SuppressWarnings("unchecked") public boolean remove(final Object object) { // Note: Could not figure out how to eliminate the unchecked cast. @@ -251,6 +263,7 @@ public boolean remove(final Map.Entry entry) { } } + @Override public int size() { return IntervalTreeMap.this.size(); } @@ -268,10 +281,12 @@ public int size() { advanceSequence(); } + @Override public boolean hasNext() { return (mTreeIterator != null && mTreeIterator.hasNext()); } + @Override public Map.Entry next() { if (!hasNext()) { throw new NoSuchElementException("Iterator exhausted"); @@ -286,6 +301,7 @@ public boolean hasNext() { return new MapEntry(key, value); } + @Override public void remove() { if (mTreeIterator == null) { throw new IllegalStateException("Iterator.next() has not been called"); @@ -315,14 +331,17 @@ private void advanceSequence() { mValue = value; } + @Override public Interval getKey() { return mKey; } + @Override public T getValue() { return mValue; } + @Override public T setValue(final T value) { mValue = value; return IntervalTreeMap.this.put(mKey, mValue); diff --git a/src/main/java/htsjdk/samtools/util/Iso8601Date.java b/src/main/java/htsjdk/samtools/util/Iso8601Date.java index 912886724..e173bd385 100644 --- a/src/main/java/htsjdk/samtools/util/Iso8601Date.java +++ b/src/main/java/htsjdk/samtools/util/Iso8601Date.java @@ -36,6 +36,7 @@ */ public class Iso8601Date extends Date { private static final ThreadLocal iso8601DateFormatter = new ThreadLocal() { + @Override protected synchronized DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); } diff --git a/src/main/java/htsjdk/samtools/util/LineReader.java b/src/main/java/htsjdk/samtools/util/LineReader.java index 018570083..4a07f15b8 100644 --- a/src/main/java/htsjdk/samtools/util/LineReader.java +++ b/src/main/java/htsjdk/samtools/util/LineReader.java @@ -47,5 +47,6 @@ */ int peek(); + @Override public void close(); } diff --git a/src/main/java/htsjdk/samtools/util/LocusComparator.java b/src/main/java/htsjdk/samtools/util/LocusComparator.java index e0f04d922..efbe09f26 100644 --- a/src/main/java/htsjdk/samtools/util/LocusComparator.java +++ b/src/main/java/htsjdk/samtools/util/LocusComparator.java @@ -34,6 +34,7 @@ public class LocusComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(T thing1, T thing2) { int refCompare = thing1.getSequenceIndex() - thing2.getSequenceIndex(); return refCompare == 0 ? thing1.getPosition() - thing2.getPosition() : refCompare; diff --git a/src/main/java/htsjdk/samtools/util/LocusImpl.java b/src/main/java/htsjdk/samtools/util/LocusImpl.java index 862907854..5986a6c94 100644 --- a/src/main/java/htsjdk/samtools/util/LocusImpl.java +++ b/src/main/java/htsjdk/samtools/util/LocusImpl.java @@ -36,6 +36,7 @@ public LocusImpl(int sequenceIndex, int position) { this.sequenceIndex = sequenceIndex; } + @Override public int getSequenceIndex() { return sequenceIndex; } @@ -43,6 +44,7 @@ public int getSequenceIndex() { /** * @return 1-based position */ + @Override public int getPosition() { return position; } diff --git a/src/main/java/htsjdk/samtools/util/Md5CalculatingInputStream.java b/src/main/java/htsjdk/samtools/util/Md5CalculatingInputStream.java index e0e7cd520..47ea9ff3b 100755 --- a/src/main/java/htsjdk/samtools/util/Md5CalculatingInputStream.java +++ b/src/main/java/htsjdk/samtools/util/Md5CalculatingInputStream.java @@ -65,12 +65,14 @@ public Md5CalculatingInputStream(InputStream is, File digestFile) { } } + @Override public int read() throws IOException { int result = is.read(); if (result != -1) md5.update((byte)result); return result; } + @Override public int read(byte[] b) throws IOException { int result = is.read(b); if (result != -1) md5.update(b, 0, result); @@ -78,6 +80,7 @@ public int read(byte[] b) throws IOException { } + @Override public int read(byte[] b, int off, int len) throws IOException { int result = is.read(b, off, len); if (result != -1) md5.update(b, off, result); @@ -104,6 +107,7 @@ private String makeHash() { } } + @Override public void close() throws IOException { is.close(); makeHash(); @@ -116,18 +120,23 @@ public void close() throws IOException { } // Methods not supported or overridden because they would not result in a valid hash + @Override public boolean markSupported() { return false; } + @Override public void mark(int readlimit) { throw new UnsupportedOperationException("mark() is not supported by the MD5CalculatingInputStream"); } + @Override public void reset() throws IOException { throw new UnsupportedOperationException("reset() is not supported by the MD5CalculatingInputStream"); } + @Override public long skip(long n) throws IOException { throw new UnsupportedOperationException("skip() is not supported by the MD5CalculatingInputStream"); } // Methods delegated to the wrapped InputStream + @Override public int available() throws IOException { return is.available(); } } diff --git a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java index 3c5a492c7..361c99032 100755 --- a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java @@ -65,17 +65,20 @@ public Md5CalculatingOutputStream(OutputStream os, File digestFile) { } } + @Override public void write(int b) throws IOException { md5.update((byte)b); os.write(b); } + @Override public void write(byte[] b) throws IOException { md5.update(b); os.write(b); } + @Override public void write(byte[] b, int off, int len) throws IOException { md5.update(b, off, len); os.write(b, off, len); @@ -102,6 +105,7 @@ private String makeHash() { } } + @Override public void close() throws IOException { os.close(); makeHash(); @@ -114,6 +118,7 @@ public void close() throws IOException { } // Pass-through method + @Override public void flush() throws IOException { os.flush(); } } diff --git a/src/main/java/htsjdk/samtools/util/PeekIterator.java b/src/main/java/htsjdk/samtools/util/PeekIterator.java index 9f16a5143..3a43ba54b 100644 --- a/src/main/java/htsjdk/samtools/util/PeekIterator.java +++ b/src/main/java/htsjdk/samtools/util/PeekIterator.java @@ -41,6 +41,7 @@ public PeekIterator(final Iterator underlyingIterator) { * @return true if the iteration has more elements. (In other words, returns true if next would return an element * rather than throwing an exception.) */ + @Override public boolean hasNext() { return peekedElement != null || underlyingIterator.hasNext(); } @@ -49,6 +50,7 @@ public boolean hasNext() { * @return the next element in the iteration. Calling this method repeatedly until the hasNext() method returns * false will return each element in the underlying collection exactly once. */ + @Override public T next() { if (peekedElement != null) { final T ret = peekedElement; @@ -72,6 +74,7 @@ public T peek() { /** * Unsupported */ + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/main/java/htsjdk/samtools/util/PeekableIterator.java b/src/main/java/htsjdk/samtools/util/PeekableIterator.java index 1587dd299..3df4c42ca 100644 --- a/src/main/java/htsjdk/samtools/util/PeekableIterator.java +++ b/src/main/java/htsjdk/samtools/util/PeekableIterator.java @@ -39,16 +39,19 @@ public PeekableIterator(Iterator iterator) { } /** Closes the underlying iterator. */ + @Override public void close() { CloserUtil.close(iterator); } /** True if there are more items, in which case both next() and peek() will return a value. */ + @Override public boolean hasNext() { return this.nextObject != null; } /** Returns the next object and advances the iterator. */ + @Override public Object next() { Object retval = this.nextObject; advance(); @@ -73,6 +76,7 @@ private void advance(){ } /** Unsupported Operation. */ + @Override public void remove() { throw new UnsupportedOperationException("Not supported: remove"); } diff --git a/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java b/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java index ef28be610..a4643db42 100644 --- a/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/PositionalOutputStream.java @@ -41,20 +41,24 @@ public PositionalOutputStream(final OutputStream out) { this.out = out; } + @Override public final void write(final byte[] bytes) throws IOException { write(bytes, 0, bytes.length); } + @Override public final void write(final byte[] bytes, final int startIndex, final int numBytes) throws IOException { position += numBytes; out.write(bytes, startIndex, numBytes); } + @Override public final void write(final int c) throws IOException { position++; out.write(c); } + @Override public final long getPosition() { return position; } @Override diff --git a/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java b/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java index b0a965ca1..0147daa35 100644 --- a/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java +++ b/src/main/java/htsjdk/samtools/util/QualityEncodingDetector.java @@ -270,6 +270,7 @@ public boolean isDeterminationAmbiguous() { } } + @Override public boolean hasNext() { // If this returns true, the head of the queue will have a next element while (!queue.isEmpty()) { @@ -281,6 +282,7 @@ public boolean hasNext() { return false; } + @Override public FastqRecord next() { if (!hasNext()) throw new NoSuchElementException(); final Iterator i = queue.poll(); @@ -289,6 +291,7 @@ public FastqRecord next() { return result; } + @Override public void remove() { throw new UnsupportedOperationException(); } diff --git a/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java b/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java index 5d173a5a4..5dd7589d7 100644 --- a/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java +++ b/src/main/java/htsjdk/samtools/util/SamRecordIntervalIteratorFactory.java @@ -107,6 +107,7 @@ private StopAfterFilteringIterator(Iterator iterator, SamRecordFilter * * @return true if the iteration has more elements. Otherwise returns false. */ + @Override public boolean hasNext() { return next != null; } @@ -117,6 +118,7 @@ public boolean hasNext() { * @return the next element in the iteration * @throws java.util.NoSuchElementException */ + @Override public SAMRecord next() { if (next == null) { throw new NoSuchElementException("Iterator has no more elements."); @@ -131,10 +133,12 @@ public SAMRecord next() { * * @throws UnsupportedOperationException */ + @Override public void remove() { throw new UnsupportedOperationException("Remove() not supported by FilteringSamIterator"); } + @Override public void close() { CloserUtil.close(iterator); } diff --git a/src/main/java/htsjdk/samtools/util/SortingCollection.java b/src/main/java/htsjdk/samtools/util/SortingCollection.java index 6babd4e35..69ce2556b 100644 --- a/src/main/java/htsjdk/samtools/util/SortingCollection.java +++ b/src/main/java/htsjdk/samtools/util/SortingCollection.java @@ -259,6 +259,7 @@ private File newTempFile() throws IOException { * Prepare to iterate through the records in order. This method may be called more than once, * but add() may not be called after this method has been called. */ + @Override public CloseableIterator iterator() { if (this.cleanedUp) { throw new IllegalStateException("Cannot call iterator() after cleanup() was called."); @@ -354,14 +355,17 @@ public void cleanup() { SortingCollection.this.comparator); } + @Override public void close() { // nothing to do } + @Override public boolean hasNext() { return this.iterationIndex < SortingCollection.this.numRecordsInRam; } + @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); @@ -372,6 +376,7 @@ public T next() { return ret; } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -409,10 +414,12 @@ public void remove() { } } + @Override public boolean hasNext() { return !this.queue.isEmpty(); } + @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); @@ -430,10 +437,12 @@ public T next() { return ret; } + @Override public void remove() { throw new UnsupportedOperationException(); } + @Override public void close() { while (!this.queue.isEmpty()) { final PeekFileRecordIterator it = this.queue.pollFirst(); @@ -464,10 +473,12 @@ public void close() { } } + @Override public boolean hasNext() { return this.currentRecord != null; } + @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); @@ -477,6 +488,7 @@ public T next() { return ret; } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -485,6 +497,7 @@ private void advance() { this.currentRecord = this.codec.decode(); } + @Override public void close() { CloserUtil.close(this.is); } @@ -505,6 +518,7 @@ public void close() { class PeekFileRecordIteratorComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(final PeekFileRecordIterator lhs, final PeekFileRecordIterator rhs) { final int result = comparator.compare(lhs.peek(), rhs.peek()); if (result == 0) return lhs.n - rhs.n; diff --git a/src/main/java/htsjdk/samtools/util/SortingLongCollection.java b/src/main/java/htsjdk/samtools/util/SortingLongCollection.java index 4cf0c367f..e75c3362e 100644 --- a/src/main/java/htsjdk/samtools/util/SortingLongCollection.java +++ b/src/main/java/htsjdk/samtools/util/SortingLongCollection.java @@ -336,6 +336,7 @@ void close() { private static class PeekFileValueIteratorComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(final PeekFileValueIterator it1, final PeekFileValueIterator it2) { if (it1.peek() < it2.peek()) { return -1; diff --git a/src/main/java/htsjdk/samtools/util/StringLineReader.java b/src/main/java/htsjdk/samtools/util/StringLineReader.java index ed383a2f2..cca3d9531 100644 --- a/src/main/java/htsjdk/samtools/util/StringLineReader.java +++ b/src/main/java/htsjdk/samtools/util/StringLineReader.java @@ -46,6 +46,7 @@ public StringLineReader(final String s) { /** * Read a line and remove the line terminator */ + @Override public String readLine() { return readLine(false); } @@ -77,6 +78,7 @@ private String readLine(final boolean includeTerminators) { /** * @return 1-based number of line most recently read */ + @Override public int getLineNumber() { return lineNumber; } @@ -86,6 +88,7 @@ public int getLineNumber() { * * @return If not eof, the next character that would be read. If eof, -1. */ + @Override public int peek() { if (curPos == theString.length()) { return -1; @@ -93,6 +96,7 @@ public int peek() { return theString.charAt(curPos); } + @Override public void close() { curPos = theString.length(); } diff --git a/src/main/java/htsjdk/samtools/util/WholeGenomeReferenceSequenceMask.java b/src/main/java/htsjdk/samtools/util/WholeGenomeReferenceSequenceMask.java index 1263285a8..b9ef975a8 100644 --- a/src/main/java/htsjdk/samtools/util/WholeGenomeReferenceSequenceMask.java +++ b/src/main/java/htsjdk/samtools/util/WholeGenomeReferenceSequenceMask.java @@ -41,6 +41,7 @@ public WholeGenomeReferenceSequenceMask(final SAMFileHeader header) { /** * @return true if the mask is set for the given sequence and position */ + @Override public boolean get(final int sequenceIndex, final int position) { if (sequenceIndex < 0) { throw new IllegalArgumentException("Negative sequence index " + sequenceIndex); @@ -55,6 +56,7 @@ public boolean get(final int sequenceIndex, final int position) { /** * @return the next pos on the given sequence >= position that is set, or -1 if there are no more set positions */ + @Override public int nextPosition(final int sequenceIndex, final int position) { if (get(sequenceIndex, position + 1)) { return position + 1; @@ -66,6 +68,7 @@ public int nextPosition(final int sequenceIndex, final int position) { /** * @return Largest sequence index for which there are set bits. */ + @Override public int getMaxSequenceIndex() { return header.getSequenceDictionary().size() - 1; } @@ -73,6 +76,7 @@ public int getMaxSequenceIndex() { /** * @return the largest position on the last sequence index */ + @Override public int getMaxPosition() { SAMSequenceRecord lastSequenceRecord = header.getSequence(getMaxSequenceIndex()); return lastSequenceRecord.getSequenceLength(); diff --git a/src/main/java/htsjdk/tribble/FeatureReader.java b/src/main/java/htsjdk/tribble/FeatureReader.java index 3471393b8..c7773a27e 100644 --- a/src/main/java/htsjdk/tribble/FeatureReader.java +++ b/src/main/java/htsjdk/tribble/FeatureReader.java @@ -32,6 +32,7 @@ public CloseableTribbleIterator iterator() throws IOException; + @Override public void close() throws IOException; public List getSequenceNames(); diff --git a/src/main/java/htsjdk/tribble/SimpleFeature.java b/src/main/java/htsjdk/tribble/SimpleFeature.java index ddc62fa10..0365dc594 100644 --- a/src/main/java/htsjdk/tribble/SimpleFeature.java +++ b/src/main/java/htsjdk/tribble/SimpleFeature.java @@ -39,14 +39,17 @@ public SimpleFeature(final String contig, final int start, final int end) { this.end = end; } + @Override public String getContig() { return contig; } + @Override public int getStart() { return start; } + @Override public int getEnd() { return end; } diff --git a/src/main/java/htsjdk/tribble/TribbleException.java b/src/main/java/htsjdk/tribble/TribbleException.java index 86202ebfb..18f1f81f8 100644 --- a/src/main/java/htsjdk/tribble/TribbleException.java +++ b/src/main/java/htsjdk/tribble/TribbleException.java @@ -54,6 +54,7 @@ public void setSource(String source) { * override the default message with ours, which attaches the source file in question * @return a string with our internal error, along with the causitive source file (or other input source) */ + @Override public String getMessage() { String ret = super.getMessage(); if ( source != null ) diff --git a/src/main/java/htsjdk/tribble/bed/FullBEDFeature.java b/src/main/java/htsjdk/tribble/bed/FullBEDFeature.java index eab568837..975777dc2 100644 --- a/src/main/java/htsjdk/tribble/bed/FullBEDFeature.java +++ b/src/main/java/htsjdk/tribble/bed/FullBEDFeature.java @@ -39,6 +39,7 @@ public FullBEDFeature(String chr, int start, int end) { } + @Override public java.util.List getExons() { return exons; } diff --git a/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java b/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java index 77a030fa9..4a6416867 100644 --- a/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java +++ b/src/main/java/htsjdk/tribble/bed/SimpleBEDFeature.java @@ -56,14 +56,17 @@ public String getContig() { return chr; } + @Override public int getStart() { return start; } + @Override public int getEnd() { return end; } + @Override public Strand getStrand() { return strand; } @@ -84,6 +87,7 @@ public void setEnd(int end) { this.end = end; } + @Override public String getType() { return type; } @@ -92,6 +96,7 @@ public void setType(String type) { this.type = type; } + @Override public Color getColor() { return color; } @@ -100,6 +105,7 @@ public void setColor(Color color) { this.color = color; } + @Override public String getDescription() { return description; } @@ -108,6 +114,7 @@ public void setDescription(String description) { this.description = description; } + @Override public String getName() { return name; } @@ -116,6 +123,7 @@ public void setName(String name) { this.name = name; } + @Override public float getScore() { return score; } @@ -124,6 +132,7 @@ public void setScore(float score) { this.score = score; } + @Override public String getLink() { return link; } @@ -134,6 +143,7 @@ public void setLink(String link) { final static List emptyExonList = new ArrayList(); + @Override public java.util.List getExons() { return emptyExonList; } diff --git a/src/main/java/htsjdk/tribble/index/AbstractIndex.java b/src/main/java/htsjdk/tribble/index/AbstractIndex.java index 47e31ccef..5ae5492d6 100644 --- a/src/main/java/htsjdk/tribble/index/AbstractIndex.java +++ b/src/main/java/htsjdk/tribble/index/AbstractIndex.java @@ -101,6 +101,7 @@ public boolean hasMD5() { * @param obj * @return true if this and obj are 'effectively' equivalent data structures. */ + @Override public boolean equalsIgnoreProperties(final Object obj) { if (this == obj) return true; if (!(obj instanceof AbstractIndex)) { @@ -194,6 +195,7 @@ protected void validateIndexHeader(final int indexType, final LittleEndianInputS * * @return true if we're up to date, false otherwise */ + @Override public boolean isCurrentVersion() { return version == VERSION; } @@ -226,6 +228,7 @@ public void setMD5(final String md5) { this.indexedFileMD5 = md5; } + @Override public boolean containsChromosome(final String chr) { return chrIndices.containsKey(chr); } @@ -306,10 +309,12 @@ private void readSequenceDictionary(final LittleEndianInputStream dis) throws IO } } + @Override public List getSequenceNames() { return new ArrayList(chrIndices.keySet()); } + @Override public List getBlocks(final String chr, final int start, final int end) { return getChrIndex(chr).getBlocks(start, end); } @@ -332,6 +337,7 @@ private final ChrIndex getChrIndex(final String chr) { } } + @Override public void write(final LittleEndianOutputStream stream) throws IOException { writeHeader(stream); @@ -418,10 +424,12 @@ protected String statsSummary() { return String.format("%12d blocks (%12d empty (%.2f%%))", stats.total, stats.empty, (100.0 * stats.empty) / stats.total); } + @Override public void addProperty(final String key, final String value) { properties.put(key, value); } + @Override public void addProperties(final Map properties) { this.properties.putAll(properties); } @@ -431,6 +439,7 @@ public void addProperties(final Map properties) { * * @return the mapping of values as an unmodifiable map */ + @Override public Map getProperties() { return Collections.unmodifiableMap(properties); } diff --git a/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java b/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java index 52153a51f..3552fbb4f 100644 --- a/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java @@ -65,6 +65,7 @@ public DynamicIndexCreator(final File inputFile, final IndexFactory.IndexBalance creators = getIndexCreators(inputFile,iba); } + @Override public Index finalizeIndex(final long finalFilePosition) { // finalize all of the indexes // return the score of the indexes we've generated @@ -123,6 +124,7 @@ public Index finalizeIndex(final long finalFilePosition) { } + @Override public void addFeature(final Feature f, final long filePosition) { // protected static Map createIndex(FileBasedFeatureIterator iterator, Map creators, IndexBalanceApproach iba) { // feed each feature to the indexes we've created diff --git a/src/main/java/htsjdk/tribble/index/IndexFactory.java b/src/main/java/htsjdk/tribble/index/IndexFactory.java index f53d9a82d..928236620 100644 --- a/src/main/java/htsjdk/tribble/index/IndexFactory.java +++ b/src/main/java/htsjdk/tribble/index/IndexFactory.java @@ -475,10 +475,12 @@ private PositionalBufferedStream initStream(final File inputFile, final long ski } } + @Override public boolean hasNext() { return nextFeature != null; } + @Override public Feature next() { final Feature ret = nextFeature; readNextFeature(); @@ -488,6 +490,7 @@ public Feature next() { /** * @throws UnsupportedOperationException */ + @Override public void remove() { throw new UnsupportedOperationException("We cannot remove"); } diff --git a/src/main/java/htsjdk/tribble/index/interval/Interval.java b/src/main/java/htsjdk/tribble/index/interval/Interval.java index 9d4787774..6c0e648ee 100644 --- a/src/main/java/htsjdk/tribble/index/interval/Interval.java +++ b/src/main/java/htsjdk/tribble/index/interval/Interval.java @@ -76,6 +76,7 @@ public int hashCode() { } + @Override public int compareTo(Object o) { Interval other = (Interval) o; if (this.start < other.start) diff --git a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java index e826edaa7..01219040c 100644 --- a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java @@ -62,6 +62,7 @@ public IntervalIndexCreator(final File inputFile) { this(inputFile, DEFAULT_FEATURE_COUNT); } + @Override public void addFeature(final Feature feature, final long filePosition) { // if we don't have a chrIndex yet, or if the last one was for the previous contig, create a new one if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getContig())) { @@ -105,6 +106,7 @@ private void addIntervalsToLastChr(final long currentPos) { * @param finalFilePosition the final file position, for indexes that have to close out with the final position * @return a Tree Index */ + @Override public Index finalizeIndex(final long finalFilePosition) { final IntervalTreeIndex featureIndex = new IntervalTreeIndex(inputFile.getAbsolutePath()); // dump the remaining bins to the index diff --git a/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java b/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java index 055888ecc..9a4206ebf 100644 --- a/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java +++ b/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java @@ -119,6 +119,7 @@ public ChrIndex(final String name) { tree = new IntervalTree(); } + @Override public String getName() { return name; } @@ -127,11 +128,13 @@ public void insert(final Interval iv) { tree.insert(iv); } + @Override public List getBlocks() { return null; } + @Override public List getBlocks(final int start, final int end) { // Get intervals and build blocks list @@ -148,6 +151,7 @@ public void insert(final Interval iv) { // Sort blocks by start position Arrays.sort(blocks, new Comparator() { + @Override public int compare(final Block b1, final Block b2) { // this is a little cryptic because the normal method (b1.getStartPosition() - b2.getStartPosition()) wraps in int space and we incorrectly sort the blocks in extreme cases return b1.getStartPosition() - b2.getStartPosition() < 1 ? -1 : (b1.getStartPosition() - b2.getStartPosition() > 1 ? 1 : 0); @@ -175,6 +179,7 @@ public void printTree() { System.out.println(tree.toString()); } + @Override public void write(final LittleEndianOutputStream dos) throws IOException { dos.writeString(name); @@ -190,6 +195,7 @@ public void write(final LittleEndianOutputStream dos) throws IOException { } + @Override public void read(final LittleEndianInputStream dis) throws IOException { tree = new IntervalTree(); diff --git a/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java b/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java index 4f4d9100e..5047ab61a 100644 --- a/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java +++ b/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java @@ -101,6 +101,7 @@ public LinearIndex(final InputStream inputStream) throws IOException { read(dis); } + @Override public boolean isCurrentVersion() { if (!super.isCurrentVersion()) return false; @@ -117,6 +118,7 @@ protected int getType() { return INDEX_TYPE; } + @Override public List getSequenceNames() { return (chrIndices == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(new ArrayList(chrIndices.keySet()))); @@ -173,6 +175,7 @@ public ChrIndex() { this.nFeatures = 0; } + @Override public String getName() { return name; } @@ -186,10 +189,12 @@ public int getNBlocks() { return blocks.size(); } + @Override public List getBlocks() { return blocks; } + @Override public List getBlocks(final int start, final int end) { if (blocks.isEmpty()) { return Collections.emptyList(); @@ -231,6 +236,7 @@ public void incrementFeatureCount() { this.nFeatures++; } + @Override public void write(final LittleEndianOutputStream dos) throws IOException { // Chr name, binSize, # bins, longest feature @@ -253,6 +259,7 @@ public void write(final LittleEndianOutputStream dos) throws IOException { dos.writeLong(pos + size); } + @Override public void read(final LittleEndianInputStream dis) throws IOException { name = dis.readString(); binWidth = dis.readInt(); diff --git a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java index 1158fdfd3..daad6cce6 100644 --- a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java @@ -64,6 +64,7 @@ public LinearIndexCreator(final File inputFile) { * @param feature the feature, from which we use the contig, start, and stop * @param filePosition the position of the file at the BEGINNING of the current feature */ + @Override public void addFeature(final Feature feature, final long filePosition) { // fi we don't have a chrIndex yet, or if the last one was for the previous contig, create a new one if (chrList.isEmpty() || !chrList.getLast().getName().equals(feature.getContig())) { @@ -97,6 +98,7 @@ public void addFeature(final Feature feature, final long filePosition) { * @param finalFilePosition the final file position, for indexes that have to close out with the final position * @return an Index object */ + @Override public Index finalizeIndex(final long finalFilePosition) { if (finalFilePosition == 0) throw new IllegalArgumentException("finalFilePosition != 0, -> " + finalFilePosition); diff --git a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java index 8f06205a7..ad66a17c7 100644 --- a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java +++ b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java @@ -57,6 +57,7 @@ public AsciiLineReader(final PositionalBufferedStream is) { /** * @return The position of the InputStream */ + @Override public long getPosition(){ if(is == null){ throw new TribbleException("getPosition() called but no default stream was provided to the class on creation"); @@ -115,6 +116,7 @@ public final String readLine(final PositionalBufferedStream stream) throws IOExc * * @return */ + @Override public final String readLine() throws IOException{ if ( is == null ){ throw new TribbleException("readLine() called without an explicit stream argument but no default stream was provided to the class on creation"); diff --git a/src/main/java/htsjdk/tribble/readers/LineReader.java b/src/main/java/htsjdk/tribble/readers/LineReader.java index 969b6b511..2782afc96 100644 --- a/src/main/java/htsjdk/tribble/readers/LineReader.java +++ b/src/main/java/htsjdk/tribble/readers/LineReader.java @@ -39,5 +39,6 @@ public String readLine() throws IOException; + @Override public void close(); } diff --git a/src/main/java/htsjdk/tribble/readers/LongLineBufferedReader.java b/src/main/java/htsjdk/tribble/readers/LongLineBufferedReader.java index 5ca8e8d13..dbb659343 100644 --- a/src/main/java/htsjdk/tribble/readers/LongLineBufferedReader.java +++ b/src/main/java/htsjdk/tribble/readers/LongLineBufferedReader.java @@ -153,6 +153,7 @@ private void fill() throws IOException { * end of the stream has been reached * @throws IOException If an I/O error occurs */ + @Override public int read() throws IOException { synchronized (lock) { ensureOpen(); @@ -250,6 +251,7 @@ private int read1(char[] cbuf, int off, int len) throws IOException { * stream has been reached * @throws IOException If an I/O error occurs */ + @Override public int read(char cbuf[], int off, int len) throws IOException { synchronized (lock) { ensureOpen(); @@ -362,6 +364,7 @@ public String readLine() throws IOException { * @throws IllegalArgumentException If n is negative. * @throws IOException If an I/O error occurs */ + @Override public long skip(long n) throws IOException { if (n < 0L) { throw new IllegalArgumentException("skip value is negative"); @@ -401,6 +404,7 @@ public long skip(long n) throws IOException { * * @throws IOException If an I/O error occurs */ + @Override public boolean ready() throws IOException { synchronized (lock) { ensureOpen(); @@ -429,6 +433,7 @@ public boolean ready() throws IOException { /** * Tells whether this stream supports the mark() operation, which it does. */ + @Override public boolean markSupported() { return true; } @@ -448,6 +453,7 @@ public boolean markSupported() { * @throws IllegalArgumentException If readAheadLimit is < 0 * @throws IOException If an I/O error occurs */ + @Override public void mark(int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); @@ -466,6 +472,7 @@ public void mark(int readAheadLimit) throws IOException { * @throws IOException If the stream has never been marked, * or if the mark has been invalidated */ + @Override public void reset() throws IOException { synchronized (lock) { ensureOpen(); @@ -478,6 +485,7 @@ public void reset() throws IOException { } } + @Override public void close() throws IOException { synchronized (lock) { if (in == null) diff --git a/src/main/java/htsjdk/tribble/readers/PositionalBufferedStream.java b/src/main/java/htsjdk/tribble/readers/PositionalBufferedStream.java index ac642df98..4d7ae05eb 100644 --- a/src/main/java/htsjdk/tribble/readers/PositionalBufferedStream.java +++ b/src/main/java/htsjdk/tribble/readers/PositionalBufferedStream.java @@ -48,6 +48,7 @@ public PositionalBufferedStream(final InputStream is, final int bufferSize) { nextChar = nChars = 0; } + @Override public final long getPosition() { return position; } @@ -129,6 +130,7 @@ private final int fill() throws IOException { return nChars; } + @Override public final long skip(final long nBytes) throws IOException { long remainingToSkip = nBytes; @@ -156,6 +158,7 @@ public final long skip(final long nBytes) throws IOException { return actuallySkipped; } + @Override public final void close() { try { is.close(); diff --git a/src/main/java/htsjdk/tribble/readers/TabixIteratorLineReader.java b/src/main/java/htsjdk/tribble/readers/TabixIteratorLineReader.java index 49b6f0cfd..2a04725e7 100644 --- a/src/main/java/htsjdk/tribble/readers/TabixIteratorLineReader.java +++ b/src/main/java/htsjdk/tribble/readers/TabixIteratorLineReader.java @@ -40,6 +40,7 @@ public TabixIteratorLineReader(TabixReader.Iterator iterator) { this.iterator = iterator; } + @Override public String readLine() { try { return iterator != null ? iterator.next() : null; @@ -48,6 +49,7 @@ public String readLine() { } } + @Override public void close() { // Ignore - } diff --git a/src/main/java/htsjdk/tribble/readers/TabixReader.java b/src/main/java/htsjdk/tribble/readers/TabixReader.java index b4882e543..244fcd5d2 100644 --- a/src/main/java/htsjdk/tribble/readers/TabixReader.java +++ b/src/main/java/htsjdk/tribble/readers/TabixReader.java @@ -78,6 +78,7 @@ public TPair64(final TPair64 p) { v = p.v; } + @Override public int compareTo(final TPair64 p) { return u == p.u ? 0 : ((u < p.u) ^ (u < 0) ^ (p.u < 0)) ? -1 : 1; // unsigned 64-bit comparison } diff --git a/src/main/java/htsjdk/tribble/util/HTTPHelper.java b/src/main/java/htsjdk/tribble/util/HTTPHelper.java index 90e622859..1e89bc26b 100644 --- a/src/main/java/htsjdk/tribble/util/HTTPHelper.java +++ b/src/main/java/htsjdk/tribble/util/HTTPHelper.java @@ -57,6 +57,7 @@ public static synchronized void setProxy(Proxy p) { proxy = p; } + @Override public URL getUrl() { return url; } @@ -65,6 +66,7 @@ public URL getUrl() { * @return content length of the resource * @throws IOException */ + @Override public long getContentLength() throws IOException { HttpURLConnection con = null; @@ -84,6 +86,7 @@ public long getContentLength() throws IOException { } + @Override public InputStream openInputStream() throws IOException { HttpURLConnection connection = openConnection(); @@ -99,6 +102,7 @@ public InputStream openInputStream() throws IOException { * @return * @throws IOException */ + @Override @Deprecated public InputStream openInputStreamForRange(long start, long end) throws IOException { @@ -118,6 +122,7 @@ private HttpURLConnection openConnection() throws IOException { return connection; } + @Override public boolean exists() throws IOException { HttpURLConnection con = null; try { diff --git a/src/main/java/htsjdk/tribble/util/LittleEndianOutputStream.java b/src/main/java/htsjdk/tribble/util/LittleEndianOutputStream.java index 9bec07188..eab2f8785 100644 --- a/src/main/java/htsjdk/tribble/util/LittleEndianOutputStream.java +++ b/src/main/java/htsjdk/tribble/util/LittleEndianOutputStream.java @@ -25,11 +25,13 @@ public LittleEndianOutputStream(OutputStream out) { super(out); } + @Override public void write(int b) throws IOException { out.write(b); written++; } + @Override public void write(byte[] data, int offset, int length) throws IOException { out.write(data, offset, length); diff --git a/src/main/java/htsjdk/tribble/util/TabixUtils.java b/src/main/java/htsjdk/tribble/util/TabixUtils.java index aa365cd58..5ae9f8afd 100644 --- a/src/main/java/htsjdk/tribble/util/TabixUtils.java +++ b/src/main/java/htsjdk/tribble/util/TabixUtils.java @@ -55,6 +55,7 @@ public TPair64(final TPair64 p) { v = p.v; } + @Override public int compareTo(final TPair64 p) { return u == p.u ? 0 : ((u < p.u) ^ (u < 0) ^ (p.u < 0)) ? -1 : 1; // unsigned 64-bit comparison } diff --git a/src/main/java/htsjdk/variant/variantcontext/Allele.java b/src/main/java/htsjdk/variant/variantcontext/Allele.java index 44fc6aaa7..71aa20126 100644 --- a/src/main/java/htsjdk/variant/variantcontext/Allele.java +++ b/src/main/java/htsjdk/variant/variantcontext/Allele.java @@ -523,6 +523,7 @@ public static Allele getMatchingAllele(final Collection allAlleles, fina return null; // couldn't find anything } + @Override public int compareTo(final Allele other) { if ( isReference() && other.isNonReference() ) return -1; diff --git a/src/main/java/htsjdk/variant/variantcontext/FastGenotype.java b/src/main/java/htsjdk/variant/variantcontext/FastGenotype.java index 665e67242..495ba4192 100644 --- a/src/main/java/htsjdk/variant/variantcontext/FastGenotype.java +++ b/src/main/java/htsjdk/variant/variantcontext/FastGenotype.java @@ -154,6 +154,7 @@ protected FastGenotype(final String sampleName, // // --------------------------------------------------------------------------------------------------------- + @Override public Map getExtendedAttributes() { return extendedAttributes; } diff --git a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java index 33ec59514..c4664b08a 100644 --- a/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java +++ b/src/main/java/htsjdk/variant/variantcontext/JEXLMap.java @@ -78,6 +78,7 @@ public JEXLMap(final Collection jexlCollection, final VariantCon * @throws IllegalArgumentException when {@code key} is {@code null} or * when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ + @Override public Boolean get(Object key) { if (key == null) { throw new IllegalArgumentException("Query key is null"); @@ -101,8 +102,10 @@ public Boolean get(Object key) { * @param o the key * @return true if we have a value for that key */ + @Override public boolean containsKey(Object o) { return jexl.containsKey(o); } + @Override public Set keySet() { return jexl.keySet(); } @@ -119,6 +122,7 @@ public Boolean get(Object key) { * * @throws IllegalArgumentException when any of the JexlVCMatchExp (i.e. keys) contains invalid Jexl expressions. */ + @Override public Collection values() { for (final JexlVCMatchExp exp : jexl.keySet()) { jexl.computeIfAbsent(exp, k -> evaluateExpression(exp)); @@ -129,16 +133,20 @@ public Boolean get(Object key) { /** * @return the number of keys, i.e. {@link JexlVCMatchExp}'s held by this mapping. */ + @Override public int size() { return jexl.size(); } + @Override public boolean isEmpty() { return this.jexl.isEmpty(); } + @Override public Boolean put(JexlVCMatchExp jexlVCMatchExp, Boolean aBoolean) { return jexl.put(jexlVCMatchExp, aBoolean); } + @Override public void putAll(Map map) { jexl.putAll(map); } @@ -207,21 +215,25 @@ private JexlContext createContext() { // this doesn't make much sense to implement, boolean doesn't offer too much variety to deal // with evaluating every key in the internal map. + @Override public boolean containsValue(Object o) { throw new UnsupportedOperationException("containsValue() not supported on a JEXLMap"); } // this doesn't make much sense + @Override public Boolean remove(Object o) { throw new UnsupportedOperationException("remove() not supported on a JEXLMap"); } + @Override public Set> entrySet() { throw new UnsupportedOperationException("entrySet() not supported on a JEXLMap"); } // nope + @Override public void clear() { throw new UnsupportedOperationException("clear() not supported on a JEXLMap"); } diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java index 55825fb4d..6def89ef9 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantContext.java @@ -1663,6 +1663,7 @@ public String getContig() { * underlying vcf file, VariantContexts representing the same biological event may have different start positions depending on the * specifics of the vcf file they are derived from */ + @Override public int getStart() { return (int)start; } @@ -1673,6 +1674,7 @@ public int getStart() { * For VariantContexts with a single alternate allele, if that allele is an insertion, the end position will be on the reference base * before the insertion event. If the single alt allele is a deletion, the end will be on the final deleted reference base. */ + @Override public int getEnd() { return (int)stop; } diff --git a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java index 34cde3395..012586381 100644 --- a/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java +++ b/src/main/java/htsjdk/variant/variantcontext/VariantJEXLContext.java @@ -76,6 +76,7 @@ public VariantJEXLContext(VariantContext vc) { this.vc = vc; } + @Override public Object get(String name) { Object result = null; if ( attributes.containsKey(name) ) { // dynamic resolution of name -> value via map @@ -89,6 +90,7 @@ public Object get(String name) { return result; } + @Override public boolean has(String name) { return get(name) != null; } @@ -96,6 +98,7 @@ public boolean has(String name) { /** * @throws UnsupportedOperationException */ + @Override public void set(String name, Object value) { throw new UnsupportedOperationException("remove() not supported on a VariantJEXLContext"); } diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/AsyncVariantContextWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/AsyncVariantContextWriter.java index 4604316b2..481ab871e 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/AsyncVariantContextWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/AsyncVariantContextWriter.java @@ -39,10 +39,12 @@ public AsyncVariantContextWriter(final VariantContextWriter out, final int queue @Override protected final String getThreadNamePrefix() { return "VariantContextWriterThread-"; } + @Override public void add(final VariantContext vc) { write(vc); } + @Override public void writeHeader(final VCFHeader header) { this.underlyingWriter.writeHeader(header); } diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java index f9dd458d0..9582e00ab 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/BCF2FieldWriter.java @@ -231,6 +231,7 @@ public FTGenotypesWriter(final VCFHeader header, final BCF2FieldEncoder fieldEnc super(header, fieldEncoder); } + @Override public void addGenotype(final BCF2Encoder encoder, final VariantContext vc, final Genotype g) throws IOException { final String fieldValue = g.getFilters(); getFieldEncoder().encodeValue(encoder, fieldValue, encodingType, nValuesPerGenotype); diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java index 6a77f6b3b..eece2d1ac 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/IndexingVariantContextWriter.java @@ -126,11 +126,13 @@ public String getStreamName() { return name; } + @Override public abstract void writeHeader(VCFHeader header); /** * attempt to close the VCF file */ + @Override public void close() { try { // close the underlying output stream @@ -161,6 +163,7 @@ public SAMSequenceDictionary getRefDict() { * * @param vc the Variant Context object */ + @Override public void add(final VariantContext vc) { // if we are doing on the fly indexing, add the record ***before*** we write any bytes if ( indexer != null ) diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriter.java index 21854827b..edc70c4ff 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriter.java @@ -51,6 +51,7 @@ public SortingVariantContextWriter(VariantContextWriter innerWriter, int maxCach this(innerWriter, maxCachingStartDistance, false); // by default, don't own inner } + @Override protected void noteCurrentRecord(VariantContext vc) { super.noteCurrentRecord(vc); // first, check for errors diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java index 690a7813c..7d9273f97 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/SortingVariantContextWriterBase.java @@ -186,6 +186,7 @@ private synchronized void emitRecords(boolean emitUnsafe) { private static class VariantContextComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; + @Override public int compare(VCFRecord r1, VCFRecord r2) { return r1.vc.getStart() - r2.vc.getStart(); } diff --git a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriter.java b/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriter.java index 187ff17c3..843901a20 100644 --- a/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriter.java +++ b/src/main/java/htsjdk/variant/variantcontext/writer/VariantContextWriter.java @@ -40,6 +40,7 @@ /** * attempt to close the VCF file */ + @Override public void close(); /** diff --git a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java index 04887aeea..8a55a1946 100644 --- a/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java +++ b/src/main/java/htsjdk/variant/vcf/AbstractVCFCodec.java @@ -257,6 +257,7 @@ public Feature decodeLoc(String line) { * @param line the line * @return a VariantContext */ + @Override public VariantContext decode(String line) { return decodeLine(line, true); } @@ -367,6 +368,7 @@ else if ( parts[2].equals(VCFConstants.EMPTY_ID_FIELD) ) * get the name of this codec * @return our set name */ + @Override public String getName() { return name; } @@ -375,6 +377,7 @@ public String getName() { * set the name of this codec * @param name new name */ + @Override public void setName(String name) { this.name = name; } diff --git a/src/main/java/htsjdk/variant/vcf/VCF3Codec.java b/src/main/java/htsjdk/variant/vcf/VCF3Codec.java index 5f4f48ec5..e9ca3abdf 100644 --- a/src/main/java/htsjdk/variant/vcf/VCF3Codec.java +++ b/src/main/java/htsjdk/variant/vcf/VCF3Codec.java @@ -56,6 +56,7 @@ * @param reader the line reader to take header lines from * @return the number of header lines */ + @Override public Object readActualHeader(final LineIterator reader) { final List headerStrings = new ArrayList(); @@ -97,6 +98,7 @@ else if (line.startsWith(VCFHeader.HEADER_INDICATOR)) { * @param filterString the string to parse * @return a set of the filters applied */ + @Override protected List parseFilters(String filterString) { // null for unfiltered diff --git a/src/main/java/htsjdk/variant/vcf/VCFCodec.java b/src/main/java/htsjdk/variant/vcf/VCFCodec.java index 89d68813e..6e5d3b7d2 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFCodec.java +++ b/src/main/java/htsjdk/variant/vcf/VCFCodec.java @@ -125,6 +125,7 @@ else if (line.startsWith(VCFHeader.HEADER_INDICATOR)) { * @param filterString the string to parse * @return a set of the filters applied or null if filters were not applied to the record (e.g. as per the missing value in a VCF) */ + @Override protected List parseFilters(final String filterString) { // null for unfiltered if ( filterString.equals(VCFConstants.UNFILTERED) ) diff --git a/src/main/java/htsjdk/variant/vcf/VCFCompoundHeaderLine.java b/src/main/java/htsjdk/variant/vcf/VCFCompoundHeaderLine.java index 48e0cdf0d..4d8c3447f 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFCompoundHeaderLine.java +++ b/src/main/java/htsjdk/variant/vcf/VCFCompoundHeaderLine.java @@ -57,6 +57,7 @@ private VCFHeaderLineType type; // access methods + @Override public String getID() { return name; } public String getDescription() { return description; } public VCFHeaderLineType getType() { return type; } @@ -221,6 +222,7 @@ private void validate() { * make a string representation of this header line * @return a string representation */ + @Override protected String toStringEncoding() { Map map = new LinkedHashMap(); map.put("ID", name); diff --git a/src/main/java/htsjdk/variant/vcf/VCFFileReader.java b/src/main/java/htsjdk/variant/vcf/VCFFileReader.java index 9024f34fc..b43935880 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFFileReader.java +++ b/src/main/java/htsjdk/variant/vcf/VCFFileReader.java @@ -129,7 +129,8 @@ public VCFHeader getFileHeader() { } /** Returns an iterator over all records in this VCF/BCF file. */ - public CloseableIterator iterator() { + @Override + public CloseableIterator iterator() { try { return reader.iterator(); } catch (final IOException ioe) { throw new TribbleException("Could not create an iterator from a feature reader.", ioe); @@ -144,7 +145,8 @@ public VCFHeader getFileHeader() { } } - public void close() { + @Override + public void close() { try { this.reader.close(); } catch (final IOException ioe) { throw new TribbleException("Could not close a variant context feature reader.", ioe); diff --git a/src/main/java/htsjdk/variant/vcf/VCFHeaderLine.java b/src/main/java/htsjdk/variant/vcf/VCFHeaderLine.java index c4c1e3bdf..ce12c4273 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFHeaderLine.java +++ b/src/main/java/htsjdk/variant/vcf/VCFHeaderLine.java @@ -127,6 +127,7 @@ public int hashCode() { return result; } + @Override public int compareTo(Object other) { return toString().compareTo(other.toString()); } diff --git a/src/main/java/htsjdk/variant/vcf/VCFHeaderLineTranslator.java b/src/main/java/htsjdk/variant/vcf/VCFHeaderLineTranslator.java index 071d815ca..3ac72b28c 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFHeaderLineTranslator.java +++ b/src/main/java/htsjdk/variant/vcf/VCFHeaderLineTranslator.java @@ -67,6 +67,7 @@ * @param valueLine the line * @return a mapping of the tags parsed out */ + @Override public Map parseLine(String valueLine, List expectedTagOrder) { // our return map Map ret = new LinkedHashMap(); @@ -145,6 +146,7 @@ class VCF3Parser implements VCFLineParser { + @Override public Map parseLine(String valueLine, List expectedTagOrder) { // our return map Map ret = new LinkedHashMap(); diff --git a/src/main/java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java b/src/main/java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java index a5da687e6..1c36f9e95 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java +++ b/src/main/java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java @@ -92,6 +92,7 @@ protected void initialize(String name, Map genericFields) { this.genericFields.putAll(genericFields); } + @Override protected String toStringEncoding() { Map map = new LinkedHashMap(); map.put("ID", name); @@ -121,6 +122,7 @@ public int hashCode() { return result; } + @Override public String getID() { return name; } diff --git a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java index 18c10c70b..067f853ab 100644 --- a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java +++ b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java @@ -424,18 +424,22 @@ SamReader newFileReader() { } class ProgramGroupAdapter extends GroupAdapter { + @Override String getGroupId(AbstractSAMHeaderRecord group) { return ((SAMProgramRecord) group).getProgramGroupId(); } + @Override List getGroups(SAMFileHeader header) { return header.getProgramRecords(); } + @Override String getTagName() { return SAMTag.PG.toString(); } + @Override List createGroups(final String[] groupIds) { final List readers = new ArrayList(); for (final String groupId : groupIds) { @@ -457,36 +461,44 @@ String getTagName() { return fileHeaderMerger.getMergedHeader().getProgramRecords(); } + @Override void setAttribute(AbstractSAMHeaderRecord group, String value) { ((SAMProgramRecord) group).setCommandLine(value); } + @Override AbstractSAMHeaderRecord newGroup(String id) { return new SAMProgramRecord(id); } + @Override void setBuilderGroup(SAMRecordSetBuilder builder, AbstractSAMHeaderRecord group) { builder.setProgramRecord((SAMProgramRecord) group); } + @Override boolean equivalent(AbstractSAMHeaderRecord group1, AbstractSAMHeaderRecord group2) { return ((SAMProgramRecord) group1).equivalent((SAMProgramRecord) group2); } } class ReadGroupAdapter extends GroupAdapter { + @Override String getGroupId(AbstractSAMHeaderRecord group) { return ((SAMReadGroupRecord) group).getReadGroupId(); } + @Override List getGroups(SAMFileHeader header) { return header.getReadGroups(); } + @Override String getTagName() { return SAMTag.RG.toString(); } + @Override List createGroups(final String[] groupIds) { final List readers = new ArrayList(); @@ -507,20 +519,24 @@ String getTagName() { return fileHeaderMerger.getMergedHeader().getReadGroups(); } + @Override void setAttribute(AbstractSAMHeaderRecord group, String value) { ((SAMReadGroupRecord) group).setPlatformUnit(value); } + @Override AbstractSAMHeaderRecord newGroup(String id) { SAMReadGroupRecord group = new SAMReadGroupRecord(id); group.setAttribute(SAMTag.SM.name(), id); return group; } + @Override void setBuilderGroup(SAMRecordSetBuilder builder, AbstractSAMHeaderRecord group) { builder.setReadGroup((SAMReadGroupRecord) group); } + @Override boolean equivalent(AbstractSAMHeaderRecord group1, AbstractSAMHeaderRecord group2) { return ((SAMReadGroupRecord) group1).equivalent((SAMReadGroupRecord) group2); } diff --git a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java index 43d37bdd3..5b86c8a59 100644 --- a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java @@ -59,6 +59,7 @@ public int inflate(byte[] b, int off, int len) throws java.util.zip.DataFormatEx } } final InflaterFactory myInflaterFactory = new InflaterFactory() { + @Override public Inflater makeInflater(final boolean gzipCompatible) { return new MyInflater(gzipCompatible); } @@ -162,11 +163,13 @@ public void queryIntervalIssue76(final String sequenceName, final int start, fin int samRecordsCreated; int bamRecordsCreated; + @Override public SAMRecord createSAMRecord(final SAMFileHeader header) { ++samRecordsCreated; return super.createSAMRecord(header); } + @Override public BAMRecord createBAMRecord(final SAMFileHeader header, final int referenceSequenceIndex, final int alignmentStart, final short readNameLength, final short mappingQuality, final int indexingBin, final int cigarLen, final int flags, final int readLen, final int mateReferenceSequenceIndex, final int mateAlignmentStart, final int insertSize, final byte[] variableLengthBlock) { ++bamRecordsCreated; return super.createBAMRecord(header, referenceSequenceIndex, alignmentStart, readNameLength, mappingQuality, indexingBin, cigarLen, flags, readLen, mateReferenceSequenceIndex, mateAlignmentStart, insertSize, variableLengthBlock); diff --git a/src/test/java/htsjdk/samtools/SamReaderTest.java b/src/test/java/htsjdk/samtools/SamReaderTest.java index 02364f318..5af88214e 100644 --- a/src/test/java/htsjdk/samtools/SamReaderTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderTest.java @@ -103,11 +103,13 @@ public void CRAMNoIndexTest(final String inputFile, final String referenceFile) int samRecordsCreated; int bamRecordsCreated; + @Override public SAMRecord createSAMRecord(final SAMFileHeader header) { ++samRecordsCreated; return super.createSAMRecord(header); } + @Override public BAMRecord createBAMRecord(final SAMFileHeader header, final int referenceSequenceIndex, final int alignmentStart, final short readNameLength, final short mappingQuality, final int indexingBin, final int cigarLen, final int flags, final int readLen, final int mateReferenceSequenceIndex, final int mateAlignmentStart, final int insertSize, final byte[] variableLengthBlock) { ++bamRecordsCreated; return super.createBAMRecord(header, referenceSequenceIndex, alignmentStart, readNameLength, mappingQuality, indexingBin, cigarLen, flags, readLen, mateReferenceSequenceIndex, mateAlignmentStart, insertSize, variableLengthBlock); diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index 4ce0b7a29..e577ed876 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -237,26 +237,32 @@ public void testNmFlagValidation() throws IOException { final Histogram results = executeValidation(samBuilder.getSamReader(), new ReferenceSequenceFile() { private int index = 0; + @Override public SAMSequenceDictionary getSequenceDictionary() { return null; } + @Override public ReferenceSequence nextSequence() { final byte[] bases = new byte[10000]; Arrays.fill(bases, (byte) 'A'); return new ReferenceSequence("foo", index++, bases); } + @Override public void reset() { this.index = 0; } + @Override public boolean isIndexed() { return false; } + @Override public ReferenceSequence getSequence(final String contig) { throw new UnsupportedOperationException(); } + @Override public ReferenceSequence getSubsequenceAt(final String contig, final long start, final long stop) { throw new UnsupportedOperationException(); } diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java index f90af4b01..69d72565f 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java @@ -181,6 +181,7 @@ public int deflate(byte[] b, int off, int len) { } final DeflaterFactory myDeflaterFactory= new DeflaterFactory(){ + @Override public Deflater makeDeflater(final int compressionLevel, final boolean gzipCompatible) { return new MyDeflater(compressionLevel, gzipCompatible); } diff --git a/src/test/java/htsjdk/samtools/util/DiskBackedQueueTest.java b/src/test/java/htsjdk/samtools/util/DiskBackedQueueTest.java index 88b05e2b7..95966520b 100644 --- a/src/test/java/htsjdk/samtools/util/DiskBackedQueueTest.java +++ b/src/test/java/htsjdk/samtools/util/DiskBackedQueueTest.java @@ -50,7 +50,9 @@ }; } + @Override @BeforeMethod void setup() { resetTmpDir(); } + @Override @AfterMethod void tearDown() { resetTmpDir(); } /** @@ -59,6 +61,7 @@ * @param numStringsToGenerate * @param maxRecordsInRam */ + @Override @Test(dataProvider = "diskBackedQueueProvider") public void testPositive(final String testName, final int numStringsToGenerate, final int maxRecordsInRam) { final String[] strings = new String[numStringsToGenerate]; diff --git a/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java b/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java index 1ec928d3b..dc9e063cd 100644 --- a/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java +++ b/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java @@ -130,19 +130,23 @@ private void assertIteratorEqualsList(final String[] strings, final Iterator iterator() { return this; } + @Override public boolean hasNext() { return numElementsGenerated < numElementsToGenerate; } + @Override public String next() { ++numElementsGenerated; return Integer.toString(random.nextInt()); } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -150,6 +154,7 @@ public void remove() { static class StringComparator implements Comparator { + @Override public int compare(final String s, final String s1) { return s.compareTo(s1); } @@ -160,6 +165,7 @@ public int compare(final String s, final String s1) { OutputStream os; InputStream is; + @Override public SortingCollection.Codec clone() { return new StringCodec(); } @@ -169,6 +175,7 @@ public int compare(final String s, final String s1) { * * @param os */ + @Override public void setOutputStream(final OutputStream os) { this.os = os; } @@ -178,6 +185,7 @@ public void setOutputStream(final OutputStream os) { * * @param is */ + @Override public void setInputStream(final InputStream is) { this.is = is; } @@ -187,6 +195,7 @@ public void setInputStream(final InputStream is) { * * @param val what to write */ + @Override public void encode(final String val) { try { byteBuffer.clear(); @@ -204,6 +213,7 @@ public void encode(final String val) { * @return null if no more records. Should throw exception if EOF is encountered in the middle of * a record. */ + @Override public String decode() { try { byteBuffer.clear(); From c1227d84733e1f2df9636f7c605ed9028a87c71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Mon, 27 Feb 2017 16:49:24 +0100 Subject: [PATCH 082/137] Refactoring FastqRecord (#572) * updating FastqRecord, deprecating some of the existing getters and adding new ones with names that are more consistent with SamRecord. * adding FastqEncoder which has methods for writing FastqRecords and converters for FastqRecord <-> SamRecord. --- .../htsjdk/samtools/fastq/BasicFastqWriter.java | 10 +- .../java/htsjdk/samtools/fastq/FastqConstants.java | 4 +- .../java/htsjdk/samtools/fastq/FastqEncoder.java | 113 ++++++++++ .../java/htsjdk/samtools/fastq/FastqRecord.java | 235 +++++++++++++++------ .../java/htsjdk/samtools/util/SequenceUtil.java | 3 +- .../htsjdk/samtools/fastq/FastqEncoderTest.java | 75 +++++++ .../htsjdk/samtools/fastq/FastqRecordTest.java | 13 +- 7 files changed, 381 insertions(+), 72 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/fastq/FastqEncoder.java create mode 100644 src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java diff --git a/src/main/java/htsjdk/samtools/fastq/BasicFastqWriter.java b/src/main/java/htsjdk/samtools/fastq/BasicFastqWriter.java index 8a5afd38a..0c9596a0b 100644 --- a/src/main/java/htsjdk/samtools/fastq/BasicFastqWriter.java +++ b/src/main/java/htsjdk/samtools/fastq/BasicFastqWriter.java @@ -58,12 +58,10 @@ public BasicFastqWriter(final PrintStream writer) { @Override public void write(final FastqRecord rec) { - writer.print(FastqConstants.SEQUENCE_HEADER); - writer.println(rec.getReadHeader()); - writer.println(rec.getReadString()); - writer.print(FastqConstants.QUALITY_HEADER); - writer.println(rec.getBaseQualityHeader() == null ? "" : rec.getBaseQualityHeader()); - writer.println(rec.getBaseQualityString()); + // encode without creating a String + FastqEncoder.write(writer, rec); + // and print a new line + writer.println(); if (writer.checkError()) { throw new SAMException("Error in writing fastq file " + path); } diff --git a/src/main/java/htsjdk/samtools/fastq/FastqConstants.java b/src/main/java/htsjdk/samtools/fastq/FastqConstants.java index f5d4150ea..4e9b95e5b 100644 --- a/src/main/java/htsjdk/samtools/fastq/FastqConstants.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqConstants.java @@ -29,7 +29,9 @@ public class FastqConstants { public static final String SEQUENCE_HEADER = "@" ; public static final String QUALITY_HEADER = "+" ; - + public static final String FIRST_OF_PAIR = "/1"; + public static final String SECOND_OF_PAIR = "/2"; + public enum FastqExtensions { FASTQ(".fastq"), FASTQ_GZ(".fastq.gz"), diff --git a/src/main/java/htsjdk/samtools/fastq/FastqEncoder.java b/src/main/java/htsjdk/samtools/fastq/FastqEncoder.java new file mode 100644 index 000000000..fdbd02dcc --- /dev/null +++ b/src/main/java/htsjdk/samtools/fastq/FastqEncoder.java @@ -0,0 +1,113 @@ +/* + * The MIT License + * + * Copyright (c) 2016 Daniel Gomez-Sanchez + * + * 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 htsjdk.samtools.fastq; + +import htsjdk.samtools.SAMException; +import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.util.SequenceUtil; + +import java.io.IOException; + +/** + * Codec for encoding records into FASTQ format. + * + * @author Daniel Gomez-Sanchez (magicDGS) + */ +public final class FastqEncoder { + + // cannot be instantiated because it is an utility class + private FastqEncoder() {} + + /** + * Encodes a FastqRecord in the String FASTQ format. + */ + public static String encode(final FastqRecord record) { + // reserve some memory based on the read length + int capacity = record.getReadLength() * 2 + 5; + // reserve some memory based on the read name + if (record.getReadName() != null) { + capacity += record.getReadName().length(); + } + return write(new StringBuilder(capacity), record).toString(); + } + + /** + * Writes a FastqRecord into the Appendable output. + * @throws SAMException if any I/O error occurs. + */ + public static Appendable write(final Appendable out,final FastqRecord record) { + final String readName = record.getReadName(); + final String readString = record.getReadString(); + final String qualHeader = record.getBaseQualityHeader(); + final String qualityString = record.getBaseQualityString(); + try { + return out.append(FastqConstants.SEQUENCE_HEADER) + .append(readName == null ? "" : readName).append('\n') + .append(readString == null ? "" : readString).append('\n') + .append(FastqConstants.QUALITY_HEADER) + .append(qualHeader == null ? "" : qualHeader).append('\n') + .append(qualityString == null ? "" : qualityString); + } catch (IOException e) { + throw new SAMException(e); + } + } + + /** + * Encodes a SAMRecord in the String FASTQ format. + * @see #encode(FastqRecord) + * @see #asSAMRecord(FastqRecord, SAMFileHeader) + */ + public static String encode(final SAMRecord record) { + return encode(asFastqRecord(record)); + } + + /** + * Converts a {@link SAMRecord} into a {@link FastqRecord}. + */ + public static FastqRecord asFastqRecord(final SAMRecord record) { + String readName = record.getReadName(); + if(record.getReadPairedFlag() && (record.getFirstOfPairFlag() || record.getSecondOfPairFlag())) { + readName += (record.getFirstOfPairFlag()) ? FastqConstants.FIRST_OF_PAIR : FastqConstants.SECOND_OF_PAIR; + } + return new FastqRecord(readName, record.getReadString(), null, record.getBaseQualityString()); + } + + /** + * Converts a {@link FastqRecord} into a simple unmapped {@link SAMRecord}. + */ + public static SAMRecord asSAMRecord(final FastqRecord record, final SAMFileHeader header) { + // construct the SAMRecord and set the unmapped flag + final SAMRecord samRecord = new SAMRecord(header); + samRecord.setReadUnmappedFlag(true); + // get the read name from the FastqRecord correctly formatted + final String readName = SequenceUtil.getSamReadNameFromFastqHeader(record.getReadName()); + // set the basic information from the FastqRecord + samRecord.setReadName(readName); + samRecord.setReadBases(record.getReadBases()); + samRecord.setBaseQualities(record.getBaseQualities()); + return samRecord; + } + +} diff --git a/src/main/java/htsjdk/samtools/fastq/FastqRecord.java b/src/main/java/htsjdk/samtools/fastq/FastqRecord.java index b1d3f7507..9fbcd3912 100755 --- a/src/main/java/htsjdk/samtools/fastq/FastqRecord.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqRecord.java @@ -23,62 +23,169 @@ */ package htsjdk.samtools.fastq; +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMUtils; +import htsjdk.samtools.util.StringUtil; + import java.io.Serializable; /** - * Represents a fastq record, fairly literally, i.e. without any conversion. + * Simple representation of a FASTQ record, without any conversion */ public class FastqRecord implements Serializable { private static final long serialVersionUID = 1L; - private final String seqHeaderPrefix; - private final String seqLine; - private final String qualHeaderPrefix; - private final String qualLine; - - public FastqRecord(final String seqHeaderPrefix, final String seqLine, final String qualHeaderPrefix, final String qualLine) { - if (seqHeaderPrefix != null && !seqHeaderPrefix.isEmpty()) this.seqHeaderPrefix = seqHeaderPrefix; - else this.seqHeaderPrefix = null; - if (qualHeaderPrefix != null && !qualHeaderPrefix.isEmpty()) this.qualHeaderPrefix = qualHeaderPrefix; - else this.qualHeaderPrefix = null; - this.seqLine = seqLine ; - this.qualLine = qualLine ; - } - - /** copy constructor */ + private final String readName; + private final String readString; + private final String qualityHeader; + private final String baseQualityString; + + /** + * Default constructor + * + * @param readName the read name (without {@link FastqConstants#SEQUENCE_HEADER}) + * @param readBases the read sequence bases + * @param qualityHeader the quality header (without {@link FastqConstants#SEQUENCE_HEADER}) + * @param baseQualities the base quality scores + */ + public FastqRecord(final String readName, final String readBases, final String qualityHeader, final String baseQualities) { + if (readName != null && !readName.isEmpty()) { + this.readName = readName; + } else { + this.readName = null; + } + if (qualityHeader != null && !qualityHeader.isEmpty()) { + this.qualityHeader = qualityHeader; + } else { + this.qualityHeader = null; + } + this.readString = readBases; + this.baseQualityString = baseQualities; + } + + /** + * Constructor for byte[] arrays + * + * @param readName the read name (without {@link FastqConstants#SEQUENCE_HEADER}) + * @param readBases the read sequence bases as ASCII bytes ACGTN=. + * @param qualityHeader the quality header (without {@link FastqConstants#SEQUENCE_HEADER}) + * @param baseQualities the base qualities as binary PHRED scores (not ASCII) + */ + public FastqRecord(final String readName, final byte[] readBases, final String qualityHeader, final byte[] baseQualities) { + this(readName, StringUtil.bytesToString(readBases), qualityHeader, SAMUtils.phredToFastq(baseQualities)); + } + + /** + * Copy constructor + * + * @param other record to copy + */ public FastqRecord(final FastqRecord other) { - if( other == null ) throw new IllegalArgumentException("new FastqRecord(null)"); - this.seqHeaderPrefix = other.seqHeaderPrefix; - this.seqLine = other.seqLine; - this.qualHeaderPrefix = other.qualHeaderPrefix; - this.qualLine = other.qualLine; - } - - /** @return the read name */ - public String getReadHeader() { return seqHeaderPrefix; } - /** @return the read DNA sequence */ - public String getReadString() { return seqLine; } - /** @return the quality header */ - public String getBaseQualityHeader() { return qualHeaderPrefix; } - /** @return the quality string */ - public String getBaseQualityString() { return qualLine; } - /** shortcut to getReadString().length() */ - public int length() { return this.seqLine==null?0:this.seqLine.length();} - + if (other == null) { + throw new IllegalArgumentException("new FastqRecord(null)"); + } + this.readName = other.readName; + this.readString = other.readString; + this.qualityHeader = other.qualityHeader; + this.baseQualityString = other.baseQualityString; + } + + /** + * @return the read name + * @deprecated since 02/2017. Use {@link #getReadName()} instead + */ + @Deprecated + public String getReadHeader() { + return getReadName(); + } + + /** + * Get the read name + * + * @return the read name + */ + public String getReadName() { + return readName; + } + + /** + * Get the DNA sequence + * + * @return read sequence as a string of ACGTN=. + */ + public String getReadString() { + return readString; + } + + /** + * Get the DNA sequence. + * + * @return read sequence as ASCII bytes ACGTN=; {@link SAMRecord#NULL_SEQUENCE} if no bases are present. + */ + public byte[] getReadBases() { + return (readString == null) ? SAMRecord.NULL_SEQUENCE : StringUtil.stringToBytes(readString); + } + + /** + * Get the base qualities encoded as a FASTQ string + * + * @return the quality string + */ + public String getBaseQualityString() { + return baseQualityString; + } + + /** + * Get the base qualities as binary PHRED scores (not ASCII) + * + * @return the base quality; {@link SAMRecord#NULL_QUALS} if no bases are present. + */ + public byte[] getBaseQualities() { + return (baseQualityString == null) ? SAMRecord.NULL_QUALS : SAMUtils.fastqToPhred(baseQualityString); + } + + /** + * Get the read length + * + * @return number of bases in the read + */ + public int getReadLength() { + return (readString == null) ? 0 : readString.length(); + } + + /** + * Get the base quality header + * + * @return the base quality header + */ + public String getBaseQualityHeader() { + return qualityHeader; + } + + /** + * shortcut to getReadString().length() + * + * @deprecated since 02/2017. Use {@link #getReadLength()} instead + */ + @Deprecated + public int length() { + return getReadLength(); + } + @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result - + ((qualHeaderPrefix == null) ? 0 : qualHeaderPrefix.hashCode()); + + ((qualityHeader == null) ? 0 : qualityHeader.hashCode()); result = prime * result - + ((qualLine == null) ? 0 : qualLine.hashCode()); + + ((baseQualityString == null) ? 0 : baseQualityString.hashCode()); result = prime * result - + ((seqHeaderPrefix == null) ? 0 : seqHeaderPrefix.hashCode()); - result = prime * result + ((seqLine == null) ? 0 : seqLine.hashCode()); + + ((readName == null) ? 0 : readName.hashCode()); + result = prime * result + ((readString == null) ? 0 : readString.hashCode()); return result; } - + @Override public boolean equals(Object obj) { if (this == obj) @@ -88,37 +195,45 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; FastqRecord other = (FastqRecord) obj; - if (seqLine == null) { - if (other.seqLine != null) + if (readString == null) { + if (other.readString != null) return false; - } else if (!seqLine.equals(other.seqLine)) + } else if (!readString.equals(other.readString)) return false; - if (qualHeaderPrefix == null) { - if (other.qualHeaderPrefix != null) + if (qualityHeader == null) { + if (other.qualityHeader != null) return false; - } else if (!qualHeaderPrefix.equals(other.qualHeaderPrefix)) + } else if (!qualityHeader.equals(other.qualityHeader)) return false; - if (qualLine == null) { - if (other.qualLine != null) + if (baseQualityString == null) { + if (other.baseQualityString != null) return false; - } else if (!qualLine.equals(other.qualLine)) + } else if (!baseQualityString.equals(other.baseQualityString)) return false; - if (seqHeaderPrefix == null) { - if (other.seqHeaderPrefix != null) + if (readName == null) { + if (other.readName != null) return false; - } else if (!seqHeaderPrefix.equals(other.seqHeaderPrefix)) + } else if (!readName.equals(other.readName)) return false; - + return true; } - + + /** + * Returns the record as the String FASTQ format. + * @see FastqEncoder#encode(FastqRecord) + */ + public String toFastQString() { + return FastqEncoder.encode(this); + } + + /** + * Returns {@link #toFastQString()} + */ @Override public String toString() { - return new StringBuilder(). - append(FastqConstants.SEQUENCE_HEADER).append(this.seqHeaderPrefix==null?"":this.seqHeaderPrefix).append('\n'). - append(this.seqLine==null?"":this.seqLine).append('\n'). - append(FastqConstants.QUALITY_HEADER).append(this.qualHeaderPrefix==null?"":this.qualHeaderPrefix).append('\n'). - append(this.qualLine==null?"":this.qualLine). - toString(); - } + // TODO: this should be change in the future for a simpler and more informative form such as + // TODO: return String.format("%s: %s bp", readName, getReadLength()); + return toFastQString(); + } } diff --git a/src/main/java/htsjdk/samtools/util/SequenceUtil.java b/src/main/java/htsjdk/samtools/util/SequenceUtil.java index 92c1a507d..7088217da 100644 --- a/src/main/java/htsjdk/samtools/util/SequenceUtil.java +++ b/src/main/java/htsjdk/samtools/util/SequenceUtil.java @@ -32,6 +32,7 @@ import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; import htsjdk.samtools.SAMTag; +import htsjdk.samtools.fastq.FastqConstants; import java.io.File; import java.math.BigInteger; @@ -1005,7 +1006,7 @@ public static String getSamReadNameFromFastqHeader(final String fastqHeader) { // NOTE: the while loop isn't necessarily the most efficient way to handle this but we don't // expect this to ever happen more than once, just trapping pathological cases - while ((readName.endsWith("/1") || readName.endsWith("/2"))) { + while ((readName.endsWith(FastqConstants.FIRST_OF_PAIR) || readName.endsWith(FastqConstants.SECOND_OF_PAIR))) { // If this is an unpaired run we want to make sure that "/1" isn't tacked on the end of the read name, // as this can cause problems down the road (ex. in Picard's MergeBamAlignment). readName = readName.substring(0, readName.length() - 2); diff --git a/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java b/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java new file mode 100644 index 000000000..72e59cff7 --- /dev/null +++ b/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright (c) 2016 Daniel Gomez-Sanchez + * + * 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 htsjdk.samtools.fastq; + +import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.SAMRecordSetBuilder; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @author Daniel Gomez-Sanchez (magicDGS) + */ +public class FastqEncoderTest { + + @Test + public void testAsFastqRecord() throws Exception { + final SAMRecord record = new SAMRecordSetBuilder().addFrag("test", 0, 1, false, false, "10M", null, 2); + record.setReadPairedFlag(true); + // test first of pair encoding + record.setFirstOfPairFlag(true); + testRecord(record.getReadName() + FastqConstants.FIRST_OF_PAIR, FastqEncoder.asFastqRecord(record), record); + record.setFirstOfPairFlag(false); + record.setSecondOfPairFlag(true); + testRecord(record.getReadName() + FastqConstants.SECOND_OF_PAIR, FastqEncoder.asFastqRecord(record), record); + record.setSecondOfPairFlag(false); + testRecord(record.getReadName(), FastqEncoder.asFastqRecord(record), record); + } + + private void testRecord(final String expectedReadName, final FastqRecord fastqRecord, final SAMRecord samRecord) { + Assert.assertEquals(fastqRecord.getReadName(), expectedReadName); + Assert.assertEquals(fastqRecord.getBaseQualities(), samRecord.getBaseQualities()); + Assert.assertEquals(fastqRecord.getReadBases(), samRecord.getReadBases()); + Assert.assertNull(fastqRecord.getBaseQualityHeader()); + } + + @Test + public void testAsSAMRecord() throws Exception { + // create a random record + final SAMRecord samRecord = new SAMRecordSetBuilder().addFrag("test", 0, 1, false, false, "10M", null, 2); + FastqRecord fastqRecord = new FastqRecord(samRecord.getReadName(), samRecord.getReadBases(), "", samRecord.getBaseQualities()); + testConvertedSAMRecord(FastqEncoder.asSAMRecord(fastqRecord, samRecord.getHeader()), samRecord); + fastqRecord = new FastqRecord(samRecord.getReadName() + FastqConstants.FIRST_OF_PAIR, samRecord.getReadBases(), "", samRecord.getBaseQualities()); + testConvertedSAMRecord(FastqEncoder.asSAMRecord(fastqRecord, samRecord.getHeader()), samRecord); + fastqRecord = new FastqRecord(samRecord.getReadName() + FastqConstants.SECOND_OF_PAIR, samRecord.getReadBases(), "", samRecord.getBaseQualities()); + testConvertedSAMRecord(FastqEncoder.asSAMRecord(fastqRecord, samRecord.getHeader()), samRecord); + } + + private void testConvertedSAMRecord(final SAMRecord converted, final SAMRecord original) { + Assert.assertEquals(converted.getReadName(), original.getReadName()); + Assert.assertEquals(converted.getBaseQualities(), original.getBaseQualities()); + Assert.assertEquals(converted.getReadBases(), original.getReadBases()); + Assert.assertTrue(converted.getReadUnmappedFlag()); + } +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java index f6f238eab..97a3d3c8d 100644 --- a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java +++ b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java @@ -15,7 +15,7 @@ public void testBasic() { Assert.assertNull(fastqRecord.getBaseQualityHeader()); - Assert.assertEquals(fastqRecord.getReadHeader(), seqHeaderPrefix); + Assert.assertEquals(fastqRecord.getReadName(), seqHeaderPrefix); Assert.assertEquals(fastqRecord.getBaseQualityString(), qualLine); Assert.assertEquals(fastqRecord.getReadString(), seqLine); Assert.assertNotNull(fastqRecord.toString());//just check not nullness @@ -25,9 +25,9 @@ public void testBasic() { Assert.assertEquals(fastqRecord, fastqRecord); Assert.assertNotEquals(fastqRecord, "fred"); Assert.assertNotEquals("fred", fastqRecord); - Assert.assertEquals(fastqRecord.length(), seqLine.length()); + Assert.assertEquals(fastqRecord.getReadLength(), seqLine.length()); Assert.assertEquals(fastqRecord.getBaseQualityString().length(), fastqRecord.getReadString().length()); - Assert.assertEquals(fastqRecord.getReadString().length(), fastqRecord.length()); + Assert.assertEquals(fastqRecord.getReadString().length(), fastqRecord.getReadLength()); } @Test @@ -37,7 +37,7 @@ public void testBasicEmptyHeaderPrefix() { final String qualHeaderPrefix = ""; final String qualLine = ";<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; final FastqRecord fastqRecord = new FastqRecord(seqHeaderPrefix, seqLine, qualHeaderPrefix, qualLine); - Assert.assertNull(fastqRecord.getReadHeader()); + Assert.assertNull(fastqRecord.getReadName()); Assert.assertNull(fastqRecord.getBaseQualityHeader()); } @@ -57,6 +57,11 @@ public void testCopy() { Assert.assertSame(fastqRecord.getBaseQualityHeader(), fastqRecordCopy.getBaseQualityHeader()); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNullCopy() { + new FastqRecord(null); + } + @Test public void testNullSeq() { final String seqHeaderPrefix = "header"; From c6773801f5309fd18a378cadb04dd6fb5d00998c Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Thu, 2 Mar 2017 16:33:35 -0500 Subject: [PATCH 083/137] Fixed bug where calling IntervalList.uniqued(false) would fail if the interval names were null. (#815) --- src/main/java/htsjdk/samtools/util/IntervalList.java | 8 +++----- src/test/java/htsjdk/samtools/util/IntervalListTest.java | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index 9bfc718f9..f9bfe05ca 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -356,11 +356,9 @@ static Interval merge(final SortedSet intervals, final boolean concate end = Math.max(end, i.getEnd()); } - if (concatenateNames) { - if (names.isEmpty()) name = null; - else name = StringUtil.join("|", names); - } - else { name = names.iterator().next(); } + if (names.isEmpty()) name = null; + else if (concatenateNames) name = StringUtil.join("|", names); + else name = names.iterator().next(); return new Interval(chrom, start, end, neg, name); } diff --git a/src/test/java/htsjdk/samtools/util/IntervalListTest.java b/src/test/java/htsjdk/samtools/util/IntervalListTest.java index 6c5fcd43c..3d919e881 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalListTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalListTest.java @@ -517,4 +517,16 @@ public void changeHeader() { Assert.assertTrue(false); } + + @Test public void uniqueIntervalsWithoutNames() { + final IntervalList test = new IntervalList(this.fileHeader); + test.add(new Interval("1", 100, 200)); + test.add(new Interval("1", 500, 600)); + test.add(new Interval("1", 550, 700)); + + for (final boolean concat : new boolean[]{true, false}) { + final IntervalList unique = test.uniqued(concat); + Assert.assertEquals(unique.size(), 2); + } + } } From 737ceec0d62e5a7c57a1d0cf2d7dce09cc78d38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 3 Mar 2017 17:10:47 +0100 Subject: [PATCH 084/137] support Path in Md5CalculatingOutputStream(#814) * added new Md5CalculatingOutputStream constructor that takes Path instead of File --- .../htsjdk/samtools/util/Md5CalculatingOutputStream.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java index 361c99032..f3b3f0779 100755 --- a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -43,14 +45,14 @@ private final OutputStream os; private final MessageDigest md5; - private final File digestFile; + private final Path digestFile; private String hash; /** * Constructor that takes in the OutputStream that we are wrapping * and creates the MD5 MessageDigest */ - public Md5CalculatingOutputStream(OutputStream os, File digestFile) { + public Md5CalculatingOutputStream(OutputStream os, Path digestFile) { super(); this.hash = null; this.os = os; @@ -65,6 +67,10 @@ public Md5CalculatingOutputStream(OutputStream os, File digestFile) { } } + public Md5CalculatingOutputStream(OutputStream os, File digestFile) { + this(os, digestFile.toPath()); + } + @Override public void write(int b) throws IOException { md5.update((byte)b); @@ -111,7 +117,7 @@ public void close() throws IOException { makeHash(); if(digestFile != null) { - BufferedWriter writer = new BufferedWriter(new FileWriter(digestFile)); + BufferedWriter writer = Files.newBufferedWriter(digestFile); writer.write(hash); writer.close(); } From 9ab551813450c05b7be8964a813171942236ff4d Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Tue, 7 Mar 2017 14:33:34 -0500 Subject: [PATCH 085/137] fixing vararg warnings on test compilation (#809) * fix <> on VariantContextUnitTest * fixing ambiguous varargs call warnings in VariantContextUnitTest explicitly casting null values to resolve warnings * adding a test case that seems to been accidentally missed --- .../variantcontext/VariantContextUnitTest.java | 89 ++++++++++++---------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java index 14056f833..3d6851598 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextUnitTest.java @@ -178,8 +178,8 @@ public void testMultipleSNPAlleleOrdering() { final List allelesUnnaturalOrder = Arrays.asList(Aref, T, C); VariantContext naturalVC = snpBuilder.alleles(allelesNaturalOrder).make(); VariantContext unnaturalVC = snpBuilder.alleles(allelesUnnaturalOrder).make(); - Assert.assertEquals(new ArrayList(naturalVC.getAlleles()), allelesNaturalOrder); - Assert.assertEquals(new ArrayList(unnaturalVC.getAlleles()), allelesUnnaturalOrder); + Assert.assertEquals(new ArrayList<>(naturalVC.getAlleles()), allelesNaturalOrder); + Assert.assertEquals(new ArrayList<>(unnaturalVC.getAlleles()), allelesUnnaturalOrder); } @Test @@ -371,7 +371,7 @@ public void testBadConstructorArgs3() { @Test (expectedExceptions = Throwable.class) public void testBadConstructorArgs4() { - new VariantContextBuilder("test", insLoc, insLocStart, insLocStop, Collections.emptyList()).make(); + new VariantContextBuilder("test", insLoc, insLocStart, insLocStop, Collections.emptyList()).make(); } @Test (expectedExceptions = Exception.class) @@ -528,7 +528,7 @@ public void testFilters() { Assert.assertTrue(vc.filtersWereApplied()); Assert.assertNotNull(vc.getFiltersMaybeNull()); - Set filters = new HashSet(Arrays.asList("BAD_SNP_BAD!", "REALLY_BAD_SNP", "CHRIST_THIS_IS_TERRIBLE")); + Set filters = new HashSet<>(Arrays.asList("BAD_SNP_BAD!", "REALLY_BAD_SNP", "CHRIST_THIS_IS_TERRIBLE")); vc = new VariantContextBuilder(vc).filters(filters).make(); Assert.assertFalse(vc.isNotFiltered()); @@ -570,12 +570,16 @@ public void testVCFfromGenotypes() { Genotype g5 = GenotypeBuilder.create("AC", Arrays.asList(Aref, C)); VariantContext vc = new VariantContextBuilder("genotypes", snpLoc, snpLocStart, snpLocStop, alleles).genotypes(g1,g2,g3,g4,g5).make(); - VariantContext vc12 = vc.subContextFromSamples(new HashSet(Arrays.asList(g1.getSampleName(), g2.getSampleName())), true); - VariantContext vc1 = vc.subContextFromSamples(new HashSet(Arrays.asList(g1.getSampleName())), true); - VariantContext vc23 = vc.subContextFromSamples(new HashSet(Arrays.asList(g2.getSampleName(), g3.getSampleName())), true); - VariantContext vc4 = vc.subContextFromSamples(new HashSet(Arrays.asList(g4.getSampleName())), true); - VariantContext vc14 = vc.subContextFromSamples(new HashSet(Arrays.asList(g1.getSampleName(), g4.getSampleName())), true); - VariantContext vc125 = vc.subContextFromSamples(new HashSet(Arrays.asList(g1.getSampleName(), g2.getSampleName(), g5.getSampleName())), true); + VariantContext vc12 = vc.subContextFromSamples( + new HashSet<>(Arrays.asList(g1.getSampleName(), g2.getSampleName())), true); + VariantContext vc1 = vc.subContextFromSamples(new HashSet<>(Arrays.asList(g1.getSampleName())), true); + VariantContext vc23 = vc.subContextFromSamples( + new HashSet<>(Arrays.asList(g2.getSampleName(), g3.getSampleName())), true); + VariantContext vc4 = vc.subContextFromSamples(new HashSet<>(Arrays.asList(g4.getSampleName())), true); + VariantContext vc14 = vc.subContextFromSamples( + new HashSet<>(Arrays.asList(g1.getSampleName(), g4.getSampleName())), true); + VariantContext vc125 = vc.subContextFromSamples( + new HashSet<>(Arrays.asList(g1.getSampleName(), g2.getSampleName(), g5.getSampleName())), true); Assert.assertTrue(vc12.isPolymorphicInSamples()); Assert.assertTrue(vc23.isPolymorphicInSamples()); @@ -676,7 +680,7 @@ public String toString() { @DataProvider(name = "getAlleles") public Object[][] mergeAllelesData() { - List tests = new ArrayList(); + List tests = new ArrayList<>(); tests.add(new Object[]{new GetAllelesTest("A*", Aref)}); tests.add(new Object[]{new GetAllelesTest("A*/C", Aref, C)}); @@ -747,7 +751,7 @@ public String toString() { VariantContext sites = new VariantContextBuilder("sites", snpLoc, snpLocStart, snpLocStop, Arrays.asList(Aref, T)).make(); VariantContext genotypes = new VariantContextBuilder(sites).source("genotypes").genotypes(g1, g2, g3).make(); - List tests = new ArrayList(); + List tests = new ArrayList<>(); tests.add(new Object[]{new SitesAndGenotypesVC("sites", sites)}); tests.add(new Object[]{new SitesAndGenotypesVC("genotypes", genotypes)}); @@ -822,7 +826,7 @@ public void runModifyVCTests(SitesAndGenotypesVC cfg) { boolean updateAlleles; private SubContextTest(Collection samples, boolean updateAlleles) { - this.samples = new HashSet(samples); + this.samples = new HashSet<>(samples); this.updateAlleles = updateAlleles; } @@ -833,10 +837,10 @@ public String toString() { @DataProvider(name = "SubContextTest") public Object[][] MakeSubContextTest() { - List tests = new ArrayList(); + List tests = new ArrayList<>(); for ( boolean updateAlleles : Arrays.asList(true, false)) { - tests.add(new Object[]{new SubContextTest(Collections.emptySet(), updateAlleles)}); + tests.add(new Object[]{new SubContextTest(Collections.emptySet(), updateAlleles)}); tests.add(new Object[]{new SubContextTest(Collections.singleton("MISSING"), updateAlleles)}); tests.add(new Object[]{new SubContextTest(Collections.singleton("AA"), updateAlleles)}); tests.add(new Object[]{new SubContextTest(Collections.singleton("AT"), updateAlleles)}); @@ -871,7 +875,7 @@ public void runSubContextTest(SubContextTest cfg) { Assert.assertEquals(sub.getID(), vc.getID()); Assert.assertEquals(sub.getAttributes(), vc.getAttributes()); - Set expectedGenotypes = new HashSet(); + Set expectedGenotypes = new HashSet<>(); if ( cfg.samples.contains(g1.getSampleName()) ) expectedGenotypes.add(g1); if ( cfg.samples.contains(g2.getSampleName()) ) expectedGenotypes.add(g2); if ( cfg.samples.contains(g3.getSampleName()) ) expectedGenotypes.add(g3); @@ -881,10 +885,10 @@ public void runSubContextTest(SubContextTest cfg) { // these values depend on the results of sub if ( cfg.updateAlleles ) { // do the work to see what alleles should be here, and which not - List expectedAlleles = new ArrayList(); + List expectedAlleles = new ArrayList<>(); expectedAlleles.add(Aref); - Set genotypeAlleles = new HashSet(); + Set genotypeAlleles = new HashSet<>(); for ( final Genotype g : expectedGC ) genotypeAlleles.addAll(g.getAlleles()); genotypeAlleles.remove(Aref); @@ -925,7 +929,7 @@ public String toString() { @DataProvider(name = "SampleNamesTest") public Object[][] MakeSampleNamesTest() { - List tests = new ArrayList(); + List tests = new ArrayList<>(); tests.add(new Object[]{new SampleNamesTest(Arrays.asList("1"), Arrays.asList("1"))}); tests.add(new Object[]{new SampleNamesTest(Arrays.asList("2", "1"), Arrays.asList("1", "2"))}); @@ -959,7 +963,7 @@ public void runSampleNamesTest(SampleNamesTest cfg) { VariantContext vc = new VariantContextBuilder("genotypes", snpLoc, snpLocStart, snpLocStop, Arrays.asList(Aref, T)).genotypes(gc).make(); // same sample names => success - Assert.assertTrue(vc.getSampleNames().equals(new HashSet(cfg.sampleNames)), "vc.getSampleNames() = " + vc.getSampleNames()); + Assert.assertTrue(vc.getSampleNames().equals(new HashSet<>(cfg.sampleNames)), "vc.getSampleNames() = " + vc.getSampleNames()); Assert.assertEquals(vc.getSampleNamesOrderedByName(), cfg.sampleNamesInOrder, "vc.getSampleNamesOrderedByName() = " + vc.getSampleNamesOrderedByName()); assertGenotypesAreInOrder(vc.getGenotypesOrderedByName(), cfg.sampleNamesInOrder); @@ -1147,7 +1151,7 @@ private VariantContext createTestVariantContextRsIds(final String rsId) { fullyDecoded, toValidate); } private Set makeRsIDsSet(final String... rsIds) { - return new HashSet(Arrays.asList(rsIds)); + return new HashSet<>(Arrays.asList(rsIds)); } @@ -1226,14 +1230,14 @@ private VariantContext createValidateAlternateAllelesContext(final List /** AN : total number of alleles in called genotypes **/ // with AN set and hom-ref, we expect AN to be 2 for Aref/Aref - final Map attributesAN = new HashMap(); + final Map attributesAN = new HashMap<>(); attributesAN.put(VCFConstants.ALLELE_NUMBER_KEY, "2"); final VariantContext vcANSet = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesAN, homRef); // with AN set, one no-call (no-calls get ignored by getCalledChrCount() in VariantContext) // we expect AN to be 1 for Aref/no-call - final Map attributesANNoCall = new HashMap(); + final Map attributesANNoCall = new HashMap<>(); attributesANNoCall.put(VCFConstants.ALLELE_NUMBER_KEY, "1"); final VariantContext vcANSetNoCall = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesANNoCall, homRefNoCall); @@ -1241,42 +1245,42 @@ private VariantContext createValidateAlternateAllelesContext(final List /** AC : allele count in genotypes, for each ALT allele, in the same order as listed **/ // with AC set, and T/T, we expect AC to be 2 (for 2 counts of ALT T) - final Map attributesAC = new HashMap(); + final Map attributesAC = new HashMap<>(); attributesAC.put(VCFConstants.ALLELE_COUNT_KEY, "2"); final VariantContext vcACSet = createValidateChromosomeCountsContext(Arrays.asList(Aref, T), attributesAC, homVarT); // with AC set and no ALT (GT is 0/0), we expect AC count to be 0 - final Map attributesACNoAlts = new HashMap(); + final Map attributesACNoAlts = new HashMap<>(); attributesACNoAlts.put(VCFConstants.ALLELE_COUNT_KEY, "0"); final VariantContext vcACSetNoAlts = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesACNoAlts, homRef); // with AC set, and two different ALTs (T and C), with GT of 1/2, we expect a count of 1 for each. // With two ALTs, a list is expected, so we set the attribute as a list of 1,1 - final Map attributesACTwoAlts = new HashMap(); + final Map attributesACTwoAlts = new HashMap<>(); attributesACTwoAlts.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1", "1")); final VariantContext vcACSetTwoAlts = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAlts, hetVarTC); // with AC set, and two different ALTs (T and C), with no GT, we expect a 2 count values. - final Map attributesACNoGtTwoAlts = new HashMap(); + final Map attributesACNoGtTwoAlts = new HashMap<>(); attributesACNoGtTwoAlts.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1", "1")); final VariantContext vcACNoGtSetTwoAlts = - createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAlts, null); + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAlts, (Genotype[]) null); // with AF set, and two different ALTs (T and C), with GT of 1/2, we expect two frequncy values. // With two ALTs, a list is expected, so we set the attribute as a list of 0.5,0.5 - final Map attributesAFTwoAlts = new HashMap(); + final Map attributesAFTwoAlts = new HashMap<>(); attributesAFTwoAlts.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5", "0.5")); final VariantContext vcAFSetTwoAlts = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFTwoAlts, hetVarTC); // with AF set, and two different ALTs (T and C), with no GT, we expect two frequency values. - final Map attributesAFNoGtTwoAlts = new HashMap(); + final Map attributesAFNoGtTwoAlts = new HashMap<>(); attributesAFNoGtTwoAlts.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5", "0.5")); final VariantContext vcAFNoGtSetTwoAlts = - createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAlts, null); + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAlts, (Genotype[]) null); return new Object[][]{ {vcNoGenotypes}, @@ -1284,6 +1288,7 @@ private VariantContext createValidateAlternateAllelesContext(final List {vcANSetNoCall}, {vcACSet}, {vcACSetNoAlts}, + {vcACSetTwoAlts}, {vcACNoGtSetTwoAlts}, {vcAFSetTwoAlts}, {vcAFNoGtSetTwoAlts} @@ -1303,60 +1308,60 @@ public void testValidateChromosomeCounts(final VariantContext vc) { /** AN : total number of alleles in called genotypes **/ // with AN set and hom-ref, we expect AN to be 2 for Aref/Aref, so 3 will fail - final Map attributesAN = new HashMap(); + final Map attributesAN = new HashMap<>(); attributesAN.put(VCFConstants.ALLELE_NUMBER_KEY, "3"); final VariantContext vcANSet = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesAN, homRef); // with AN set, one no-call (no-calls get ignored by getCalledChrCount() in VariantContext) // we expect AN to be 1 for Aref/no-call, so 2 will fail - final Map attributesANNoCall = new HashMap(); + final Map attributesANNoCall = new HashMap<>(); attributesANNoCall.put(VCFConstants.ALLELE_NUMBER_KEY, "2"); final VariantContext vcANSetNoCall = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesANNoCall, homRefNoCall); /** AC : allele count in genotypes, for each ALT allele, in the same order as listed **/ // with AC set but no ALTs, we expect a count of 0, so the wrong count will fail here - final Map attributesACWrongCount = new HashMap(); + final Map attributesACWrongCount = new HashMap<>(); attributesACWrongCount.put(VCFConstants.ALLELE_COUNT_KEY, "2"); final VariantContext vcACWrongCount = createValidateChromosomeCountsContext(Arrays.asList(Aref), attributesACWrongCount, homRef); // with AC set, two ALTs, but AC is not a list with count for each ALT - final Map attributesACTwoAlts = new HashMap(); + final Map attributesACTwoAlts = new HashMap<>(); attributesACTwoAlts.put(VCFConstants.ALLELE_COUNT_KEY, "1"); final VariantContext vcACSetTwoAlts = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAlts, hetVarTC); // with AC set, two ALTs, and a list is correctly used, but wrong counts (we expect counts to be 1,1) - final Map attributesACTwoAltsWrongCount = new HashMap(); + final Map attributesACTwoAltsWrongCount = new HashMap<>(); attributesACTwoAltsWrongCount.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1", "2")); final VariantContext vcACSetTwoAltsWrongCount = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAltsWrongCount, hetVarTC); // with AC set, two ALTs, but only count for one ALT (we expect two items in the list: 1,1) - final Map attributesACTwoAltsOneAltCount = new HashMap(); + final Map attributesACTwoAltsOneAltCount = new HashMap<>(); attributesACTwoAltsOneAltCount.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1")); final VariantContext vcACSetTwoAltsOneAltCount = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACTwoAltsOneAltCount, hetVarTC); // with AC set, no GT, two ALTs, but only count for one ALT (we expect two items in the list: 1,1) - final Map attributesACNoGtTwoAltsOneAltCount = new HashMap(); + final Map attributesACNoGtTwoAltsOneAltCount = new HashMap<>(); attributesACNoGtTwoAltsOneAltCount.put(VCFConstants.ALLELE_COUNT_KEY, Arrays.asList("1")); final VariantContext vcACNoGtSetTwoAltsOneAltCount = - createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAltsOneAltCount, null); + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesACNoGtTwoAltsOneAltCount, (Genotype[])null); // with AF set, two ALTs, but only frequency for one ALT (we expect two items in the list - final Map attributesAFTwoAltsWrongFreq = new HashMap(); + final Map attributesAFTwoAltsWrongFreq = new HashMap<>(); attributesAFTwoAltsWrongFreq.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5")); final VariantContext vcAFSetTwoAltsWrongFreq = createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFTwoAltsWrongFreq, hetVarTC); // with AF set, no GT, two ALTs, but only frequency for one ALT (we expect two items in the list - final Map attributesAFNoGtTwoAltsWrongCount = new HashMap(); + final Map attributesAFNoGtTwoAltsWrongCount = new HashMap<>(); attributesAFNoGtTwoAltsWrongCount.put(VCFConstants.ALLELE_FREQUENCY_KEY, Arrays.asList("0.5")); final VariantContext vcAFNoGtSetTwoAltsWrongFreq = - createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAltsWrongCount, null); + createValidateChromosomeCountsContext(Arrays.asList(Aref, T, C), attributesAFNoGtTwoAltsWrongCount, (Genotype[])null); return new Object[][]{ {vcANSet}, From 6d2265879702cf65c8f26031d4a1d7759f141120 Mon Sep 17 00:00:00 2001 From: Ekaterina Kazachkova Date: Tue, 7 Mar 2017 22:37:23 +0300 Subject: [PATCH 086/137] Fix for issue #513: NPE in ValidateSamFile with CRAM (#735) * resolve issue #513 NPE when validating a CRAM file with a .fasta reference with no .dict file --- .../java/htsjdk/samtools/SamFileValidator.java | 7 +- .../reference/ReferenceSequenceFileWalker.java | 4 +- .../java/htsjdk/samtools/ValidateSamFileTest.java | 15 +++++ .../ValidateSamFileTest/nm_tag_validation.cram | Bin 0 -> 39392 bytes .../ValidateSamFileTest/nm_tag_validation.fa | 71 +++++++++++++++++++++ .../ValidateSamFileTest/nm_tag_validation.fa.fai | 1 + 6 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.cram create mode 100644 src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa create mode 100644 src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa.fai diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index c774b6fd9..5f82bd194 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -301,8 +301,7 @@ private void validateSamRecordsAndQualityFormat(final Iterable samRec if (cigarIsValid) { try { validateNmTag(record, recordNumber); - } - catch (SAMException e) { + } catch (SAMException e) { if (hasValidSortOrder) { // If a CRAM file has an invalid sort order, the ReferenceFileWalker will throw a // SAMException due to an out of order request when retrieving reference bases during NM @@ -554,12 +553,12 @@ private void validateHeader(final SAMFileHeader fileHeader) { "A platform (PL) attribute was not found for read group ", readGroupID)); } - else { + else { // NB: cannot be null, so not catching a NPE try { SAMReadGroupRecord.PlatformValue.valueOf(platformValue.toUpperCase()); } catch (IllegalArgumentException e) { - addError(new SAMValidationError(Type.INVALID_PLATFORM_VALUE, + addError(new SAMValidationError(Type.INVALID_PLATFORM_VALUE, "The platform (PL) attribute (" + platformValue + ") + was not one of the valid values for read group ", readGroupID)); } diff --git a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java index 6a820ebbe..936f14c86 100644 --- a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java +++ b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileWalker.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2009 The Broad Institute + * Copyright (c) 2009-2016 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -83,7 +83,7 @@ public ReferenceSequence get(final int sequenceIndex) { } referenceSequence = null; - if(referenceSequenceFile.isIndexed()) { + if(referenceSequenceFile.isIndexed() && referenceSequenceFile.getSequenceDictionary() != null) { final SAMSequenceRecord samSequenceRecord = referenceSequenceFile.getSequenceDictionary().getSequence(sequenceIndex); if(samSequenceRecord != null) { referenceSequence = referenceSequenceFile.getSequence(samSequenceRecord.getSequenceName()) ; diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index e577ed876..77ac59f0e 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -27,6 +27,7 @@ import htsjdk.samtools.BamIndexValidator.IndexValidationStringency; import htsjdk.samtools.metrics.MetricBase; import htsjdk.samtools.metrics.MetricsFile; +import htsjdk.samtools.reference.FastaSequenceFile; import htsjdk.samtools.reference.ReferenceSequence; import htsjdk.samtools.reference.ReferenceSequenceFile; import htsjdk.samtools.util.CloserUtil; @@ -70,6 +71,20 @@ public void testValidSamFile() throws Exception { } @Test + public void testValidCRAMFileWithoutSeqDict() throws Exception { + final File reference = new File(TEST_DATA_DIR, "nm_tag_validation.fa"); + final SamReader samReader = SamReaderFactory + .makeDefault() + .validationStringency(ValidationStringency.SILENT) + .referenceSequence(reference) + .open(new File(TEST_DATA_DIR, "nm_tag_validation.cram")); + final Histogram results = executeValidation(samReader, + new FastaSequenceFile(reference, true), + IndexValidationStringency.EXHAUSTIVE); + Assert.assertTrue(!results.isEmpty()); + } + + @Test public void testSamFileVersion1pt5() throws Exception { final SamReader samReader = SamReaderFactory.makeDefault().validationStringency(ValidationStringency.SILENT).open(new File(TEST_DATA_DIR, "test_samfile_version_1pt5.bam")); final Histogram results = executeValidation(samReader, null, IndexValidationStringency.EXHAUSTIVE); diff --git a/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.cram b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.cram new file mode 100644 index 0000000000000000000000000000000000000000..57c58dfd0cd719aaae1f51a2a44d676b1718009f GIT binary patch literal 39392 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 z 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa new file mode 100644 index 000000000..7c2ec2a88 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa @@ -0,0 +1,71 @@ +>17 17:1-4200 +AAGCTTCTCACCCTGTTCCTGCATAGATAATTGCATGACAATTGCCTTGTCCCTGCTGAA +TGTGCTCTGGGGTCTCTGGGGTCTCACCCACGACCAACTCCCTGGGCCTGGCACCAGGGA +GCTTAACAAACATCTGTCCAGCGAATACCTGCATCCCTAGAAGTGAAGCCACCGCCCAAA +GACACGCCCATGTCCAGCTTAACCTGCATCCCTAGAAGTGAAGGCACCGCCCAAAGACAC +GCCCATGTCCAGCTTATTCTGCCCAGTTCCTCTCCAGAAAGGCTGCATGGTTGACACACA +GTGCCTGCGACAAAGCTGAATGCTATCATTTAAAAACTCCTTGCTGGTTTGAGAGGCAGA +AAATGATATCTCATAGTTGCTTTACTTTGCATATTTTAAAATTGTGACTTTCATGGCATA +AATAATACTGGTTTATTACAGAAGCACTAGAAAATGCATGTGGACAAAAGTTGGGATTAG +GAGAGAGAAATGAAGACATATGTCCACACAAAAACCTGTTCATTGCAGCTTTCTACCATC +ACCAAAAATTGCAAACAACCACACGCCCTTCAACTGGGGAACTCATCAACAACAAACTTG +TGGTTTACCCACACAATGGAAGACCACTTAGCAACAAAAAGGACCAAACTCCTGGTACAT +GCAACTGACAGATGAATCTCAAACGCATTCCTCCGTGTGAAAGAAGCCGGACTCACAGGG +CAACACACTATCTGACTGTTTCATGGGAAAGTCTGGAAACGGCAACACCATTGAGACAGA +AAACAGGTGAGTGGTTGCCTGGGGCCAGGGAACTTTCTGGGGTCATATTCTCTGTGTTGA +TTCTGGTGGTGGAAACAAGACTGTCCCAGCCTGGGTGATACAGCGAGACCCCATCTCTAC +CAAAAAATTAAAAATTAGCTGGGCATGGTGGTGCATGCCTGTAGTCCCAGCTATTCACAG +TGCTGAGGTGGGAAGATGCTTGAGCCCAGGAGTTCAAGGCTGCAATGAGCTATGATTGCG +CCACTGCACTTTGGCCTGGACAACAGAGCAAAACCCTGTCTCTAAAAAAAGAAAAGAAAA +GAAAAACTCACTGGATATGAATGATACAGGTTGAGGATCCATTATCTGAAATGCTTGGAC +CAGATGTTTTGAATTTTGGATTTTTTCATATTTTGTAATCTTTGCAGTATATTTACCAGT +TCAGCATCCCTAACTCAAAAATTCAAAAATCTGAAATCCCAAACGCGCCAATAAGCATTC +CCTTTGAGCGTCATGTCGGTGCTTGGAATGTTTGGGGTTTTGGATTTACAGCTTTGGGAC +GCTCAACCTGTACCTCAATAAACCTGATTTTAAAAAAGTTTGGGGGGATTCCCCTAAGCC +CGCCACCCGGAGACAGCGGATTTCCTTAGTTACTTACTATGCTCCTTGGCCATTTCTCTA +GGTATTGGTATATTGTGTCTGCTGTGAACTGTCCTTGGCCTGTTTGGTGACGGGTGAGGA +GCAGGGACAGAAGGGTCCTGCGTGCCCTGCCTTCACAAGCCCCTGGAAGGAAAGTTGTTT +TGGGATCTCTGCACCCTCAGCCTGGACAACTTGTGCCCATCTGGTGACCCCTCACTCAGC +CACCAGACTTCCACGACAGGCTCCAGCCTCGGCACCTTCAGCCATGGACAGTTCCGCCAG +CGTTGCCCTCTGTTCTGCTGTTTTCTCTACCAGAAGTGCCCTTCCCTCCTCACCTGACCA +CTCTGGGGAAATCCCTCAGCACCCTCCCTGAGCATACCCTACTCTGGCACAAGCCCACCC +TGCAAAGCCCCTGAGGCCCGCCCTGTGGCGTCTCTCCCTCCCTTGCTGTCAGGACAGTGG +TCCTGGCCACCGGGGCTCACGGAGCCGCCCTGTGCCGTGTACCTCTGAGCCCTCTGCACA +GTGCCTTCTGCTTGCCTGTGGCTTTGAGAAGAAACCCCTTCTGGTTATACATAAGACAGC +CAGAGAAGGGAGTTGCCCAGGGTGGCACAGCACGTTGCTGCCAGTTACTGCCATTTTCAC +GGGCATGAAATGGAGATAACAACAGGAGCGACCGCACAGGCTGCTGAGCGCGTCACACGC +AGCCATCGCGCAGCTCAGGGATATTACGTGTAACTCGACATGTCAGCGATTGTCACAGGC +ACTGCTACTCCTGGGGTTTTCCATCAAACCCTCAAGAGCTGGGCCTGGGGTCAACTTCCG +GCCTGGGGAAACTGGGGCAAGTATCACCAGAGATGAGCTTTATAAAAATAATGGTGCTAG +CTGGGCATGGTGGCTTGCACCTGTAATCCCAGCACTTTGGGAGGCCGAGCTAGGAGGATC +GTTTGAGTCCAGCAGTTTGAGACCAGCCTGGCCAATACGGCAAAACCCAGTCTCTACAAA +AAATACAAAAAACAACTAGCCAGGCGTGGTGGTGCACACCTGTAGTCCCAGCTACTCAGG +AGGCTGAGGGGGAAGGACTGCTTGAGCCCAGGAGTTTGAGGCTGCTGTGAGCTGTGATCG +CATCACTGCATTCCAGCCCGGTGACAGAGTGAGTCACTGTCTCAAAAAAGAAAGGAAGAA +ATAAAGAAAACAAATAAAAATAATAGTGCAGACAAAAGGCCTTGACCCATCTAGCTTTGG +CCCTCAGCATCAACCGCTAGATACGTCCCTCCCTTTCTTCTGGGGCACAGGTCACACTCT +CTTCCAGGTCTAGGATGCAGCTGAGGGGTGCCCCTCTTACCATCTAATCTGTGCCCTTAT +TTCCTCTGCTTTAGTGAGGAAGAGGCCCCTGGTCCATGAAGGGGCCTTTCAGAGACGGGG +ACCCCTGAGGAGCCCCGAGCAGCAGCCGTCGTGTCTCACCCAGGGTGTCTGAAACAGATG +TGGAGGTCTCGGGTGAGGCGTGGCTCAGATACAGGGAGTGGCCCACAGCTCGGCCTGTCT +TTGAAAGGCCACGTGACCTGGCCCACGGCTGGCAGGTGGGACCCAGCTGCAGGGGTCCAG +CAGCACCCACAGCAGCCACCTGTGGCAGGGAGGAGCTTGTGGTACAGTGGACAGGCCCTG +CCCAGATGGCCCCCCGCCTGCCTGTGGAAGTTGACCAGACCATCTGTCACAGCAGGTAAG +ACTCTGCTTTCTGGGCAACCCAGCAGGTGACCCTGGAATTCCTGTCCATCTGGCAGGTGG +GCATTGAAACTGGTTTAAAAATGTCACACCATAGGCCGGGCACAGTGGCTCACGCCTGTA +ATCCCAGCCCTTTGGGAGGCCAGGGTGGGTGGATCACTTGAGGTCAGGAGTTCAAGACCA +GCCTGGCCAACATGGTGAAACCCCGTCTACTAAAAATACAAAAATTAGCCTGGCGTGGTG +GCGCATGCCTGTAATCCCAGCTACTTGGGAAGCTGAGGGATGAGAACTGCTTGAACCTGG +GAGGCAGACGTTGCAGTGAGCTGAGATCACGCCACTGCACTCCAGCCTGGGCAACAGAGT +AAGACTCTGTCTCAAAAAAAAAAAAATCACACCATTTTGGCTTCAGATTGCATATCCTCC +TGCAAGGATATATACGCGTGAAATTCAAGTCAATGACAAATCAGAAGAAAAAACATATAT +ATACGCAAACCAGTATCCTACTGTGTGTGTCGTTTGTTGTGTTTTCGACAGCTGTCCGTG +TTATAATAATTCCTCTAGTTCAAATTTATTCATTTTTAACTTCATAGTACCACATTCTAC +ACACTGCCCATGTCCCCTCAAGCTTCCCCTGGCTCCTGCAACCACAAATCTACTCTCTGC +CTCTGTGGGTTGACCTATTCTGGACACGTCATAGAAATAGAGTCCTGCAACACGTGGCCG +TCTGTGTCTGGCTTCTCTCGCTTAGCATCTTGTTTCCAAGGTCCTCCCACAGTGTAGCAT +GCACCTGCTACACTCCTTCTTAGGGCTGATATTCCACGCACCTGCTACACTCCTTCTTAT +GGCTGATATTCCACGCACCTGCTACACTCCTTCTTAGGGCTGATATTCCACACACCCGCT +ACACTCCTTCTTAGGGCTGATATTCCACGCACCCGCTACACTCCTTCTTAGGGCTGATAT +TCCACGCACCTGCTACACTCCTTCTTAGGGCTGATATTCCACGCACCTGCTACACTCCTT +CTTAGGGCTGATATTCCACGCACCTGCTACACTCCTTCTTAGGGCTGATATTCCACGCAC diff --git a/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa.fai b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa.fai new file mode 100644 index 000000000..c2112667e --- /dev/null +++ b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/nm_tag_validation.fa.fai @@ -0,0 +1 @@ +17 4200 14 60 61 From 0c282b8395d4654da2fe8678c2fae36efb40d33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 10 Mar 2017 16:26:21 +0100 Subject: [PATCH 087/137] Tribble/Tabix index path support (#810) * Path support for tribble/tabix index It is now possible to write / read tribble and tabix indexes from arbitrary `Paths` as well as files This is not a backwards compatible change. Anyone who implemented tribbles `Index` will need to update their implementations by implementing 2 new methods. * `write( Path)` * `writeBasedOnFeaturePath()` The original file based versions of these methods have been reimplemented as default methods delegating to the new `Path` based methods and in most cases can be removed. * several protected fields have changed in the Index related classes have changed to use Paths instead Files --- .../java/htsjdk/tribble/TabixFeatureReader.java | 1 - src/main/java/htsjdk/tribble/Tribble.java | 45 ++++++++++---- .../tribble/TribbleIndexedFeatureReader.java | 2 - .../java/htsjdk/tribble/index/AbstractIndex.java | 72 ++++++++++++++++------ .../htsjdk/tribble/index/DynamicIndexCreator.java | 23 ++++--- src/main/java/htsjdk/tribble/index/Index.java | 30 ++++++++- .../index/interval/IntervalIndexCreator.java | 19 ++++-- .../tribble/index/interval/IntervalTreeIndex.java | 10 +++ .../htsjdk/tribble/index/linear/LinearIndex.java | 22 ++++++- .../tribble/index/linear/LinearIndexCreator.java | 17 +++-- .../htsjdk/tribble/index/tabix/TabixIndex.java | 33 +++++++--- src/test/java/htsjdk/tribble/index/IndexTest.java | 32 ++++++++++ 12 files changed, 240 insertions(+), 66 deletions(-) diff --git a/src/main/java/htsjdk/tribble/TabixFeatureReader.java b/src/main/java/htsjdk/tribble/TabixFeatureReader.java index 889fdc22b..e72243325 100644 --- a/src/main/java/htsjdk/tribble/TabixFeatureReader.java +++ b/src/main/java/htsjdk/tribble/TabixFeatureReader.java @@ -23,7 +23,6 @@ */ package htsjdk.tribble; -import htsjdk.samtools.seekablestream.SeekableStreamFactory; import htsjdk.samtools.util.BlockCompressedInputStream; import htsjdk.samtools.util.RuntimeIOException; import htsjdk.tribble.readers.*; diff --git a/src/main/java/htsjdk/tribble/Tribble.java b/src/main/java/htsjdk/tribble/Tribble.java index 468f55d77..f2c07a248 100644 --- a/src/main/java/htsjdk/tribble/Tribble.java +++ b/src/main/java/htsjdk/tribble/Tribble.java @@ -27,6 +27,7 @@ import htsjdk.tribble.util.TabixUtils; import java.io.File; +import java.nio.file.Path; /** * Common, tribble wide constants and static functions @@ -37,9 +38,9 @@ private Tribble() { } // can't be instantiated public final static String STANDARD_INDEX_EXTENSION = ".idx"; /** - * Return the name of the index file for the provided vcf {@code filename} + * Return the name of the index file for the provided {@code filename} * Does not actually create an index - * @param filename name of the vcf file + * @param filename name of the file * @return non-null String representing the index filename */ public static String indexFile(final String filename) { @@ -47,9 +48,9 @@ public static String indexFile(final String filename) { } /** - * Return the File of the index file for the provided vcf {@code file} + * Return the File of the index file for the provided {@code file} * Does not actually create an index - * @param file the vcf file + * @param file the file * @return a non-null File representing the index */ public static File indexFile(final File file) { @@ -57,9 +58,19 @@ public static File indexFile(final File file) { } /** - * Return the name of the tabix index file for the provided vcf {@code filename} + * Return the name of the index file for the provided {@code path} * Does not actually create an index - * @param filename name of the vcf file + * @param path the path + * @return Path representing the index filename + */ + public static Path indexPath(final Path path) { + return path.getFileSystem().getPath(indexFile(path.toAbsolutePath().toString())); + } + + /** + * Return the name of the tabix index file for the provided {@code filename} + * Does not actually create an index + * @param filename name of the file * @return non-null String representing the index filename */ public static String tabixIndexFile(final String filename) { @@ -67,9 +78,9 @@ public static String tabixIndexFile(final String filename) { } /** - * Return the File of the tabix index file for the provided vcf {@code file} + * Return the File of the tabix index file for the provided {@code file} * Does not actually create an index - * @param file the vcf file + * @param file the file * @return a non-null File representing the index */ public static File tabixIndexFile(final File file) { @@ -77,9 +88,19 @@ public static File tabixIndexFile(final File file) { } /** - * Return the name of the index file for the provided vcf {@code filename} and {@code extension} + * Return the name of the tabix index file for the provided {@code path} + * Does not actually create an index + * @param path the path + * @return Path representing the index filename + */ + public static Path tabixIndexPath(final Path path) { + return path.getFileSystem().getPath(tabixIndexFile(path.toAbsolutePath().toString())); + } + + /** + * Return the name of the index file for the provided {@code filename} and {@code extension} * Does not actually create an index - * @param filename name of the vcf file + * @param filename name of the file * @param extension the extension to use for the index * @return non-null String representing the index filename */ @@ -88,9 +109,9 @@ private static String indexFile(final String filename, final String extension) { } /** - * Return the File of the index file for the provided vcf {@code file} and {@code extension} + * Return the File of the index file for the provided {@code file} and {@code extension} * Does not actually create an index - * @param file the vcf file + * @param file the file * @param extension the extension to use for the index * @return a non-null File representing the index */ diff --git a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java index 365cc281a..7c39faa04 100644 --- a/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java +++ b/src/main/java/htsjdk/tribble/TribbleIndexedFeatureReader.java @@ -33,11 +33,9 @@ import htsjdk.tribble.util.ParsingUtils; import java.io.BufferedInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.channels.SeekableByteChannel; import java.util.ArrayList; diff --git a/src/main/java/htsjdk/tribble/index/AbstractIndex.java b/src/main/java/htsjdk/tribble/index/AbstractIndex.java index 5ae5492d6..b1cc1364c 100644 --- a/src/main/java/htsjdk/tribble/index/AbstractIndex.java +++ b/src/main/java/htsjdk/tribble/index/AbstractIndex.java @@ -18,6 +18,9 @@ package htsjdk.tribble.index; +import htsjdk.samtools.util.IOUtil; +import htsjdk.samtools.util.Log; +import htsjdk.samtools.util.RuntimeIOException; import htsjdk.tribble.Tribble; import htsjdk.tribble.TribbleException; import htsjdk.tribble.util.LittleEndianInputStream; @@ -25,8 +28,9 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -67,11 +71,12 @@ private final static long NO_TS = -1L; protected int version; // Our version value - protected File indexedFile = null; // The file we've created this index for + protected Path indexedPath = null; // The file we've created this index for protected long indexedFileSize = NO_FILE_SIZE; // The size of the indexed file protected long indexedFileTS = NO_TS; // The timestamp protected String indexedFileMD5 = NO_MD5; // The MD5 value, generally not filled in (expensive to calc) protected int flags; + protected final Log logger = Log.getInstance(this.getClass()); public boolean hasFileSize() { return indexedFileSize != NO_FILE_SIZE; @@ -116,8 +121,8 @@ public boolean equalsIgnoreProperties(final Object obj) { return false; } - if (indexedFile != other.indexedFile && (indexedFile == null || !indexedFile.equals(other.indexedFile))) { - System.err.printf("equals indexedFile: this %s != other %s%n", indexedFile, other.indexedFile); + if (indexedPath != other.indexedPath && (indexedPath == null || !indexedPath.equals(other.indexedPath))) { + System.err.printf("equals indexedPath: this %s != other %s%n", indexedPath, other.indexedPath); return false; } @@ -159,18 +164,27 @@ public AbstractIndex() { * @param featureFile the feature file to create an index from */ public AbstractIndex(final String featureFile) { - this(new File(featureFile)); + this(); + try { + this.indexedPath = IOUtil.getPath(featureFile).toAbsolutePath(); + } catch (IOException e) { + throw new IllegalArgumentException("IO error: " + e.getMessage(), e); + } } public AbstractIndex(final File featureFile) { + this(featureFile.toPath()); + } + + public AbstractIndex(final Path featurePath) { this(); - this.indexedFile = featureFile; + this.indexedPath = featurePath.toAbsolutePath(); } public AbstractIndex(final AbstractIndex parent) { this(); this.version = parent.version; - this.indexedFile = parent.indexedFile; + this.indexedPath = parent.indexedPath; this.indexedFileSize = parent.indexedFileSize; this.indexedFileTS = parent.indexedFileTS; this.indexedFileMD5 = parent.indexedFileMD5; @@ -200,8 +214,18 @@ public boolean isCurrentVersion() { return version == VERSION; } + /** + * Gets the indexed file. + * @throws UnsupportedOperationException if the path cannot be represented as a file. + * @deprecated on 03/2017. Use {@link #getIndexedPath()} instead. + */ + @Deprecated public File getIndexedFile() { - return indexedFile; + return getIndexedPath().toFile(); + } + + public Path getIndexedPath() { + return indexedPath; } public long getIndexedFileSize() { @@ -234,10 +258,14 @@ public boolean containsChromosome(final String chr) { } public void finalizeIndex() { - // these two functions must be called now because the file may be being written during on the fly indexing - if (indexedFile != null) { - this.indexedFileSize = indexedFile.length(); - this.indexedFileTS = indexedFile.lastModified(); + try { + // these two functions must be called now because the file may be being written during on the fly indexing + if (indexedPath != null) { + this.indexedFileSize = Files.size(indexedPath); + this.indexedFileTS = Files.getLastModifiedTime(indexedPath).toMillis(); + } + } catch (IOException e) { + throw new RuntimeIOException(e); } } @@ -251,7 +279,7 @@ private void writeHeader(final LittleEndianOutputStream dos) throws IOException dos.writeInt(MAGIC_NUMBER); dos.writeInt(getType()); dos.writeInt(version); - dos.writeString(indexedFile.getAbsolutePath()); + dos.writeString(indexedPath.toUri().toString()); dos.writeLong(indexedFileSize); dos.writeLong(indexedFileTS); dos.writeString(indexedFileMD5); @@ -274,7 +302,7 @@ private void writeHeader(final LittleEndianOutputStream dos) throws IOException private void readHeader(final LittleEndianInputStream dis) throws IOException { version = dis.readInt(); - indexedFile = new File(dis.readString()); + indexedPath = IOUtil.getPath(dis.readString()); indexedFileSize = dis.readLong(); indexedFileTS = dis.readLong(); indexedFileMD5 = dis.readString(); @@ -349,18 +377,22 @@ public void write(final LittleEndianOutputStream stream) throws IOException { } @Override - public void write(final File idxFile) throws IOException { - try(final LittleEndianOutputStream idxStream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)))) { + public void write(final Path idxPath) throws IOException { + try(final LittleEndianOutputStream idxStream = new LittleEndianOutputStream(new BufferedOutputStream(Files.newOutputStream(idxPath)))) { write(idxStream); } } @Override - public void writeBasedOnFeatureFile(final File featureFile) throws IOException { - if (!featureFile.isFile()) return; - write(Tribble.indexFile(featureFile)); + public void writeBasedOnFeaturePath(final Path featurePath) throws IOException { + if (!Files.isRegularFile(featurePath)) { + logger.warn("Index not written into ", featurePath); + return; + } + write(Tribble.indexPath(featurePath)); } + public void read(final LittleEndianInputStream dis) throws IOException { try { readHeader(dis); @@ -386,7 +418,7 @@ public void read(final LittleEndianInputStream dis) throws IOException { } protected void printIndexInfo() { - System.out.println(String.format("Index for %s with %d indices", indexedFile, chrIndices.size())); + System.out.println(String.format("Index for %s with %d indices", indexedPath, chrIndices.size())); final BlockStats stats = getBlockStats(true); System.out.println(String.format(" total blocks %d", stats.total)); System.out.println(String.format(" total empty blocks %d", stats.empty)); diff --git a/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java b/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java index 3552fbb4f..17274ace6 100644 --- a/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/DynamicIndexCreator.java @@ -31,6 +31,7 @@ import htsjdk.tribble.util.MathUtils; import java.io.File; +import java.nio.file.Path; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -56,13 +57,15 @@ MathUtils.RunningStat stats = new MathUtils.RunningStat(); long basesSeen = 0; Feature lastFeature = null; - File inputFile; - public DynamicIndexCreator(final File inputFile, final IndexFactory.IndexBalanceApproach iba) { + public DynamicIndexCreator(final Path inputPath, final IndexFactory.IndexBalanceApproach iba) { this.iba = iba; // get a list of index creators - this.inputFile = inputFile; - creators = getIndexCreators(inputFile,iba); + creators = getIndexCreators(inputPath, iba); + } + + public DynamicIndexCreator(final File inputFile, final IndexFactory.IndexBalanceApproach iba) { + this(inputFile.toPath(), iba); } @Override @@ -90,19 +93,19 @@ public Index finalizeIndex(final long finalFilePosition) { /** * create a list of index creators (initialized) representing the common index types we'd suspect they'd like to use - * @param inputFile the input file to use to create the indexes + * @param inputPath the input path to use to create the indexes * @return a map of index type to the best index for that balancing approach */ - private Map getIndexCreators(final File inputFile, final IndexFactory.IndexBalanceApproach iba) { + private Map getIndexCreators(final Path inputPath, final IndexFactory.IndexBalanceApproach iba) { final Map creators = new HashMap(); if (iba == IndexFactory.IndexBalanceApproach.FOR_SIZE) { // add a linear index with the default bin size - final LinearIndexCreator linearNormal = new LinearIndexCreator(inputFile, LinearIndexCreator.DEFAULT_BIN_WIDTH); + final LinearIndexCreator linearNormal = new LinearIndexCreator(inputPath, LinearIndexCreator.DEFAULT_BIN_WIDTH); creators.put(IndexFactory.IndexType.LINEAR,linearNormal); // create a tree index with the default size - final IntervalIndexCreator treeNormal = new IntervalIndexCreator(inputFile, IntervalIndexCreator.DEFAULT_FEATURE_COUNT); + final IntervalIndexCreator treeNormal = new IntervalIndexCreator(inputPath, IntervalIndexCreator.DEFAULT_FEATURE_COUNT); creators.put(IndexFactory.IndexType.INTERVAL_TREE,treeNormal); } @@ -111,12 +114,12 @@ public Index finalizeIndex(final long finalFilePosition) { if (iba == IndexFactory.IndexBalanceApproach.FOR_SEEK_TIME) { // create a linear index with a small bin size final LinearIndexCreator linearSmallBin = - new LinearIndexCreator(inputFile, Math.max(200, LinearIndexCreator.DEFAULT_BIN_WIDTH / 4)); + new LinearIndexCreator(inputPath, Math.max(200, LinearIndexCreator.DEFAULT_BIN_WIDTH / 4)); creators.put(IndexFactory.IndexType.LINEAR,linearSmallBin); // create a tree index with a small index size final IntervalIndexCreator treeSmallBin = - new IntervalIndexCreator(inputFile, Math.max(20, IntervalIndexCreator.DEFAULT_FEATURE_COUNT / 8)); + new IntervalIndexCreator(inputPath, Math.max(20, IntervalIndexCreator.DEFAULT_FEATURE_COUNT / 8)); creators.put(IndexFactory.IndexType.INTERVAL_TREE,treeSmallBin); } diff --git a/src/main/java/htsjdk/tribble/index/Index.java b/src/main/java/htsjdk/tribble/index/Index.java index ca6cc60d3..c5a63ff6e 100644 --- a/src/main/java/htsjdk/tribble/index/Index.java +++ b/src/main/java/htsjdk/tribble/index/Index.java @@ -27,6 +27,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -72,17 +73,42 @@ /** * Writes the index into a file. * + * Default implementation delegates to {@link #write(Path)} + * * @param idxFile Where to write the index. * @throws IOException if the index is unable to write to the specified file */ - public void write(final File idxFile) throws IOException; + public default void write(final File idxFile) throws IOException { + write(idxFile.toPath()); + } + + /** + * Writes the index into a path. + * + * @param indexPath Where to write the index. + * @throws IOException if the index is unable to write to the specified path. + */ + public void write(final Path indexPath) throws IOException; /** * Write an appropriately named and located Index file based on the name and location of the featureFile. * If featureFile is not a normal file, the index will silently not be written. + * + * Default implementation delegates to {@link #writeBasedOnFeaturePath(Path)} + * * @param featureFile */ - public void writeBasedOnFeatureFile(File featureFile) throws IOException; + public default void writeBasedOnFeatureFile(File featureFile) throws IOException { + writeBasedOnFeaturePath(featureFile.toPath()); + } + + /** + * Write an appropriately named and located Index file based on the name and location of the featureFile. + * If featureFile is not a normal file, the index will silently not be written. + * + * @param featurePath + */ + public void writeBasedOnFeaturePath(Path featurePath) throws IOException; /** * @return get the list of properties for this index. Returns null if no properties. diff --git a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java index 01219040c..58e2f87ee 100644 --- a/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/interval/IntervalIndexCreator.java @@ -25,6 +25,7 @@ import htsjdk.tribble.index.interval.IntervalTreeIndex.ChrIndex; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedList; @@ -51,15 +52,23 @@ private final ArrayList intervals = new ArrayList(); - File inputFile; + Path inputPath; - public IntervalIndexCreator(final File inputFile, final int featuresPerInterval) { - this.inputFile = inputFile; + public IntervalIndexCreator(final Path inputPath, final int featuresPerInterval) { + this.inputPath = inputPath; this.featuresPerInterval = featuresPerInterval; } + public IntervalIndexCreator(final File inputFile, final int featuresPerInterval) { + this(inputFile.toPath(), featuresPerInterval); + } + public IntervalIndexCreator(final File inputFile) { - this(inputFile, DEFAULT_FEATURE_COUNT); + this(inputFile.toPath()); + } + + public IntervalIndexCreator(final Path inputPath) { + this(inputPath, DEFAULT_FEATURE_COUNT); } @Override @@ -108,7 +117,7 @@ private void addIntervalsToLastChr(final long currentPos) { */ @Override public Index finalizeIndex(final long finalFilePosition) { - final IntervalTreeIndex featureIndex = new IntervalTreeIndex(inputFile.getAbsolutePath()); + final IntervalTreeIndex featureIndex = new IntervalTreeIndex(inputPath); // dump the remaining bins to the index addIntervalsToLastChr(finalFilePosition); featureIndex.setChrIndex(chrList); diff --git a/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java b/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java index 9a4206ebf..c4b2865dc 100644 --- a/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java +++ b/src/main/java/htsjdk/tribble/index/interval/IntervalTreeIndex.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -57,6 +58,15 @@ public IntervalTreeIndex(final InputStream inputStream) throws IOException { * * @param featureFile File which we are indexing */ + public IntervalTreeIndex(final Path featureFile) { + super(featureFile); + } + + /** + * Prepare to build an index. + * + * @param featureFile File which we are indexing + */ public IntervalTreeIndex(final String featureFile) { super(featureFile); } diff --git a/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java b/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java index 5047ab61a..3d7905af1 100644 --- a/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java +++ b/src/main/java/htsjdk/tribble/index/linear/LinearIndex.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -71,12 +72,21 @@ * @param indices * @param featureFile */ - public LinearIndex(final List indices, final File featureFile) { - super(featureFile.getAbsolutePath()); + public LinearIndex(final List indices, final Path featureFile) { + super(featureFile); for (final ChrIndex index : indices) chrIndices.put(index.getName(), index); } + /** + * Initialize using the specified {@code indices} + * @param indices + * @param featureFile + */ + public LinearIndex(final List indices, final File featureFile) { + this(indices, featureFile.toPath()); + } + private LinearIndex(final LinearIndex parent, final List indices) { super(parent); for (final ChrIndex index : indices) @@ -92,6 +102,14 @@ public LinearIndex(final String featureFile) { } /** + * Initialize with default parameters + * @param featurePath Path for which this is an index + */ + public LinearIndex(final Path featurePath) { + super(featurePath); + } + + /** * Load from file. * @param inputStream This method assumes that the input stream is already buffered as appropriate. */ diff --git a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java index daad6cce6..9109705d2 100644 --- a/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java +++ b/src/main/java/htsjdk/tribble/index/linear/LinearIndexCreator.java @@ -29,6 +29,7 @@ import htsjdk.tribble.index.TribbleIndexCreator; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedList; @@ -43,20 +44,28 @@ private int binWidth = DEFAULT_BIN_WIDTH; // the input file - private final File inputFile; + private final Path inputFile; private final LinkedList chrList = new LinkedList(); private int longestFeature= 0; private final ArrayList blocks = new ArrayList(); - public LinearIndexCreator(final File inputFile, final int binSize) { - this.inputFile = inputFile; + public LinearIndexCreator(final Path inputPath, final int binSize) { + this.inputFile = inputPath; binWidth = binSize; } + public LinearIndexCreator(final File inputFile, final int binSize) { + this(inputFile.toPath(), binSize); + } + public LinearIndexCreator(final File inputFile) { - this(inputFile, DEFAULT_BIN_WIDTH); + this(inputFile.toPath()); + } + + public LinearIndexCreator(final Path inputPath) { + this(inputPath, DEFAULT_BIN_WIDTH); } /** diff --git a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java index 044cefe61..43b1a2b40 100644 --- a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java +++ b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java @@ -30,7 +30,10 @@ import htsjdk.samtools.util.BlockCompressedInputStream; import htsjdk.samtools.util.BlockCompressedOutputStream; import htsjdk.samtools.util.CloserUtil; +import htsjdk.samtools.util.IOUtil; +import htsjdk.samtools.util.Log; import htsjdk.samtools.util.StringUtil; +import htsjdk.tribble.Tribble; import htsjdk.tribble.TribbleException; import htsjdk.tribble.index.Block; import htsjdk.tribble.index.Index; @@ -44,6 +47,8 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; /** @@ -61,6 +66,8 @@ MAGIC_NUMBER = bb.order(ByteOrder.LITTLE_ENDIAN).getInt(); } + private static final Log LOGGER = Log.getInstance(TabixIndex.class); + private final TabixFormat formatSpec; private final List sequenceNames; private final BinningIndexContent[] indices; @@ -95,6 +102,13 @@ public TabixIndex(final File tabixFile) throws IOException { this(new BlockCompressedInputStream(tabixFile), true); } + /** + * Convenient ctor that opens the path, wraps with with BGZF reader, and closes after reading index. + */ + public TabixIndex(final Path tabixPath) throws IOException { + this(new BlockCompressedInputStream(Files.newInputStream(tabixPath)), true); + } + private TabixIndex(final InputStream inputStream, final boolean closeInputStream) throws IOException { final LittleEndianInputStream dis = new LittleEndianInputStream(inputStream); if (dis.readInt() != MAGIC_NUMBER) { @@ -199,24 +213,27 @@ public TabixFormat getFormatSpec() { /** * Writes the index with BGZF. * - * @param tabixFile Where to write the index. + * @param tabixPath Where to write the index. */ @Override - public void write(final File tabixFile) throws IOException { - try(final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(tabixFile))) { + public void write(final Path tabixPath) throws IOException { + try(final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(Files.newOutputStream(tabixPath), null))) { write(los); } } /** - * Writes to a file with appropriate name and directory based on feature file. + * Writes to a path with appropriate name and directory based on feature path. * - * @param featureFile File being indexed. + * @param featurePath Path being indexed. */ @Override - public void writeBasedOnFeatureFile(final File featureFile) throws IOException { - if (!featureFile.isFile()) return; - write(new File(featureFile.getAbsolutePath() + TabixUtils.STANDARD_INDEX_EXTENSION)); + public void writeBasedOnFeaturePath(final Path featurePath) throws IOException { + if (!Files.isRegularFile(featurePath)) { + LOGGER.warn("Index not written into ", featurePath); + return; + } + write(Tribble.tabixIndexPath(featurePath)); } /** diff --git a/src/test/java/htsjdk/tribble/index/IndexTest.java b/src/test/java/htsjdk/tribble/index/IndexTest.java index aa179a9a2..56200e672 100644 --- a/src/test/java/htsjdk/tribble/index/IndexTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexTest.java @@ -1,10 +1,14 @@ package htsjdk.tribble.index; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import htsjdk.samtools.util.IOUtil; +import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.TestUtils; import htsjdk.tribble.Tribble; import htsjdk.tribble.bed.BEDCodec; +import htsjdk.tribble.index.interval.IntervalTreeIndex; import htsjdk.tribble.index.linear.LinearIndex; import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.index.tabix.TabixIndex; @@ -18,6 +22,11 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -94,4 +103,27 @@ public void testWriteIndex(final File inputFile, final IndexFactory.IndexType ty index.write(new LittleEndianOutputStream(nullOutputStrem)); } + @Test(dataProvider = "writeIndexData") + public void testWritePathIndex(final File inputFile, final IndexFactory.IndexType type, final FeatureCodec codec) throws Exception { + try (final FileSystem fs = Jimfs.newFileSystem("test", Configuration.unix())) { + // create the index + final Index index = IndexFactory.createIndex(inputFile, codec, type); + final Path path = fs.getPath(inputFile.getName() + ".index"); + // write the index to a file + index.write(path); + + // test if the index does not blow up with the path constructor + switch (type) { + case TABIX: + new TabixIndex(path); + break; + case LINEAR: + new LinearIndex(path); + break; + case INTERVAL_TREE: + new IntervalTreeIndex(path); + break; + } + } + } } From 8c97c994f57bcff1446a77c7a1e9715ceb9d1917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 10 Mar 2017 16:50:38 +0100 Subject: [PATCH 088/137] Update javadoc for Feature (#818) * Update javadoc for Feature --- src/main/java/htsjdk/tribble/Feature.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/tribble/Feature.java b/src/main/java/htsjdk/tribble/Feature.java index 941790f34..9ed852b14 100644 --- a/src/main/java/htsjdk/tribble/Feature.java +++ b/src/main/java/htsjdk/tribble/Feature.java @@ -27,13 +27,14 @@ import htsjdk.samtools.util.Locatable; /** - * Represents a locus on a reference sequence. All Features are expected to return 1-based closed-ended intervals. + * Marker interface for Locatables with Tribble support. A Feature represents a record in a tribble-supported file format. + * As {@link Locatable}, represents a locus on a reference sequence and is expected to return 1-based closed-ended intervals. */ public interface Feature extends Locatable { /** * Return the features reference sequence name, e.g chromosome or contig - * @deprecated use getContig() instead + * @deprecated on 03/2015. Use getContig() instead. */ @Deprecated default public String getChr() { From bbab32dbdf0b9f1564871803822f41f93c58e4c2 Mon Sep 17 00:00:00 2001 From: Len Trigg Date: Sat, 25 Mar 2017 10:49:42 +1300 Subject: [PATCH 089/137] Adding getSAMString to AbstractSAMHeaderRecord (#827) * Added getSAMString as abstract method to AbstractSAMHeaderRecord this returns a String that represents the SAM format encoding of the header record * new methods on SAMTextHeaderCodec to encode SamHeaderRecords as Strings * BREAKING CHANGE: any subclasses of AbstractSAMHeaderRecord in downstream projects will have to implement getSAMString() --- .../htsjdk/samtools/AbstractSAMHeaderRecord.java | 9 +++-- src/main/java/htsjdk/samtools/SAMFileHeader.java | 9 +++-- .../java/htsjdk/samtools/SAMProgramRecord.java | 6 ++++ .../java/htsjdk/samtools/SAMReadGroupRecord.java | 5 +++ .../java/htsjdk/samtools/SAMSequenceRecord.java | 12 ++++--- .../java/htsjdk/samtools/SAMTextHeaderCodec.java | 29 +++++++++------ .../java/htsjdk/samtools/SAMProgramRecordTest.java | 42 ++++++++++++++++++++++ .../htsjdk/samtools/SAMReadGroupRecordTest.java | 42 ++++++++++++++++++++++ .../htsjdk/samtools/SAMSequenceRecordTest.java | 42 ++++++++++++++++++++++ 9 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/SAMProgramRecordTest.java create mode 100644 src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java create mode 100644 src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java diff --git a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java index 769a7a735..7078bf1dc 100644 --- a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java +++ b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java @@ -23,13 +23,12 @@ */ package htsjdk.samtools; +import javax.xml.bind.annotation.XmlTransient; import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import javax.xml.bind.annotation.XmlTransient; - /** * Base class for the various concrete records in a SAM header, providing uniform * access to the attributes. @@ -113,4 +112,10 @@ protected int attributesHashCode() { @Override public String toString() { return getClass().getSimpleName() + this.mAttributes.toString(); } + + /** + * Returns the record in the SAM line-based text format. Fields are + * separated by '\t' characters. The String is NOT terminated by '\n'. + */ + abstract public String getSAMString(); } diff --git a/src/main/java/htsjdk/samtools/SAMFileHeader.java b/src/main/java/htsjdk/samtools/SAMFileHeader.java index 41eed4a4c..b94598185 100644 --- a/src/main/java/htsjdk/samtools/SAMFileHeader.java +++ b/src/main/java/htsjdk/samtools/SAMFileHeader.java @@ -358,9 +358,14 @@ public int hashCode() { public final SAMFileHeader clone() { final SAMTextHeaderCodec codec = new SAMTextHeaderCodec(); codec.setValidationStringency(ValidationStringency.SILENT); + return codec.decode(new StringLineReader(getSAMString()), "SAMFileHeader.clone"); + } + + @Override + public String getSAMString() { final StringWriter stringWriter = new StringWriter(); - codec.encode(stringWriter, this); - return codec.decode(new StringLineReader(stringWriter.toString()), "SAMFileHeader.clone"); + new SAMTextHeaderCodec().encode(stringWriter, this); + return stringWriter.toString(); } /** Little class to generate program group IDs */ diff --git a/src/main/java/htsjdk/samtools/SAMProgramRecord.java b/src/main/java/htsjdk/samtools/SAMProgramRecord.java index 91d0dac44..f5ddd964a 100644 --- a/src/main/java/htsjdk/samtools/SAMProgramRecord.java +++ b/src/main/java/htsjdk/samtools/SAMProgramRecord.java @@ -131,4 +131,10 @@ public int hashCode() { Set getStandardTags() { return STANDARD_TAGS; } + + + @Override + public String getSAMString() { + return new SAMTextHeaderCodec().getPGLine(this); + } } diff --git a/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java b/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java index bae3c4f62..14f1c50e3 100644 --- a/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java +++ b/src/main/java/htsjdk/samtools/SAMReadGroupRecord.java @@ -163,5 +163,10 @@ public int hashCode() { Set getStandardTags() { return STANDARD_TAGS; } + + @Override + public String getSAMString() { + return new SAMTextHeaderCodec().getRGLine(this); + } } diff --git a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java index 8e7ccf295..a4b4df236 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceRecord.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceRecord.java @@ -23,6 +23,9 @@ */ package htsjdk.samtools; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlValue; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; @@ -32,10 +35,6 @@ import java.util.Set; import java.util.regex.Pattern; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlValue; - /** * Header information about a reference sequence. Corresponds to @SQ header record in SAM text header. */ @@ -246,5 +245,10 @@ public String toString() { getAssembly() ); } + + @Override + public String getSAMString() { + return new SAMTextHeaderCodec().getSQLine(this); + } } diff --git a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java index fb4b02ac3..402ea3ce8 100644 --- a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java +++ b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java @@ -429,22 +429,27 @@ private void println(final String s) { } private void writePGLine(final SAMProgramRecord programRecord) { - if (programRecord == null) { - return; - } + println(getPGLine(programRecord)); + } + + protected String getPGLine(final SAMProgramRecord programRecord) { final String[] fields = new String[2 + programRecord.getAttributes().size()]; fields[0] = HEADER_LINE_START + HeaderRecordType.PG; fields[1] = SAMProgramRecord.PROGRAM_GROUP_ID_TAG + TAG_KEY_VALUE_SEPARATOR + programRecord.getProgramGroupId(); encodeTags(programRecord, fields, 2); - println(StringUtil.join(FIELD_SEPARATOR, fields)); + return StringUtil.join(FIELD_SEPARATOR, fields); } private void writeRGLine(final SAMReadGroupRecord readGroup) { - final String[] fields = new String[2 + readGroup.getAttributes().size()]; - fields[0] = HEADER_LINE_START + HeaderRecordType.RG; - fields[1] = SAMReadGroupRecord.READ_GROUP_ID_TAG + TAG_KEY_VALUE_SEPARATOR + readGroup.getReadGroupId(); - encodeTags(readGroup, fields, 2); - println(StringUtil.join(FIELD_SEPARATOR, fields)); + println(getRGLine(readGroup)); + } + + protected String getRGLine(final SAMReadGroupRecord readGroup) { + final String[] fields = new String[2 + readGroup.getAttributes().size()]; + fields[0] = HEADER_LINE_START + HeaderRecordType.RG; + fields[1] = SAMReadGroupRecord.READ_GROUP_ID_TAG + TAG_KEY_VALUE_SEPARATOR + readGroup.getReadGroupId(); + encodeTags(readGroup, fields, 2); + return StringUtil.join(FIELD_SEPARATOR, fields); } private void writeHDLine(final boolean keepExistingVersionNumber) { @@ -470,13 +475,17 @@ private void writeHDLine(final boolean keepExistingVersionNumber) { } private void writeSQLine(final SAMSequenceRecord sequenceRecord) { + println(getSQLine(sequenceRecord)); + } + + protected String getSQLine(final SAMSequenceRecord sequenceRecord) { final int numAttributes = sequenceRecord.getAttributes() != null ? sequenceRecord.getAttributes().size() : 0; final String[] fields = new String[3 + numAttributes]; fields[0] = HEADER_LINE_START + HeaderRecordType.SQ; fields[1] = SAMSequenceRecord.SEQUENCE_NAME_TAG + TAG_KEY_VALUE_SEPARATOR + sequenceRecord.getSequenceName(); fields[2] = SAMSequenceRecord.SEQUENCE_LENGTH_TAG + TAG_KEY_VALUE_SEPARATOR + Integer.toString(sequenceRecord.getSequenceLength()); encodeTags(sequenceRecord, fields, 3); - println(StringUtil.join(FIELD_SEPARATOR, fields)); + return StringUtil.join(FIELD_SEPARATOR, fields); } /** diff --git a/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java b/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java new file mode 100644 index 000000000..5d9a36893 --- /dev/null +++ b/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Copyright (c) 2017 The Broad Institute + * + * 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 htsjdk.samtools; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Test for SAMReadGroupRecordTest + */ +public class SAMProgramRecordTest { + + @Test + public void testGetSAMString() { + SAMProgramRecord r = new SAMProgramRecord("SW-eIV"); + r.setProgramName("telnet"); + r.setProgramVersion("0.17-40"); + r.setCommandLine("telnet towel.blinkenlights.nl"); + Assert.assertEquals("@PG\tID:SW-eIV\tPN:telnet\tVN:0.17-40\tCL:telnet towel.blinkenlights.nl", r.getSAMString()); + } +} diff --git a/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java b/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java new file mode 100644 index 000000000..c3a7423d3 --- /dev/null +++ b/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Copyright (c) 2017 The Broad Institute + * + * 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 htsjdk.samtools; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Test for SAMReadGroupRecordTest + */ +public class SAMReadGroupRecordTest { + + @Test + public void testGetSAMString() { + SAMReadGroupRecord r = new SAMReadGroupRecord("rg1"); + r.setSample("mysample"); + r.setPlatform("ILLUMINA"); + r.setDescription("my description"); + Assert.assertEquals("@RG\tID:rg1\tSM:mysample\tPL:ILLUMINA\tDS:my description", r.getSAMString()); + } +} diff --git a/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java new file mode 100644 index 000000000..1035d1b10 --- /dev/null +++ b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * + * Copyright (c) 2017 The Broad Institute + * + * 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 htsjdk.samtools; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Test for SAMReadGroupRecordTest + */ +public class SAMSequenceRecordTest { + + @Test + public void testGetSAMString() { + SAMSequenceRecord r = new SAMSequenceRecord("chr5_but_without_a_prefix", 271828); + r.setSpecies("Psephophorus terrypratchetti"); + r.setAssembly("GRCt01"); + r.setMd5("7a6dd3d307de916b477e7bf304ac22bc"); + Assert.assertEquals("@SQ\tSN:chr5_but_without_a_prefix\tLN:271828\tSP:Psephophorus terrypratchetti\tAS:GRCt01\tM5:7a6dd3d307de916b477e7bf304ac22bc", r.getSAMString()); + } +} From 9d343e792ef0c40072ba8a14c99caf12c41ca90e Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Thu, 30 Mar 2017 16:17:44 -0400 Subject: [PATCH 090/137] Make exception message informative in SAMUtils.charToCompressedBase (#836) * Make exception message informative * Add read name where accessible to the exception message --- src/main/java/htsjdk/samtools/BAMRecord.java | 7 ++++- src/main/java/htsjdk/samtools/BAMRecordCodec.java | 7 ++++- src/main/java/htsjdk/samtools/SAMUtils.java | 27 +++++++++-------- src/test/java/htsjdk/samtools/SAMUtilsTest.java | 36 +++++++++++++++++++++++ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/main/java/htsjdk/samtools/BAMRecord.java b/src/main/java/htsjdk/samtools/BAMRecord.java index 672e802c3..14b629595 100644 --- a/src/main/java/htsjdk/samtools/BAMRecord.java +++ b/src/main/java/htsjdk/samtools/BAMRecord.java @@ -342,7 +342,12 @@ private String decodeReadName() { return NULL_SEQUENCE; } final int basesOffset = readNameSize() + cigarSize(); - return SAMUtils.compressedBasesToBytes(mReadLength, mRestOfBinaryData, basesOffset); + try { + return SAMUtils.compressedBasesToBytes(mReadLength, mRestOfBinaryData, basesOffset); + } catch ( final IllegalArgumentException ex ) { + final String msg = ex.getMessage() + " in read: " + getReadName(); + throw new IllegalStateException(msg, ex); + } } /* methods for computing disk size of variably-sized elements, in order to locate diff --git a/src/main/java/htsjdk/samtools/BAMRecordCodec.java b/src/main/java/htsjdk/samtools/BAMRecordCodec.java index 5b0a408f6..e363a5b95 100644 --- a/src/main/java/htsjdk/samtools/BAMRecordCodec.java +++ b/src/main/java/htsjdk/samtools/BAMRecordCodec.java @@ -154,7 +154,12 @@ public void encode(final SAMRecord alignment) { // that it is specced as a uint. this.binaryCodec.writeInt(cigarElement); } - this.binaryCodec.writeBytes(SAMUtils.bytesToCompressedBases(alignment.getReadBases())); + try { + this.binaryCodec.writeBytes(SAMUtils.bytesToCompressedBases(alignment.getReadBases())); + } catch ( final IllegalArgumentException ex ) { + final String msg = ex.getMessage() + " in read: " + alignment.getReadName(); + throw new IllegalStateException(msg, ex); + } byte[] qualities = alignment.getBaseQualities(); if (qualities.length == 0) { qualities = new byte[alignment.getReadLength()]; diff --git a/src/main/java/htsjdk/samtools/SAMUtils.java b/src/main/java/htsjdk/samtools/SAMUtils.java index 25b6799c7..d439a4a83 100644 --- a/src/main/java/htsjdk/samtools/SAMUtils.java +++ b/src/main/java/htsjdk/samtools/SAMUtils.java @@ -111,8 +111,8 @@ public static final int MAX_PHRED_SCORE = 93; /** - * Convert from a byte array containing =AaCcGgTtNn represented as ASCII, to a byte array half as long, - * with =, A, C, G, T converted to 0, 1, 2, 4, 8, 15. + * Convert from a byte array containing =AaCcGgTtNnMmRrSsVvWwYyHhKkDdBb represented as ASCII, to a byte array half as long, + * with for example, =, A, C, G, T converted to 0, 1, 2, 4, 8, 15. * * @param readBases Bases as ASCII bytes. * @return New byte array with bases represented as nybbles, in BAM binary format. @@ -126,13 +126,13 @@ } // Last nybble if (i == readBases.length) { - compressedBases[i / 2] = charToCompressedBaseHigh((char) readBases[i - 1]); + compressedBases[i / 2] = charToCompressedBaseHigh(readBases[i - 1]); } return compressedBases; } /** - * Convert from a byte array with basese stored in nybbles, with =, A, C, G, T represented as 0, 1, 2, 4, 8, 15, + * Convert from a byte array with bases stored in nybbles, with for example,=, A, C, G, T, N represented as 0, 1, 2, 4, 8, 15, * to a a byte array containing =AaCcGgTtNn represented as ASCII. * * @param length Number of bases (not bytes) to convert. @@ -158,10 +158,11 @@ /** * Convert from ASCII byte to BAM nybble representation of a base in low-order nybble. * - * @param base One of =AaCcGgTtNn. + * @param base One of =AaCcGgTtNnMmRrSsVvWwYyHhKkDdBb. * @return Low-order nybble-encoded equivalent. + * @throws IllegalArgumentException if the base is not one of =AaCcGgTtNnMmRrSsVvWwYyHhKkDdBb. */ - private static byte charToCompressedBaseLow(final int base) { + private static byte charToCompressedBaseLow(final byte base) { switch (base) { case '=': return COMPRESSED_EQUAL_LOW; @@ -214,17 +215,18 @@ private static byte charToCompressedBaseLow(final int base) { case 'b': return COMPRESSED_B_LOW; default: - throw new IllegalArgumentException("Bad byte passed to charToCompressedBase: " + base); + throw new IllegalArgumentException("Bad base passed to charToCompressedBaseLow: " + Character.toString((char)base) + "(" + base + ")"); } } /** * Convert from ASCII byte to BAM nybble representation of a base in high-order nybble. * - * @param base One of =AaCcGgTtNn. + * @param base One of =AaCcGgTtNnMmRrSsVvWwYyHhKkDdBb. * @return High-order nybble-encoded equivalent. + * @throws IllegalArgumentException if the base is not one of =AaCcGgTtNnMmRrSsVvWwYyHhKkDdBb. */ - private static byte charToCompressedBaseHigh(final int base) { + private static byte charToCompressedBaseHigh(final byte base) { switch (base) { case '=': return COMPRESSED_EQUAL_HIGH; @@ -277,20 +279,21 @@ private static byte charToCompressedBaseHigh(final int base) { case 'b': return COMPRESSED_B_HIGH; default: - throw new IllegalArgumentException("Bad byte passed to charToCompressedBase: " + base); + throw new IllegalArgumentException("Bad base passed to charToCompressedBaseHigh: " + Character.toString((char)base) + "(" + base + ")"); } } /** * Returns the byte corresponding to a certain nybble * @param base One of COMPRESSED_*_LOW, a low-order nybble encoded base. - * @return ASCII base, one of ACGTN=. + * @return ASCII base, one of =ACGTNMRSVWYHKDB. + * @throws IllegalArgumentException if the base is not one of =ACGTNMRSVWYHKDB. */ private static byte compressedBaseToByte(byte base){ try{ return COMPRESSED_LOOKUP_TABLE[base]; }catch(IndexOutOfBoundsException e){ - throw new IllegalArgumentException("Bad byte passed to charToCompressedBase: " + base); + throw new IllegalArgumentException("Bad base passed to charToCompressedBase: " + Character.toString((char)base) + "(" + base + ")"); } } diff --git a/src/test/java/htsjdk/samtools/SAMUtilsTest.java b/src/test/java/htsjdk/samtools/SAMUtilsTest.java index 3be7e390c..e3fe72656 100644 --- a/src/test/java/htsjdk/samtools/SAMUtilsTest.java +++ b/src/test/java/htsjdk/samtools/SAMUtilsTest.java @@ -24,8 +24,10 @@ package htsjdk.samtools; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.Arrays; import java.util.List; public class SAMUtilsTest { @@ -244,7 +246,41 @@ public void testOtherCanonicalAlignments() { Assert.assertEquals(other.getAttribute(SAMTagUtil.getSingleton().NM),null); Assert.assertEquals(other.getCigarString(),"8M2S"); Assert.assertEquals(other.getInferredInsertSize(),-91);//100(mate) - 191(other) + } + + @Test() + public void testBytesToCompressedBases() { + final byte[] bases = new byte[]{'=', 'a', 'A', 'c', 'C', 'g', 'G', 't', 'T', 'n', 'N', '.', 'M', 'm', + 'R', 'r', 'S', 's', 'V', 'v', 'W', 'w', 'Y', 'y', 'H', 'h', 'K', 'k', 'D', 'd', 'B', 'b'}; + final byte[] compressedBases = SAMUtils.bytesToCompressedBases(bases); + String expectedCompressedBases = "[1, 18, 36, 72, -113, -1, 51, 85, 102, 119, -103, -86, -69, -52, -35, -18]"; + Assert.assertEquals(Arrays.toString(compressedBases), expectedCompressedBases); + } + + @DataProvider + public Object[][] testBadBase() { + return new Object[][]{ + {new byte[]{'>', 'A'}, '>'}, + {new byte[]{'A', '>'} , '>'} + }; + } + @Test(dataProvider = "testBadBase", expectedExceptions = IllegalArgumentException.class) + public void testBytesToCompressedBasesException(final byte[] bases, final char failingBase) { + try { + SAMUtils.bytesToCompressedBases(bases); + } catch ( final IllegalArgumentException ex ) { + Assert.assertTrue(ex.getMessage().contains(Character.toString(failingBase))); + throw ex; + } } + @Test + public void testCompressedBasesToBytes() { + final byte[] compressedBases = new byte[]{1, 18, 36, 72, -113, -1, 51, 85, 102, 119, -103, -86, -69, -52, -35, -18}; + final byte[] bytes = SAMUtils.compressedBasesToBytes(2*compressedBases.length, compressedBases, 0); + final byte[] expectedBases = new byte[]{'=', 'A', 'A', 'C', 'C', 'G', 'G', 'T', 'T', 'N', 'N', 'N', 'M', 'M', + 'R', 'R', 'S', 'S', 'V', 'V', 'W', 'W', 'Y', 'Y', 'H', 'H', 'K', 'K', 'D', 'D', 'B', 'B'}; + Assert.assertEquals(new String(bytes), new String(expectedBases)); + } } From 476adb281a2b051d8fa8b4fed6b8605ccb5984ff Mon Sep 17 00:00:00 2001 From: Len Trigg Date: Wed, 5 Apr 2017 07:00:23 +1200 Subject: [PATCH 091/137] Fix DateParserTest unit tests that were failing in my locale. (#832) The tests were failing due to use of the deprecated Date.parse() method. Replaced Date.parse() calls in this test with equivalent tests. Tests now pass. --- src/test/java/htsjdk/samtools/util/DateParserTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/htsjdk/samtools/util/DateParserTest.java b/src/test/java/htsjdk/samtools/util/DateParserTest.java index 71313c15b..04cfa7819 100644 --- a/src/test/java/htsjdk/samtools/util/DateParserTest.java +++ b/src/test/java/htsjdk/samtools/util/DateParserTest.java @@ -76,7 +76,6 @@ This W3C work (including software, documents, or other related items) is import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.text.DateFormat; import java.util.Date; /** @@ -107,8 +106,7 @@ private static void test(final Date date) { final Date dateRoundTrip = DateParser.parse(isodate); assertDatesAreClose(date, dateRoundTrip); - Assert.assertTrue(Math.abs(Date.parse(date.toString()) - Date.parse(dateRoundTrip.toString())) < 10); - + Assert.assertTrue(Math.abs(date.getTime() - dateRoundTrip.getTime()) < 10); } @DataProvider(name="dateDate") From 31b761ba0fc7790918877d755cfa79e20e2945d0 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Fri, 7 Apr 2017 13:11:35 -0400 Subject: [PATCH 092/137] Maintain ordering of header --- src/main/java/htsjdk/variant/vcf/VCFUtils.java | 8 ++------ src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/htsjdk/variant/vcf/VCFUtils.java b/src/main/java/htsjdk/variant/vcf/VCFUtils.java index c8eceeab5..72f757105 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFUtils.java +++ b/src/main/java/htsjdk/variant/vcf/VCFUtils.java @@ -32,26 +32,23 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; public class VCFUtils { public static Set smartMergeHeaders(final Collection headers, final boolean emitWarnings) throws IllegalStateException { // We need to maintain the order of the VCFHeaderLines, otherwise they will be scrambled in the returned Set. // This will cause problems for VCFHeader.getSequenceDictionary and anything else that implicitly relies on the line ordering. - final TreeMap map = new TreeMap(); // from KEY.NAME -> line + final LinkedHashMap map = new LinkedHashMap<>(); // from KEY.NAME -> line final HeaderConflictWarner conflictWarner = new HeaderConflictWarner(emitWarnings); // todo -- needs to remove all version headers from sources and add its own VCF version line for ( final VCFHeader source : headers ) { - //System.out.printf("Merging in header %s%n", source); for ( final VCFHeaderLine line : source.getMetaDataInSortedOrder()) { String key = line.getKey(); @@ -102,12 +99,11 @@ } } else { map.put(key, line); - //System.out.printf("Adding header line %s%n", line); } } } // returning a LinkedHashSet so that ordering will be preserved. Ensures the contig lines do not get scrambled. - return new LinkedHashSet(map.values()); + return new LinkedHashSet<>(map.values()); } /** diff --git a/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java b/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java index e9135cc72..89d07f48a 100644 --- a/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java +++ b/src/test/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java @@ -37,6 +37,7 @@ import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; @@ -120,10 +121,18 @@ public void testVCFHeaderSampleRenamingSingleSampleVCF() throws Exception { } } - @Test - public void testVCFHeaderDictionaryMerging() { - VCFHeader headerOne = new VCFFileReader(new File(variantTestDataRoot + "dbsnp_135.b37.1000.vcf"), false).getFileHeader(); - VCFHeader headerTwo = new VCFHeader(headerOne); // deep copy + @DataProvider + public Object[][] testVCFHeaderDictionaryMergingData() { + return new Object[][]{ + {"diagnosis_targets_testfile.vcf"}, // numerically ordered contigs + {"dbsnp_135.b37.1000.vcf"} // lexicographically ordered contigs + }; + } + + @Test(dataProvider = "testVCFHeaderDictionaryMergingData") + public void testVCFHeaderDictionaryMerging(final String vcfFileName) { + final VCFHeader headerOne = new VCFFileReader(new File(variantTestDataRoot + vcfFileName), false).getFileHeader(); + final VCFHeader headerTwo = new VCFHeader(headerOne); // deep copy final List sampleList = new ArrayList(); sampleList.addAll(headerOne.getSampleNamesInOrder()); From 656dc24930e1dc7fd0abd4c5e2abedf6f5074b35 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Sat, 15 Apr 2017 14:00:28 -0400 Subject: [PATCH 093/137] Enabling writing and running of tests with scalatest. (#789) --- .travis.yml | 2 +- build.gradle | 88 +++++--------- gradle/wrapper/gradle-wrapper.properties | 4 +- src/test/java/htsjdk/HtsjdkTest.java | 10 ++ .../htsjdk/cram/io/ExternalCompressionTest.java | 9 +- .../htsjdk/samtools/AbstractBAMFileIndexTest.java | 5 +- .../java/htsjdk/samtools/BAMCigarOverflowTest.java | 3 +- .../java/htsjdk/samtools/BAMFileIndexTest.java | 6 +- .../java/htsjdk/samtools/BAMFileWriterTest.java | 3 +- .../java/htsjdk/samtools/BAMIndexWriterTest.java | 3 +- src/test/java/htsjdk/samtools/BAMIteratorTest.java | 3 +- ...AMQueryMultipleIntervalsIteratorFilterTest.java | 3 +- .../java/htsjdk/samtools/BAMRemoteFileTest.java | 3 +- src/test/java/htsjdk/samtools/BinTest.java | 3 +- .../java/htsjdk/samtools/CRAMBAIIndexerTest.java | 3 +- .../java/htsjdk/samtools/CRAMCRAIIndexerTest.java | 5 +- .../java/htsjdk/samtools/CRAMComplianceTest.java | 3 +- .../samtools/CRAMContainerStreamWriterTest.java | 3 +- .../java/htsjdk/samtools/CRAMEdgeCasesTest.java | 3 +- .../java/htsjdk/samtools/CRAMFileBAIIndexTest.java | 3 +- .../htsjdk/samtools/CRAMFileCRAIIndexTest.java | 4 +- .../java/htsjdk/samtools/CRAMFileReaderTest.java | 3 +- .../java/htsjdk/samtools/CRAMFileWriterTest.java | 3 +- .../samtools/CRAMFileWriterWithIndexTest.java | 3 +- .../java/htsjdk/samtools/CRAMIndexQueryTest.java | 3 +- src/test/java/htsjdk/samtools/ChunkTest.java | 3 +- src/test/java/htsjdk/samtools/CigarCodecTest.java | 3 +- src/test/java/htsjdk/samtools/CigarTest.java | 3 +- .../htsjdk/samtools/DownsamplingIteratorTests.java | 3 +- .../htsjdk/samtools/DuplicateSetIteratorTest.java | 3 +- .../java/htsjdk/samtools/GenomicIndexUtilTest.java | 5 +- ...MergingSamRecordIteratorGroupCollisionTest.java | 3 +- .../samtools/MergingSamRecordIteratorTest.java | 3 +- .../htsjdk/samtools/ProgramRecordChainingTest.java | 3 +- .../samtools/SAMBinaryTagAndValueUnitTest.java | 3 +- src/test/java/htsjdk/samtools/SAMCloneTest.java | 3 +- .../htsjdk/samtools/SAMFileWriterFactoryTest.java | 3 +- src/test/java/htsjdk/samtools/SAMFlagTest.java | 3 +- .../java/htsjdk/samtools/SAMIntegerTagTest.java | 3 +- .../samtools/SAMRecordDuplicateComparatorTest.java | 3 +- .../java/htsjdk/samtools/SAMRecordUnitTest.java | 3 +- .../samtools/SAMSequenceDictionaryCodecTest.java | 7 +- .../htsjdk/samtools/SAMSequenceDictionaryTest.java | 14 +-- .../java/htsjdk/samtools/SAMTextReaderTest.java | 3 +- .../java/htsjdk/samtools/SAMTextWriterTest.java | 3 +- src/test/java/htsjdk/samtools/SAMUtilsTest.java | 3 +- .../htsjdk/samtools/SamFileHeaderMergerTest.java | 3 +- src/test/java/htsjdk/samtools/SamFilesTest.java | 4 +- .../java/htsjdk/samtools/SamFlagFieldTest.java | 5 +- .../samtools/SamHeaderRecordComparatorTest.java | 3 +- src/test/java/htsjdk/samtools/SamIndexesTest.java | 3 +- src/test/java/htsjdk/samtools/SamPairUtilTest.java | 3 +- .../java/htsjdk/samtools/SamReaderFactoryTest.java | 3 +- .../java/htsjdk/samtools/SamReaderSortTest.java | 3 +- src/test/java/htsjdk/samtools/SamReaderTest.java | 3 +- src/test/java/htsjdk/samtools/SamSpecIntTest.java | 3 +- src/test/java/htsjdk/samtools/SamStreamsTest.java | 5 +- .../SequenceNameTruncationAndValidationTest.java | 3 +- .../java/htsjdk/samtools/ValidateSamFileTest.java | 3 +- .../java/htsjdk/samtools/cram/CRAIEntryTest.java | 3 +- .../java/htsjdk/samtools/cram/CRAIIndexTest.java | 17 +-- .../samtools/cram/LosslessRoundTripTest.java | 21 +--- .../java/htsjdk/samtools/cram/VersionTest.java | 3 +- .../cram/build/CompressionHeaderFactoryTest.java | 3 +- .../samtools/cram/build/ContainerFactoryTest.java | 3 +- .../samtools/cram/build/ContainerParserTest.java | 3 +- .../htsjdk/samtools/cram/build/CramIOTest.java | 3 +- .../cram/encoding/huffman/codec/HuffmanTest.java | 3 +- .../samtools/cram/encoding/rans/RansTest.java | 3 +- .../java/htsjdk/samtools/cram/io/ITF8Test.java | 3 +- .../java/htsjdk/samtools/cram/io/LTF8Test.java | 3 +- .../cram/lossy/QualityScorePreservationTest.java | 3 +- .../samtools/cram/ref/EnaRefServiceTest.java | 3 +- .../cram/structure/CramCompressionRecordTest.java | 9 +- .../samtools/cram/structure/ReadTagTest.java | 10 +- .../htsjdk/samtools/cram/structure/SliceTests.java | 4 +- .../cram/structure/SubstitutionMatrixTest.java | 6 +- .../htsjdk/samtools/fastq/FastqRecordTest.java | 5 +- .../htsjdk/samtools/fastq/FastqWriterTest.java | 3 +- .../filter/FailsVendorReadQualityFilterTest.java | 3 +- .../samtools/filter/InsertSizeFilterTest.java | 3 +- .../filter/IntervalKeepPairFilterTest.java | 3 +- .../filter/JavascriptSamRecordFilterTest.java | 3 +- .../samtools/filter/MappingQualityFilterTest.java | 3 +- .../samtools/filter/OverclippedReadFilterTest.java | 3 +- .../samtools/filter/SolexaNoiseFilterTest.java | 3 +- .../java/htsjdk/samtools/filter/TagFilterTest.java | 5 +- .../htsjdk/samtools/liftover/LiftOverTest.java | 3 +- .../htsjdk/samtools/metrics/MetricBaseTest.java | 3 +- .../htsjdk/samtools/metrics/MetricsFileTest.java | 3 +- .../htsjdk/samtools/metrics/StringHeaderTest.java | 5 +- .../htsjdk/samtools/metrics/VersionHeaderTest.java | 5 +- .../samtools/reference/FastaSequenceFileTest.java | 3 +- .../samtools/reference/FastaSequenceIndexTest.java | 3 +- .../reference/IndexedFastaSequenceFileTest.java | 3 +- .../ReferenceSequenceFileFactoryTests.java | 3 +- .../reference/ReferenceSequenceFileWalkerTest.java | 3 +- .../samtools/reference/ReferenceSequenceTests.java | 3 +- .../seekablestream/SeekableBufferedStreamTest.java | 3 +- .../seekablestream/SeekableFTPStreamTest.java | 3 +- .../seekablestream/SeekableFileStreamTest.java | 3 +- .../seekablestream/SeekableMemoryStreamTest.java | 3 +- .../seekablestream/SeekablePathStreamTest.java | 4 +- .../seekablestream/SeekableStreamFactoryTest.java | 3 +- .../java/htsjdk/samtools/sra/AbstractSRATest.java | 3 +- .../samtools/util/AbstractLocusInfoTest.java | 3 +- .../util/AbstractLocusIteratorTestTemplate.java | 5 +- .../samtools/util/AbstractRecordAndOffsetTest.java | 3 +- .../util/AsyncBlockCompressedInputStreamTest.java | 3 +- .../samtools/util/AsyncBufferedIteratorTest.java | 3 +- .../java/htsjdk/samtools/util/AsyncWriterTest.java | 3 +- .../java/htsjdk/samtools/util/BinaryCodecTest.java | 3 +- .../util/BlockCompressedFilePointerUtilTest.java | 4 +- .../util/BlockCompressedInputStreamTest.java | 23 ++-- .../util/BlockCompressedOutputStreamTest.java | 3 +- .../util/BlockCompressedTerminatorTest.java | 3 +- .../java/htsjdk/samtools/util/CigarUtilTest.java | 3 +- .../samtools/util/CloseableIteratorTest.java | 3 +- .../java/htsjdk/samtools/util/CodeUtilTest.java | 3 +- .../htsjdk/samtools/util/ComparableTupleTest.java | 3 +- .../samtools/util/CoordSpanInputSteamTest.java | 3 +- .../java/htsjdk/samtools/util/DateParserTest.java | 5 +- .../samtools/util/EdgingRecordAndOffsetTest.java | 3 +- .../java/htsjdk/samtools/util/HistogramTest.java | 3 +- .../htsjdk/samtools/util/IntervalListTest.java | 3 +- .../htsjdk/samtools/util/IntervalTreeMapTest.java | 3 +- .../htsjdk/samtools/util/IntervalTreeTest.java | 3 +- src/test/java/htsjdk/samtools/util/IoUtilTest.java | 3 +- .../java/htsjdk/samtools/util/Iso8601DateTest.java | 3 +- src/test/java/htsjdk/samtools/util/IupacTest.java | 3 +- .../htsjdk/samtools/util/MergingIteratorTest.java | 3 +- .../htsjdk/samtools/util/OverlapDetectorTest.java | 3 +- .../samtools/util/PositionalOutputStreamTest.java | 5 +- .../samtools/util/QualityEncodingDetectorTest.java | 3 +- .../samtools/util/RelativeIso8601DateTest.java | 3 +- .../htsjdk/samtools/util/SequenceUtilTest.java | 3 +- .../samtools/util/SolexaQualityConverterTest.java | 3 +- .../samtools/util/SortingCollectionTest.java | 3 +- .../samtools/util/SortingLongCollectionTest.java | 3 +- .../htsjdk/samtools/util/StringLineReaderTest.java | 3 +- .../java/htsjdk/samtools/util/StringUtilTest.java | 122 ------------------- .../htsjdk/samtools/util/TrimmingUtilTest.java | 3 +- src/test/java/htsjdk/samtools/util/TupleTest.java | 5 +- .../htsjdk/tribble/AbstractFeatureReaderTest.java | 3 +- .../java/htsjdk/tribble/BinaryFeaturesTest.java | 3 +- .../java/htsjdk/tribble/FeatureReaderTest.java | 3 +- .../tribble/TribbleIndexFeatureReaderTest.java | 7 +- src/test/java/htsjdk/tribble/TribbleTest.java | 3 +- src/test/java/htsjdk/tribble/bed/BEDCodecTest.java | 3 +- .../htsjdk/tribble/index/IndexFactoryTest.java | 8 +- src/test/java/htsjdk/tribble/index/IndexTest.java | 9 +- .../tribble/index/interval/IntervalTreeTest.java | 3 +- .../tribble/index/linear/LinearIndexTest.java | 3 +- .../htsjdk/tribble/index/tabix/TabixIndexTest.java | 3 +- .../tribble/readers/AsciiLineReaderTest.java | 13 +- .../readers/LongLineBufferedReaderTest.java | 3 +- .../readers/PositionalBufferedStreamTest.java | 3 +- .../java/htsjdk/tribble/readers/ReaderTest.java | 3 +- .../readers/SynchronousLineReaderUnitTest.java | 3 +- .../htsjdk/tribble/readers/TabixReaderTest.java | 3 +- .../java/htsjdk/tribble/util/ParsingUtilsTest.java | 17 +-- .../htsjdk/tribble/util/ftp/FTPClientTest.java | 3 +- .../java/htsjdk/tribble/util/ftp/FTPUtilsTest.java | 3 +- .../util/popgen/HardyWeinbergCalculationTest.java | 3 +- .../htsjdk/variant/PrintVariantsExampleTest.java | 5 +- src/test/java/htsjdk/variant/VariantBaseTest.java | 3 +- .../utils/SAMSequenceDictionaryExtractorTest.java | 3 +- .../variantcontext/VariantContextTestProvider.java | 5 +- .../variantcontext/filter/CompoundFilterTest.java | 5 +- .../FilteringVariantContextIteratorTest.java | 3 +- .../filter/GenotypeQualityFilterTest.java | 3 +- .../filter/HeterozygosityFilterTest.java | 3 +- .../filter/JavascriptVariantFilterTest.java | 3 +- .../filter/PassingVariantFilterTest.java | 5 +- .../variantcontext/filter/SnpFilterTest.java | 3 +- .../writer/TabixOnTheFlyIndexCreationTest.java | 3 +- .../java/htsjdk/variant/vcf/VCFEncoderTest.java | 3 +- src/test/scala/htsjdk/UnitSpec.scala | 6 + .../htsjdk/samtools/util/StringUtilTest.scala | 134 +++++++++++++++++++++ 179 files changed, 575 insertions(+), 477 deletions(-) create mode 100644 src/test/java/htsjdk/HtsjdkTest.java delete mode 100644 src/test/java/htsjdk/samtools/util/StringUtilTest.java create mode 100644 src/test/scala/htsjdk/UnitSpec.scala create mode 100644 src/test/scala/htsjdk/samtools/util/StringUtilTest.scala diff --git a/.travis.yml b/.travis.yml index 9c046e353..270855ea7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ cache: - $HOME/.m2 jdk: - oraclejdk8 -script: ./gradlew testSRA jacocoTestReport; +script: ./gradlew test jacocoTestReport; after_success: - bash <(curl -s https://codecov.io/bash) - echo "TRAVIS_BRANCH='$TRAVIS_BRANCH'"; diff --git a/build.gradle b/build.gradle index ddd12a34d..9760a79fa 100644 --- a/build.gradle +++ b/build.gradle @@ -5,12 +5,14 @@ buildscript { } plugins { - id "java" + id 'java' + id 'scala' id 'maven' id 'signing' id 'jacoco' id 'com.palantir.git-version' version '0.5.1' id 'com.github.johnrengelman.shadow' version '1.2.3' + id 'com.github.maiflai.scalatest' version '0.15' } repositories { @@ -18,7 +20,6 @@ repositories { } jacocoTestReport { - dependsOn test group = "Reporting" description = "Generate Jacoco coverage reports after running tests." additionalSourceDirs = files(sourceSets.main.allJava.srcDirs) @@ -29,10 +30,6 @@ jacocoTestReport { } } -jacoco { - toolVersion = "0.7.5.201505241946" -} - dependencies { compile "org.apache.commons:commons-jexl:2.1.1" compile "commons-logging:commons-logging:1.1.1" @@ -41,6 +38,9 @@ dependencies { compile "org.tukaani:xz:1.5" compile "gov.nih.nlm.ncbi:ngs-java:1.2.4" + testCompile "org.scala-lang:scala-library:2.12.1" + testCompile "org.scalatest:scalatest_2.12:3.0.1" + testRuntime 'org.pegdown:pegdown:1.4.2' // Necessary for generating HTML reports with ScalaTest testCompile "org.testng:testng:6.9.9" testCompile "com.google.jimfs:jimfs:1.1" } @@ -67,70 +67,46 @@ jar { import org.gradle.internal.os.OperatingSystem; -tasks.withType(Test) { - outputs.upToDateWhen { false } // tests will always rerun - useTestNG() - - // set heap size for the test JVM(s) - minHeapSize = "1G" - maxHeapSize = "2G" +tasks.withType(Test) { task -> + task.outputs.upToDateWhen { false } // tests will always rerun - jvmArgs '-Djava.awt.headless=true' //this prevents awt from displaying a java icon while the tests are running + // Always run serially because there are some very badly behaved tests in HTSJDK that + // will cause errors and even deadlocks if run multi-threaded + task.maxParallelForks = 1 - if (System.env.CI == "true") { //if running under a CI output less into the logs - int count = 0 + // set heap size for the test JVM(s) + task.minHeapSize = "1G" + task.maxHeapSize = "2G" - beforeTest { descriptor -> - count++ - if( count % 100 == 0) { - logger.lifecycle("Finished "+ Integer.toString(count++) + " tests") - } - } - } else { - // show standard out and standard error of the test JVM(s) on the console - testLogging.showStandardStreams = true - beforeTest { descriptor -> - logger.lifecycle("Running Test: " + descriptor) - } + task.jvmArgs '-Djava.awt.headless=true' //this prevents awt from displaying a java icon while the tests are running +} - // listen to standard out and standard error of the test JVM(s) - onOutput { descriptor, event -> - logger.lifecycle("Test: " + descriptor + " produced standard out/err: " + event.message ) - } - } +test { + description = "Runs the unit tests other than the SRA tests" testLogging { - testLogging { - events "skipped", "failed" - exceptionFormat = "full" - } - afterSuite { desc, result -> - if (!desc.parent) { // will match the outermost suite - println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" - } - } + events "failed", "skipped" } -} -test { - description = "Runs the unit tests other than the SRA tests" + if (System.env.CI == "true") { + jvmArgs += '-Dsamjdk.sra_libraries_download=true' + } - useTestNG { - if( OperatingSystem.current().isUnix() ){ - excludeGroups "slow", "broken", "sra" - } else { - excludeGroups "slow", "broken", "unix", "sra" - } + tags { + exclude "slow" + exclude "broken" + if (System.env.CI == "false") exclude "sra" + if (!OperatingSystem.current().isUnix()) exclude "unix" } } task testSRA(type: Test) { - jvmArgs '-Dsamjdk.sra_libraries_download=true' + description = "Run the SRA tests" + jvmArgs += '-Dsamjdk.sra_libraries_download=true' - description "Run the SRA tests" - useTestNG { - configFailurePolicy 'continue' - includeGroups "sra" + tags { + exclude "slow" + exclude "broken" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7aff310b7..f08cd01bf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Nov 29 15:10:15 EST 2016 +#Fri Jan 20 17:10:11 EST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-all.zip diff --git a/src/test/java/htsjdk/HtsjdkTest.java b/src/test/java/htsjdk/HtsjdkTest.java new file mode 100644 index 000000000..4da626b7e --- /dev/null +++ b/src/test/java/htsjdk/HtsjdkTest.java @@ -0,0 +1,10 @@ +package htsjdk; + +import org.scalatest.testng.TestNGSuite; + +/** + * Base class for all Java tests in HTSJDK. + */ +public class HtsjdkTest extends TestNGSuite { + +} diff --git a/src/test/java/htsjdk/cram/io/ExternalCompressionTest.java b/src/test/java/htsjdk/cram/io/ExternalCompressionTest.java index 09f6e4905..60a65197d 100644 --- a/src/test/java/htsjdk/cram/io/ExternalCompressionTest.java +++ b/src/test/java/htsjdk/cram/io/ExternalCompressionTest.java @@ -1,16 +1,15 @@ -package htsjdk.samtools.cram.io; +package htsjdk.cram.io; -import org.apache.commons.compress.utils.IOUtils; +import htsjdk.HtsjdkTest; +import htsjdk.samtools.cram.io.ExternalCompression; import org.testng.Assert; import org.testng.annotations.Test; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; -public class ExternalCompressionTest { +public class ExternalCompressionTest extends HtsjdkTest { public static final File BZIP2_FILE = new File("src/test/resources/htsjdk/samtools/cram/io/bzip2-test.bz2"); public static final byte [] TEST_BYTES = "This is a simple string to test BZip2".getBytes(); diff --git a/src/test/java/htsjdk/samtools/AbstractBAMFileIndexTest.java b/src/test/java/htsjdk/samtools/AbstractBAMFileIndexTest.java index 74c2dd7f2..cf451b86a 100644 --- a/src/test/java/htsjdk/samtools/AbstractBAMFileIndexTest.java +++ b/src/test/java/htsjdk/samtools/AbstractBAMFileIndexTest.java @@ -1,11 +1,12 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.seekablestream.SeekableStream; import org.testng.annotations.Test; import java.io.IOException; -public class AbstractBAMFileIndexTest { +public class AbstractBAMFileIndexTest extends HtsjdkTest { /** * @see https://github.com/samtools/htsjdk/issues/73 @@ -59,4 +60,4 @@ public int read() throws IOException { buffer.readInteger(); buffer.readBytes(new byte[10000]); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/BAMCigarOverflowTest.java b/src/test/java/htsjdk/samtools/BAMCigarOverflowTest.java index dd630f937..8f91c6448 100644 --- a/src/test/java/htsjdk/samtools/BAMCigarOverflowTest.java +++ b/src/test/java/htsjdk/samtools/BAMCigarOverflowTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; @@ -10,7 +11,7 @@ * Test the fix of a bug reported by s-andrews in which the use of an arithmetic rather than a logical right shift in BinaryCigarCodec.binaryCigarToCigarElement() * causes an overflow in the CIGAR when reading a BAM file for a read that spans a very large intron. */ -public class BAMCigarOverflowTest { +public class BAMCigarOverflowTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @Test diff --git a/src/test/java/htsjdk/samtools/BAMFileIndexTest.java b/src/test/java/htsjdk/samtools/BAMFileIndexTest.java index 33c3709c0..0271ade37 100755 --- a/src/test/java/htsjdk/samtools/BAMFileIndexTest.java +++ b/src/test/java/htsjdk/samtools/BAMFileIndexTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.StopWatch; @@ -46,7 +47,7 @@ /** * Test BAM file indexing. */ -public class BAMFileIndexTest { +public class BAMFileIndexTest extends HtsjdkTest { private final File BAM_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"); private final boolean mVerbose = false; @@ -78,8 +79,7 @@ public void testSpecificQueries() } @Test(groups = {"slow"}) - public void testRandomQueries() - throws Exception { + public void testRandomQueries() throws Exception { runRandomTest(BAM_FILE, 1000, new Random()); } diff --git a/src/test/java/htsjdk/samtools/BAMFileWriterTest.java b/src/test/java/htsjdk/samtools/BAMFileWriterTest.java index a8944d0de..b228d76d8 100644 --- a/src/test/java/htsjdk/samtools/BAMFileWriterTest.java +++ b/src/test/java/htsjdk/samtools/BAMFileWriterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; @@ -35,7 +36,7 @@ * Test that BAM writing doesn't blow up. For presorted writing, the resulting BAM file is read and contents are * compared with the original SAM file. */ -public class BAMFileWriterTest { +public class BAMFileWriterTest extends HtsjdkTest { private SAMRecordSetBuilder getRecordSetBuilder(final boolean sortForMe, final SAMFileHeader.SortOrder sortOrder) { final SAMRecordSetBuilder ret = new SAMRecordSetBuilder(sortForMe, sortOrder); diff --git a/src/test/java/htsjdk/samtools/BAMIndexWriterTest.java b/src/test/java/htsjdk/samtools/BAMIndexWriterTest.java index 09f92360e..db9ccb957 100644 --- a/src/test/java/htsjdk/samtools/BAMIndexWriterTest.java +++ b/src/test/java/htsjdk/samtools/BAMIndexWriterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.IOUtil; import org.testng.annotations.DataProvider; @@ -38,7 +39,7 @@ /** * Test BAM file index creation */ -public class BAMIndexWriterTest { +public class BAMIndexWriterTest extends HtsjdkTest { // Two input files for basic test private final String BAM_FILE_LOCATION = "src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"; private final String BAI_FILE_LOCATION = "src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam.bai"; diff --git a/src/test/java/htsjdk/samtools/BAMIteratorTest.java b/src/test/java/htsjdk/samtools/BAMIteratorTest.java index 5fa9e7dc4..6fa67cd9f 100644 --- a/src/test/java/htsjdk/samtools/BAMIteratorTest.java +++ b/src/test/java/htsjdk/samtools/BAMIteratorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; @@ -34,7 +35,7 @@ /** * @author alecw@broadinstitute.org */ -public class BAMIteratorTest { +public class BAMIteratorTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @Test(dataProvider = "dataProvider") diff --git a/src/test/java/htsjdk/samtools/BAMQueryMultipleIntervalsIteratorFilterTest.java b/src/test/java/htsjdk/samtools/BAMQueryMultipleIntervalsIteratorFilterTest.java index 7c0bb1fc2..d25e7ba65 100644 --- a/src/test/java/htsjdk/samtools/BAMQueryMultipleIntervalsIteratorFilterTest.java +++ b/src/test/java/htsjdk/samtools/BAMQueryMultipleIntervalsIteratorFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ import java.util.Arrays; import java.util.Random; -public class BAMQueryMultipleIntervalsIteratorFilterTest { +public class BAMQueryMultipleIntervalsIteratorFilterTest extends HtsjdkTest { private final byte[] BASES = {'A', 'C', 'G', 'T'}; private final Random random = new Random(); diff --git a/src/test/java/htsjdk/samtools/BAMRemoteFileTest.java b/src/test/java/htsjdk/samtools/BAMRemoteFileTest.java index 4b686cf04..dccfddcac 100644 --- a/src/test/java/htsjdk/samtools/BAMRemoteFileTest.java +++ b/src/test/java/htsjdk/samtools/BAMRemoteFileTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.TestUtil; import org.testng.annotations.Test; @@ -40,7 +41,7 @@ /** * Test BAM file indexing. */ -public class BAMRemoteFileTest { +public class BAMRemoteFileTest extends HtsjdkTest { private final File BAM_INDEX_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam.bai"); private final File BAM_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"); private final String BAM_URL_STRING = TestUtil.BASE_URL_FOR_HTTP_TESTS + "index_test.bam"; diff --git a/src/test/java/htsjdk/samtools/BinTest.java b/src/test/java/htsjdk/samtools/BinTest.java index 271a41101..6009ed37c 100644 --- a/src/test/java/htsjdk/samtools/BinTest.java +++ b/src/test/java/htsjdk/samtools/BinTest.java @@ -24,12 +24,13 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; import java.util.Collections; -public class BinTest { +public class BinTest extends HtsjdkTest { @Test public void testEmptyBin() { // Construct a new empty bin and ensure that the bin list is empty, not null. diff --git a/src/test/java/htsjdk/samtools/CRAMBAIIndexerTest.java b/src/test/java/htsjdk/samtools/CRAMBAIIndexerTest.java index 6f3b95459..ce32e7a8b 100644 --- a/src/test/java/htsjdk/samtools/CRAMBAIIndexerTest.java +++ b/src/test/java/htsjdk/samtools/CRAMBAIIndexerTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.build.ContainerFactory; import htsjdk.samtools.cram.structure.Container; import htsjdk.samtools.cram.structure.CramCompressionRecord; @@ -17,7 +18,7 @@ /** * Created by vadim on 12/01/2016. */ -public class CRAMBAIIndexerTest { +public class CRAMBAIIndexerTest extends HtsjdkTest { private static CramCompressionRecord createRecord(int recordIndex, int seqId, int start) { byte[] bases = "AAAAA".getBytes(); diff --git a/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java b/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java index 604e78670..c5a9634d4 100644 --- a/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java +++ b/src/test/java/htsjdk/samtools/CRAMCRAIIndexerTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.CRAIEntry; import htsjdk.samtools.cram.build.CramContainerIterator; import htsjdk.samtools.cram.ref.ReferenceSource; @@ -17,7 +18,7 @@ * Companion to CRAMBAIIndexerTest, for testing CRAI indices created on cram * streams; */ -public class CRAMCRAIIndexerTest { +public class CRAMCRAIIndexerTest extends HtsjdkTest { @Test public void testCRAIIndexerFromContainer() throws IOException { @@ -180,4 +181,4 @@ private long getIteratorCount(Iterator it) { return count; } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/CRAMComplianceTest.java b/src/test/java/htsjdk/samtools/CRAMComplianceTest.java index 635fa6791..57167279d 100644 --- a/src/test/java/htsjdk/samtools/CRAMComplianceTest.java +++ b/src/test/java/htsjdk/samtools/CRAMComplianceTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.build.CramIO; import htsjdk.samtools.cram.common.CramVersions; import htsjdk.samtools.cram.ref.ReferenceSource; @@ -22,7 +23,7 @@ /** * Created by vadim on 28/04/2015. */ -public class CRAMComplianceTest { +public class CRAMComplianceTest extends HtsjdkTest { @FunctionalInterface public interface TriConsumer { diff --git a/src/test/java/htsjdk/samtools/CRAMContainerStreamWriterTest.java b/src/test/java/htsjdk/samtools/CRAMContainerStreamWriterTest.java index b26f4b06b..9ab9ed278 100644 --- a/src/test/java/htsjdk/samtools/CRAMContainerStreamWriterTest.java +++ b/src/test/java/htsjdk/samtools/CRAMContainerStreamWriterTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; import htsjdk.samtools.seekablestream.SeekableMemoryStream; @@ -23,7 +24,7 @@ import java.util.Collections; import java.util.List; -public class CRAMContainerStreamWriterTest { +public class CRAMContainerStreamWriterTest extends HtsjdkTest { @BeforeClass public void initClass() { diff --git a/src/test/java/htsjdk/samtools/CRAMEdgeCasesTest.java b/src/test/java/htsjdk/samtools/CRAMEdgeCasesTest.java index e77e0e8dc..4fa9b1a59 100644 --- a/src/test/java/htsjdk/samtools/CRAMEdgeCasesTest.java +++ b/src/test/java/htsjdk/samtools/CRAMEdgeCasesTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.CRAMException; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; @@ -20,7 +21,7 @@ /** * A collection of CRAM test based on round trip comparison of SAMRecord before and after CRAM compression. */ -public class CRAMEdgeCasesTest { +public class CRAMEdgeCasesTest extends HtsjdkTest { @BeforeTest public void beforeTest() { diff --git a/src/test/java/htsjdk/samtools/CRAMFileBAIIndexTest.java b/src/test/java/htsjdk/samtools/CRAMFileBAIIndexTest.java index eba2b4cb7..32160920b 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileBAIIndexTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileBAIIndexTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.build.ContainerParser; import htsjdk.samtools.cram.build.CramContainerIterator; import htsjdk.samtools.cram.ref.ReferenceSource; @@ -32,7 +33,7 @@ * The scan* tests check that for every records in the BAM file the query returns the same records from the CRAM file. * Created by Vadim on 14/03/2015. */ -public class CRAMFileBAIIndexTest { +public class CRAMFileBAIIndexTest extends HtsjdkTest { private final File BAM_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"); private File cramFile; private File indexFile; diff --git a/src/test/java/htsjdk/samtools/CRAMFileCRAIIndexTest.java b/src/test/java/htsjdk/samtools/CRAMFileCRAIIndexTest.java index 9084a0fc5..b919c4619 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileCRAIIndexTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileCRAIIndexTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.build.ContainerParser; import htsjdk.samtools.cram.build.CramContainerIterator; import htsjdk.samtools.cram.ref.ReferenceSource; @@ -29,7 +30,8 @@ * file as the source of the test data. The scan* tests check that for every records in the * CRAM file the query returns the same records from the CRAM file. */ -public class CRAMFileCRAIIndexTest { +@Test(singleThreaded = true) +public class CRAMFileCRAIIndexTest extends HtsjdkTest { private final File BAM_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"); private final int nofReads = 10000 ; diff --git a/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java b/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java index 9e36c3ccc..da53f170c 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileReaderTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; import htsjdk.samtools.seekablestream.SeekableFileStream; @@ -40,7 +41,7 @@ /** * Additional tests for CRAMFileReader are in CRAMFileIndexTest */ -public class CRAMFileReaderTest { +public class CRAMFileReaderTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); private static final File CRAM_WITH_CRAI = new File(TEST_DATA_DIR, "cram_with_crai_index.cram"); diff --git a/src/test/java/htsjdk/samtools/CRAMFileWriterTest.java b/src/test/java/htsjdk/samtools/CRAMFileWriterTest.java index 312bf5404..bd3a5ab5b 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileWriterTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileWriterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; import htsjdk.samtools.util.Log; @@ -40,7 +41,7 @@ import java.util.Collections; import java.util.List; -public class CRAMFileWriterTest { +public class CRAMFileWriterTest extends HtsjdkTest { @BeforeClass public void initClass() { diff --git a/src/test/java/htsjdk/samtools/CRAMFileWriterWithIndexTest.java b/src/test/java/htsjdk/samtools/CRAMFileWriterWithIndexTest.java index b7e3eab0b..b7facb6cb 100644 --- a/src/test/java/htsjdk/samtools/CRAMFileWriterWithIndexTest.java +++ b/src/test/java/htsjdk/samtools/CRAMFileWriterWithIndexTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.CRAIIndex; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; @@ -22,7 +23,7 @@ /** * Created by vadim on 23/03/2015. */ -public class CRAMFileWriterWithIndexTest { +public class CRAMFileWriterWithIndexTest extends HtsjdkTest { private byte[] cramBytes; private byte[] indexBytes; private InMemoryReferenceSequenceFile rsf; diff --git a/src/test/java/htsjdk/samtools/CRAMIndexQueryTest.java b/src/test/java/htsjdk/samtools/CRAMIndexQueryTest.java index df9431071..845433f9f 100644 --- a/src/test/java/htsjdk/samtools/CRAMIndexQueryTest.java +++ b/src/test/java/htsjdk/samtools/CRAMIndexQueryTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.util.CloseableIterator; import org.testng.Assert; @@ -42,7 +43,7 @@ * whatever index format (.bai or .crai converted to .bai) is available for the * target file. */ -public class CRAMIndexQueryTest { +public class CRAMIndexQueryTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/cram"); diff --git a/src/test/java/htsjdk/samtools/ChunkTest.java b/src/test/java/htsjdk/samtools/ChunkTest.java index d2bc157e2..b3a9e0a53 100644 --- a/src/test/java/htsjdk/samtools/ChunkTest.java +++ b/src/test/java/htsjdk/samtools/ChunkTest.java @@ -23,10 +23,11 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class ChunkTest { +public class ChunkTest extends HtsjdkTest { @Test public void testOverlaps() { // Test completely disjoint offsets. diff --git a/src/test/java/htsjdk/samtools/CigarCodecTest.java b/src/test/java/htsjdk/samtools/CigarCodecTest.java index 8275a9484..7ccde7d1b 100644 --- a/src/test/java/htsjdk/samtools/CigarCodecTest.java +++ b/src/test/java/htsjdk/samtools/CigarCodecTest.java @@ -23,12 +23,13 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; import java.util.Arrays; -public class CigarCodecTest { +public class CigarCodecTest extends HtsjdkTest { @Test diff --git a/src/test/java/htsjdk/samtools/CigarTest.java b/src/test/java/htsjdk/samtools/CigarTest.java index acdc22407..07fe34633 100644 --- a/src/test/java/htsjdk/samtools/CigarTest.java +++ b/src/test/java/htsjdk/samtools/CigarTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -33,7 +34,7 @@ /** * @author alecw@broadinstitute.org */ -public class CigarTest { +public class CigarTest extends HtsjdkTest { @DataProvider(name = "positiveTestsData") public Object[][] testPositive() { diff --git a/src/test/java/htsjdk/samtools/DownsamplingIteratorTests.java b/src/test/java/htsjdk/samtools/DownsamplingIteratorTests.java index e84ee2e48..96dff4656 100644 --- a/src/test/java/htsjdk/samtools/DownsamplingIteratorTests.java +++ b/src/test/java/htsjdk/samtools/DownsamplingIteratorTests.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.DownsamplingIteratorFactory.Strategy; import org.testng.Assert; import org.testng.annotations.Test; @@ -14,7 +15,7 @@ * Tests for the downsampling iterator class. * @author Tim Fennell */ -public class DownsamplingIteratorTests { +public class DownsamplingIteratorTests extends HtsjdkTest { final int NUM_TEMPLATES = 50000; final EnumMap ACCURACY = new EnumMap(Strategy.class){{ put(Strategy.HighAccuracy, 0.001); diff --git a/src/test/java/htsjdk/samtools/DuplicateSetIteratorTest.java b/src/test/java/htsjdk/samtools/DuplicateSetIteratorTest.java index 595295345..27e167881 100644 --- a/src/test/java/htsjdk/samtools/DuplicateSetIteratorTest.java +++ b/src/test/java/htsjdk/samtools/DuplicateSetIteratorTest.java @@ -1,12 +1,13 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; -public class DuplicateSetIteratorTest { +public class DuplicateSetIteratorTest extends HtsjdkTest { protected final static int DEFAULT_BASE_QUALITY = 10; private SAMRecordSetBuilder getSAMRecordSetBuilder() { diff --git a/src/test/java/htsjdk/samtools/GenomicIndexUtilTest.java b/src/test/java/htsjdk/samtools/GenomicIndexUtilTest.java index 8f5569c59..0bf322d58 100644 --- a/src/test/java/htsjdk/samtools/GenomicIndexUtilTest.java +++ b/src/test/java/htsjdk/samtools/GenomicIndexUtilTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ /** * Tests for GenomicIndexUtil. */ -public class GenomicIndexUtilTest { +public class GenomicIndexUtilTest extends HtsjdkTest { @Test(dataProvider = "testRegionToBinDataProvider") public void testRegionToBin(final int beg, final int end, final int bin) { @@ -47,4 +48,4 @@ public void testRegionToBin(final int beg, final int end, final int bin) { {1<<26, 1<<26+1, 2} }; } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java index 067f853ab..d350b8f38 100644 --- a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java +++ b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorGroupCollisionTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -37,7 +38,7 @@ * * @author Dave Tefft, Andre Mesarovic */ -public class MergingSamRecordIteratorGroupCollisionTest { +public class MergingSamRecordIteratorGroupCollisionTest extends HtsjdkTest { private GroupAdapter padapter = new ProgramGroupAdapter(); private GroupAdapter radapter = new ReadGroupAdapter(); diff --git a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorTest.java b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorTest.java index 885321b26..a50c02645 100644 --- a/src/test/java/htsjdk/samtools/MergingSamRecordIteratorTest.java +++ b/src/test/java/htsjdk/samtools/MergingSamRecordIteratorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.SequenceUtil; import org.testng.Assert; import org.testng.annotations.Test; @@ -38,7 +39,7 @@ * * @author Dave Tefft */ -public class MergingSamRecordIteratorTest { +public class MergingSamRecordIteratorTest extends HtsjdkTest { @Test public void testVanillaCoordinateMultiIterator() throws Exception { diff --git a/src/test/java/htsjdk/samtools/ProgramRecordChainingTest.java b/src/test/java/htsjdk/samtools/ProgramRecordChainingTest.java index cd470c449..4811148f3 100644 --- a/src/test/java/htsjdk/samtools/ProgramRecordChainingTest.java +++ b/src/test/java/htsjdk/samtools/ProgramRecordChainingTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Test for SequenceUtil.chainProgramRecord */ -public class ProgramRecordChainingTest { +public class ProgramRecordChainingTest extends HtsjdkTest { @Test public void testChainProgramRecord() { diff --git a/src/test/java/htsjdk/samtools/SAMBinaryTagAndValueUnitTest.java b/src/test/java/htsjdk/samtools/SAMBinaryTagAndValueUnitTest.java index f5f7a5c01..93a20dc6e 100644 --- a/src/test/java/htsjdk/samtools/SAMBinaryTagAndValueUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMBinaryTagAndValueUnitTest.java @@ -1,11 +1,12 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.BinaryCodec; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class SAMBinaryTagAndValueUnitTest { +public class SAMBinaryTagAndValueUnitTest extends HtsjdkTest { @DataProvider(name="allowedAttributeTypes") public Object[][] allowedTypes() { diff --git a/src/test/java/htsjdk/samtools/SAMCloneTest.java b/src/test/java/htsjdk/samtools/SAMCloneTest.java index 8fdfb3bde..e05d29d7a 100644 --- a/src/test/java/htsjdk/samtools/SAMCloneTest.java +++ b/src/test/java/htsjdk/samtools/SAMCloneTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * @author alecw@broadinstitute.org */ -public class SAMCloneTest { +public class SAMCloneTest extends HtsjdkTest { private SAMRecordSetBuilder getSAMReader(final boolean sortForMe, final SAMFileHeader.SortOrder sortOrder) { final SAMRecordSetBuilder ret = new SAMRecordSetBuilder(sortForMe, sortOrder); ret.addPair("readB", 20, 200, 300); diff --git a/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java b/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java index dc7a6f381..8c15a1656 100644 --- a/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java +++ b/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.build.CramIO; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.util.IOUtil; @@ -32,7 +33,7 @@ import java.io.*; -public class SAMFileWriterFactoryTest { +public class SAMFileWriterFactoryTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); diff --git a/src/test/java/htsjdk/samtools/SAMFlagTest.java b/src/test/java/htsjdk/samtools/SAMFlagTest.java index 7b5a5539f..86dd8f0ee 100644 --- a/src/test/java/htsjdk/samtools/SAMFlagTest.java +++ b/src/test/java/htsjdk/samtools/SAMFlagTest.java @@ -24,10 +24,11 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class SAMFlagTest { +public class SAMFlagTest extends HtsjdkTest { @Test public void testFlags() { Assert.assertTrue(SAMFlag.getFlags(83).contains(SAMFlag.READ_PAIRED)); diff --git a/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java b/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java index 133062a15..3fa38df62 100644 --- a/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java +++ b/src/test/java/htsjdk/samtools/SAMIntegerTagTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.util.BinaryCodec; import htsjdk.samtools.util.CloserUtil; @@ -45,7 +46,7 @@ * * @author alecw@broadinstitute.org */ -public class SAMIntegerTagTest { +public class SAMIntegerTagTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/SAMIntegerTagTest"); private static final String BYTE_TAG = "BY"; diff --git a/src/test/java/htsjdk/samtools/SAMRecordDuplicateComparatorTest.java b/src/test/java/htsjdk/samtools/SAMRecordDuplicateComparatorTest.java index cb509258e..99d187a58 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordDuplicateComparatorTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordDuplicateComparatorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -37,7 +38,7 @@ * * @author nhomer */ -public class SAMRecordDuplicateComparatorTest { +public class SAMRecordDuplicateComparatorTest extends HtsjdkTest { private final static SAMRecordDuplicateComparator comparator = new SAMRecordDuplicateComparator(); diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index 951ecee78..29118197f 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.BinaryCodec; import htsjdk.samtools.util.TestUtil; import org.testng.Assert; @@ -34,7 +35,7 @@ import java.util.Arrays; import java.util.List; -public class SAMRecordUnitTest { +public class SAMRecordUnitTest extends HtsjdkTest { @DataProvider(name = "serializationTestData") public Object[][] getSerializationTestData() { diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java index 32de1cd82..4257508cf 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryCodecTest.java @@ -24,23 +24,24 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.LineReader; import htsjdk.samtools.util.StringLineReader; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import javax.sound.sampled.Line; import java.io.BufferedWriter; import java.io.StringWriter; import java.util.List; import java.util.Random; -import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; /** * @author Pavel_Silin@epam.com, EPAM Systems, Inc. */ -public class SAMSequenceDictionaryCodecTest { +public class SAMSequenceDictionaryCodecTest extends HtsjdkTest { private static final Random random = new Random(); private SAMSequenceDictionary dictionary; diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java index 0b1a50780..8b1763067 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java @@ -26,23 +26,21 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collections; -public class SAMSequenceDictionaryTest { +public class SAMSequenceDictionaryTest extends HtsjdkTest { @Test public void testAliases() { final SAMSequenceRecord ssr1 = new SAMSequenceRecord("1", 1); diff --git a/src/test/java/htsjdk/samtools/SAMTextReaderTest.java b/src/test/java/htsjdk/samtools/SAMTextReaderTest.java index c80924b65..142eea32c 100644 --- a/src/test/java/htsjdk/samtools/SAMTextReaderTest.java +++ b/src/test/java/htsjdk/samtools/SAMTextReaderTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; @@ -31,7 +32,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -public class SAMTextReaderTest { +public class SAMTextReaderTest extends HtsjdkTest { // Simple input, spot check that parsed correctly, and make sure nothing blows up. @Test public void testBasic() throws Exception { diff --git a/src/test/java/htsjdk/samtools/SAMTextWriterTest.java b/src/test/java/htsjdk/samtools/SAMTextWriterTest.java index 123ab6ba1..5c9ff28cd 100644 --- a/src/test/java/htsjdk/samtools/SAMTextWriterTest.java +++ b/src/test/java/htsjdk/samtools/SAMTextWriterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -31,7 +32,7 @@ import java.util.Iterator; import java.util.Map; -public class SAMTextWriterTest { +public class SAMTextWriterTest extends HtsjdkTest { private SAMRecordSetBuilder getSAMReader(final boolean sortForMe, final SAMFileHeader.SortOrder sortOrder) { final SAMRecordSetBuilder ret = new SAMRecordSetBuilder(sortForMe, sortOrder); diff --git a/src/test/java/htsjdk/samtools/SAMUtilsTest.java b/src/test/java/htsjdk/samtools/SAMUtilsTest.java index e3fe72656..28e89f755 100644 --- a/src/test/java/htsjdk/samtools/SAMUtilsTest.java +++ b/src/test/java/htsjdk/samtools/SAMUtilsTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -30,7 +31,7 @@ import java.util.Arrays; import java.util.List; -public class SAMUtilsTest { +public class SAMUtilsTest extends HtsjdkTest { @Test public void testCompareMapqs() { Assert.assertEquals(SAMUtils.compareMapqs(0, 0), 0); diff --git a/src/test/java/htsjdk/samtools/SamFileHeaderMergerTest.java b/src/test/java/htsjdk/samtools/SamFileHeaderMergerTest.java index 6e4fd750f..5c55c0b82 100644 --- a/src/test/java/htsjdk/samtools/SamFileHeaderMergerTest.java +++ b/src/test/java/htsjdk/samtools/SamFileHeaderMergerTest.java @@ -25,6 +25,7 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.IOUtil; import htsjdk.samtools.util.SequenceUtil; @@ -58,7 +59,7 @@ *

* Tests the ability of the SamFileHeaderMerger class to merge sequence dictionaries. */ -public class SamFileHeaderMergerTest { +public class SamFileHeaderMergerTest extends HtsjdkTest { private static File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); diff --git a/src/test/java/htsjdk/samtools/SamFilesTest.java b/src/test/java/htsjdk/samtools/SamFilesTest.java index 443a4d1e9..e7c1919d4 100644 --- a/src/test/java/htsjdk/samtools/SamFilesTest.java +++ b/src/test/java/htsjdk/samtools/SamFilesTest.java @@ -1,6 +1,8 @@ package htsjdk.samtools; import java.nio.file.Path; + +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -12,7 +14,7 @@ * Test valid combinations of bam/cram vs bai/crai files. * Created by vadim on 10/08/2015. */ -public class SamFilesTest { +public class SamFilesTest extends HtsjdkTest { private static final String TEST_DATA = "src/test/resources/htsjdk/samtools/BAMFileIndexTest/"; private static final File BAM_FILE = new File(TEST_DATA + "index_test.bam"); diff --git a/src/test/java/htsjdk/samtools/SamFlagFieldTest.java b/src/test/java/htsjdk/samtools/SamFlagFieldTest.java index f09e63683..36008cf69 100644 --- a/src/test/java/htsjdk/samtools/SamFlagFieldTest.java +++ b/src/test/java/htsjdk/samtools/SamFlagFieldTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ /** * @author nhomer */ -public class SamFlagFieldTest { +public class SamFlagFieldTest extends HtsjdkTest { @Test public void testAllFlags() { @@ -147,4 +148,4 @@ public void testIllegalHexadecimalFlagCharacter(){ public void testIllegalStringFlagCharacterExclamation(){ SamFlagField.STRING.parse("pmMr!F1s"); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/SamHeaderRecordComparatorTest.java b/src/test/java/htsjdk/samtools/SamHeaderRecordComparatorTest.java index c11be38a1..da93add5b 100644 --- a/src/test/java/htsjdk/samtools/SamHeaderRecordComparatorTest.java +++ b/src/test/java/htsjdk/samtools/SamHeaderRecordComparatorTest.java @@ -24,11 +24,12 @@ * THE SOFTWARE. */ +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class SamHeaderRecordComparatorTest { +public class SamHeaderRecordComparatorTest extends HtsjdkTest { @DataProvider(name="UsualSuspects") public Object[][] createData() { diff --git a/src/test/java/htsjdk/samtools/SamIndexesTest.java b/src/test/java/htsjdk/samtools/SamIndexesTest.java index d13001f67..f78b0f371 100644 --- a/src/test/java/htsjdk/samtools/SamIndexesTest.java +++ b/src/test/java/htsjdk/samtools/SamIndexesTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.CRAIEntry; import htsjdk.samtools.cram.CRAIIndex; import htsjdk.samtools.seekablestream.SeekableFileStream; @@ -19,7 +20,7 @@ import java.util.List; import java.util.zip.GZIPOutputStream; -public class SamIndexesTest { +public class SamIndexesTest extends HtsjdkTest { @Test public void testEmptyBai() throws IOException { diff --git a/src/test/java/htsjdk/samtools/SamPairUtilTest.java b/src/test/java/htsjdk/samtools/SamPairUtilTest.java index 80841c906..f5c288a54 100644 --- a/src/test/java/htsjdk/samtools/SamPairUtilTest.java +++ b/src/test/java/htsjdk/samtools/SamPairUtilTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SamPairUtil.SetMateInfoIterator; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -32,7 +33,7 @@ import java.util.List; -public class SamPairUtilTest { +public class SamPairUtilTest extends HtsjdkTest { @Test(dataProvider = "testGetPairOrientation") public void testGetPairOrientation(final String testName, diff --git a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java index 5b86c8a59..c244f3c8b 100644 --- a/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderFactoryTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.seekablestream.ISeekableStreamFactory; import htsjdk.samtools.seekablestream.SeekableFileStream; @@ -30,7 +31,7 @@ import java.util.function.BiFunction; import java.util.zip.Inflater; -public class SamReaderFactoryTest { +public class SamReaderFactoryTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); private static final Log LOG = Log.getInstance(SamReaderFactoryTest.class); diff --git a/src/test/java/htsjdk/samtools/SamReaderSortTest.java b/src/test/java/htsjdk/samtools/SamReaderSortTest.java index e7484c787..4d712100b 100755 --- a/src/test/java/htsjdk/samtools/SamReaderSortTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderSortTest.java @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.ref.ReferenceSource; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -36,7 +37,7 @@ * * @author ktibbett@broadinstitute.org */ -public class SamReaderSortTest { +public class SamReaderSortTest extends HtsjdkTest { private static final String COORDINATE_SORTED_FILE = "src/test/resources/htsjdk/samtools/coordinate_sorted.sam"; private static final String QUERYNAME_SORTED_FILE = "src/test/resources/htsjdk/samtools/queryname_sorted.sam"; diff --git a/src/test/java/htsjdk/samtools/SamReaderTest.java b/src/test/java/htsjdk/samtools/SamReaderTest.java index 5af88214e..4d4d05634 100644 --- a/src/test/java/htsjdk/samtools/SamReaderTest.java +++ b/src/test/java/htsjdk/samtools/SamReaderTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloseableIterator; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; @@ -31,7 +32,7 @@ import java.io.File; -public class SamReaderTest { +public class SamReaderTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @Test(dataProvider = "variousFormatReaderTestCases") diff --git a/src/test/java/htsjdk/samtools/SamSpecIntTest.java b/src/test/java/htsjdk/samtools/SamSpecIntTest.java index 8305065da..2ebc24e70 100644 --- a/src/test/java/htsjdk/samtools/SamSpecIntTest.java +++ b/src/test/java/htsjdk/samtools/SamSpecIntTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; import org.testng.annotations.Test; @@ -33,7 +34,7 @@ import java.util.ArrayList; import java.util.List; -public class SamSpecIntTest { +public class SamSpecIntTest extends HtsjdkTest { private static final File SAM_INPUT = new File("src/test/resources/htsjdk/samtools/inttest.sam"); private static final File BAM_INPUT = new File("src/test/resources/htsjdk/samtools/inttest.bam"); diff --git a/src/test/java/htsjdk/samtools/SamStreamsTest.java b/src/test/java/htsjdk/samtools/SamStreamsTest.java index c92d6dbc0..48a074a8e 100644 --- a/src/test/java/htsjdk/samtools/SamStreamsTest.java +++ b/src/test/java/htsjdk/samtools/SamStreamsTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.seekablestream.SeekableStream; import htsjdk.samtools.seekablestream.SeekableStreamFactory; @@ -34,7 +35,7 @@ import java.io.*; import java.net.URL; -public class SamStreamsTest { +public class SamStreamsTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @@ -121,4 +122,4 @@ public void sourceLikeBam( SeekableStreamFactory.getInstance().getStreamFor(new URL(resourceName)); Assert.assertEquals(SamStreams.sourceLikeBam(strm), expected); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/SequenceNameTruncationAndValidationTest.java b/src/test/java/htsjdk/samtools/SequenceNameTruncationAndValidationTest.java index 2c3a95c6a..01999c481 100644 --- a/src/test/java/htsjdk/samtools/SequenceNameTruncationAndValidationTest.java +++ b/src/test/java/htsjdk/samtools/SequenceNameTruncationAndValidationTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.CloserUtil; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -36,7 +37,7 @@ * * @author alecw@broadinstitute.org */ -public class SequenceNameTruncationAndValidationTest { +public class SequenceNameTruncationAndValidationTest extends HtsjdkTest { private static File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools"); @Test(expectedExceptions = {SAMException.class}, dataProvider = "badSequenceNames") diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index 77ac59f0e..f227332d2 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import htsjdk.samtools.BamIndexValidator.IndexValidationStringency; import htsjdk.samtools.metrics.MetricBase; import htsjdk.samtools.metrics.MetricsFile; @@ -58,7 +59,7 @@ * * @author Doug Voet */ -public class ValidateSamFileTest { +public class ValidateSamFileTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/ValidateSamFileTest"); private static final int TERMINATION_GZIP_BLOCK_SIZE = 28; private static final int RANDOM_NUMBER_TRUNC_BYTE = 128; diff --git a/src/test/java/htsjdk/samtools/cram/CRAIEntryTest.java b/src/test/java/htsjdk/samtools/cram/CRAIEntryTest.java index 6cf49344b..d43f2fc14 100644 --- a/src/test/java/htsjdk/samtools/cram/CRAIEntryTest.java +++ b/src/test/java/htsjdk/samtools/cram/CRAIEntryTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.structure.Container; import htsjdk.samtools.cram.structure.Slice; import org.testng.Assert; @@ -12,7 +13,7 @@ /** * Created by vadim on 25/08/2015. */ -public class CRAIEntryTest { +public class CRAIEntryTest extends HtsjdkTest { @Test public void testFromContainer() { diff --git a/src/test/java/htsjdk/samtools/cram/CRAIIndexTest.java b/src/test/java/htsjdk/samtools/cram/CRAIIndexTest.java index 7ebdb75e1..9e48d6b4e 100644 --- a/src/test/java/htsjdk/samtools/cram/CRAIIndexTest.java +++ b/src/test/java/htsjdk/samtools/cram/CRAIIndexTest.java @@ -1,23 +1,14 @@ package htsjdk.samtools.cram; -import htsjdk.samtools.BAMFileSpan; -import htsjdk.samtools.CRAMCRAIIndexer; -import htsjdk.samtools.DiskBasedBAMFileIndex; -import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.HtsjdkTest; +import htsjdk.samtools.*; import htsjdk.samtools.seekablestream.SeekableBufferedStream; import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.seekablestream.SeekableStream; import org.testng.Assert; import org.testng.annotations.Test; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; @@ -26,7 +17,7 @@ /** * Created by vadim on 25/08/2015. */ -public class CRAIIndexTest { +public class CRAIIndexTest extends HtsjdkTest { @Test public void testFind() throws IOException, CloneNotSupportedException { diff --git a/src/test/java/htsjdk/samtools/cram/LosslessRoundTripTest.java b/src/test/java/htsjdk/samtools/cram/LosslessRoundTripTest.java index 67cd4833c..1ae8e142a 100644 --- a/src/test/java/htsjdk/samtools/cram/LosslessRoundTripTest.java +++ b/src/test/java/htsjdk/samtools/cram/LosslessRoundTripTest.java @@ -1,31 +1,18 @@ package htsjdk.samtools.cram; -import htsjdk.samtools.CRAMFileReader; -import htsjdk.samtools.CRAMFileWriter; -import htsjdk.samtools.Cigar; -import htsjdk.samtools.CigarElement; -import htsjdk.samtools.CigarOperator; -import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMReadGroupRecord; -import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.SAMRecordIterator; -import htsjdk.samtools.SAMSequenceRecord; -import htsjdk.samtools.ValidationStringency; +import htsjdk.HtsjdkTest; +import htsjdk.samtools.*; import htsjdk.samtools.cram.ref.ReferenceSource; import htsjdk.samtools.reference.InMemoryReferenceSequenceFile; import org.testng.Assert; import org.testng.annotations.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; /** * Created by vadim on 19/02/2016. */ -public class LosslessRoundTripTest { +public class LosslessRoundTripTest extends HtsjdkTest { @Test public void test_MD_NM() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/src/test/java/htsjdk/samtools/cram/VersionTest.java b/src/test/java/htsjdk/samtools/cram/VersionTest.java index 0602eb376..be2851eb6 100644 --- a/src/test/java/htsjdk/samtools/cram/VersionTest.java +++ b/src/test/java/htsjdk/samtools/cram/VersionTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram; +import htsjdk.HtsjdkTest; import htsjdk.samtools.CRAMFileWriter; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; @@ -26,7 +27,7 @@ /** * Created by vadim on 18/02/2016. */ -public class VersionTest { +public class VersionTest extends HtsjdkTest { /** * The test purpose is to ensure that a CRAM written by {@link CRAMFileWriter} adheres to CRAM3 specs expectations: * 1. version 3.+, via both actual byte comparison and CramIO API diff --git a/src/test/java/htsjdk/samtools/cram/build/CompressionHeaderFactoryTest.java b/src/test/java/htsjdk/samtools/cram/build/CompressionHeaderFactoryTest.java index a3d91cdc7..8e39d9f76 100644 --- a/src/test/java/htsjdk/samtools/cram/build/CompressionHeaderFactoryTest.java +++ b/src/test/java/htsjdk/samtools/cram/build/CompressionHeaderFactoryTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.build; +import htsjdk.HtsjdkTest; import htsjdk.samtools.ValidationStringency; import htsjdk.samtools.cram.encoding.readfeatures.Substitution; import htsjdk.samtools.cram.structure.CompressionHeader; @@ -17,7 +18,7 @@ /** * Created by vadim on 07/01/2016. */ -public class CompressionHeaderFactoryTest { +public class CompressionHeaderFactoryTest extends HtsjdkTest { @Test public void testAllEncodingsPresent() { final CompressionHeader header = new CompressionHeaderFactory().build(new ArrayList<>(), new SubstitutionMatrix(new long[256][256]), true); diff --git a/src/test/java/htsjdk/samtools/cram/build/ContainerFactoryTest.java b/src/test/java/htsjdk/samtools/cram/build/ContainerFactoryTest.java index cb004a729..cf4f91e51 100644 --- a/src/test/java/htsjdk/samtools/cram/build/ContainerFactoryTest.java +++ b/src/test/java/htsjdk/samtools/cram/build/ContainerFactoryTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.build; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMSequenceRecord; @@ -19,7 +20,7 @@ /** * Created by vadim on 15/12/2015. */ -public class ContainerFactoryTest { +public class ContainerFactoryTest extends HtsjdkTest { @Test public void testUnmapped() throws IOException, IllegalAccessException { diff --git a/src/test/java/htsjdk/samtools/cram/build/ContainerParserTest.java b/src/test/java/htsjdk/samtools/cram/build/ContainerParserTest.java index fe25ce667..b16dc0f15 100644 --- a/src/test/java/htsjdk/samtools/cram/build/ContainerParserTest.java +++ b/src/test/java/htsjdk/samtools/cram/build/ContainerParserTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.build; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.ValidationStringency; @@ -24,7 +25,7 @@ /** * Created by vadim on 11/01/2016. */ -public class ContainerParserTest { +public class ContainerParserTest extends HtsjdkTest { @Test public void testEOF() throws IOException, IllegalAccessException { diff --git a/src/test/java/htsjdk/samtools/cram/build/CramIOTest.java b/src/test/java/htsjdk/samtools/cram/build/CramIOTest.java index 1035f242e..bab50dc44 100644 --- a/src/test/java/htsjdk/samtools/cram/build/CramIOTest.java +++ b/src/test/java/htsjdk/samtools/cram/build/CramIOTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.build; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMSequenceRecord; import htsjdk.samtools.cram.common.CramVersions; @@ -15,7 +16,7 @@ /** * Created by vadim on 25/08/2015. */ -public class CramIOTest { +public class CramIOTest extends HtsjdkTest { @Test public void testCheckHeaderAndEOF_v2() throws IOException { final String id = "testid"; diff --git a/src/test/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanTest.java b/src/test/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanTest.java index f2ca2f2b1..fd24c6b8e 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.encoding.huffman.codec; +import htsjdk.HtsjdkTest; import htsjdk.samtools.cram.io.DefaultBitInputStream; import htsjdk.samtools.cram.io.DefaultBitOutputStream; import htsjdk.samtools.cram.structure.ReadTag; @@ -13,7 +14,7 @@ /** * Created by vadim on 22/04/2015. */ -public class HuffmanTest { +public class HuffmanTest extends HtsjdkTest { @Test public void testHuffmanIntHelper() throws IOException { int size = 1000000; diff --git a/src/test/java/htsjdk/samtools/cram/encoding/rans/RansTest.java b/src/test/java/htsjdk/samtools/cram/encoding/rans/RansTest.java index ca846863b..8e05a12f1 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/rans/RansTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/rans/RansTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.encoding.rans; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -10,7 +11,7 @@ /** * Created by vadim on 22/04/2015. */ -public class RansTest { +public class RansTest extends HtsjdkTest { @Test public void testEmpty() { roundTrip(new byte[0]); diff --git a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java index 5d95d2cc7..a206ad1f0 100644 --- a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.io; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.Tuple; import org.testng.Assert; import org.testng.annotations.BeforeClass; @@ -16,7 +17,7 @@ /** * Created by vadim on 03/02/2015. */ -public class ITF8Test { +public class ITF8Test extends HtsjdkTest { private ExposedByteArrayOutputStream testBAOS; private ByteArrayInputStream testBAIS; diff --git a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java index 510379732..03d310dde 100644 --- a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.io; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; @@ -14,7 +15,7 @@ /** * Created by vadim on 03/02/2015. */ -public class LTF8Test { +public class LTF8Test extends HtsjdkTest { private ExposedByteArrayOutputStream ltf8TestBAOS; private ByteArrayInputStream ltf8TestBAIS; diff --git a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java index 34b4676d9..575485e82 100644 --- a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java +++ b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.cram.lossy; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SamInputResource; @@ -23,7 +24,7 @@ import static org.testng.Assert.*; -public class QualityScorePreservationTest { +public class QualityScorePreservationTest extends HtsjdkTest { @Test public void test1() { diff --git a/src/test/java/htsjdk/samtools/cram/ref/EnaRefServiceTest.java b/src/test/java/htsjdk/samtools/cram/ref/EnaRefServiceTest.java index 852a513b4..7f537843e 100644 --- a/src/test/java/htsjdk/samtools/cram/ref/EnaRefServiceTest.java +++ b/src/test/java/htsjdk/samtools/cram/ref/EnaRefServiceTest.java @@ -1,11 +1,12 @@ package htsjdk.samtools.cram.ref; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; import java.io.IOException; -public class EnaRefServiceTest { +public class EnaRefServiceTest extends HtsjdkTest { @Test public void test() throws IOException, EnaRefService.GaveUpException { diff --git a/src/test/java/htsjdk/samtools/cram/structure/CramCompressionRecordTest.java b/src/test/java/htsjdk/samtools/cram/structure/CramCompressionRecordTest.java index 03360bd6b..a455476fa 100644 --- a/src/test/java/htsjdk/samtools/cram/structure/CramCompressionRecordTest.java +++ b/src/test/java/htsjdk/samtools/cram/structure/CramCompressionRecordTest.java @@ -1,11 +1,8 @@ package htsjdk.samtools.cram.structure; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; -import htsjdk.samtools.cram.encoding.readfeatures.Deletion; -import htsjdk.samtools.cram.encoding.readfeatures.InsertBase; -import htsjdk.samtools.cram.encoding.readfeatures.Insertion; -import htsjdk.samtools.cram.encoding.readfeatures.ReadFeature; -import htsjdk.samtools.cram.encoding.readfeatures.SoftClip; +import htsjdk.samtools.cram.encoding.readfeatures.*; import org.testng.Assert; import org.testng.annotations.Test; @@ -14,7 +11,7 @@ /** * Created by vadim on 28/09/2015. */ -public class CramCompressionRecordTest { +public class CramCompressionRecordTest extends HtsjdkTest { @Test public void test_getAlignmentEnd() { CramCompressionRecord r = new CramCompressionRecord(); diff --git a/src/test/java/htsjdk/samtools/cram/structure/ReadTagTest.java b/src/test/java/htsjdk/samtools/cram/structure/ReadTagTest.java index 3ed0b4006..314fd2498 100644 --- a/src/test/java/htsjdk/samtools/cram/structure/ReadTagTest.java +++ b/src/test/java/htsjdk/samtools/cram/structure/ReadTagTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.cram.structure; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.ValidationStringency; @@ -31,14 +32,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; -public class ReadTagTest { +public class ReadTagTest extends HtsjdkTest { @Test public void test () { diff --git a/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java b/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java index c52dccba1..eeb34ee09 100644 --- a/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java +++ b/src/test/java/htsjdk/samtools/cram/structure/SliceTests.java @@ -1,7 +1,7 @@ package htsjdk.samtools.cram.structure; +import htsjdk.HtsjdkTest; import htsjdk.samtools.CRAMFileReader; -import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.ValidationStringency; import htsjdk.samtools.cram.CRAMException; @@ -17,7 +17,7 @@ /** * Created by vadim on 07/12/2015. */ -public class SliceTests { +public class SliceTests extends HtsjdkTest { @Test public void testUnmappedValidateRef() { Slice slice = new Slice(); diff --git a/src/test/java/htsjdk/samtools/cram/structure/SubstitutionMatrixTest.java b/src/test/java/htsjdk/samtools/cram/structure/SubstitutionMatrixTest.java index 31e770832..625118923 100644 --- a/src/test/java/htsjdk/samtools/cram/structure/SubstitutionMatrixTest.java +++ b/src/test/java/htsjdk/samtools/cram/structure/SubstitutionMatrixTest.java @@ -1,17 +1,15 @@ package htsjdk.samtools.cram.structure; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.BeforeGroups; import org.testng.annotations.DataProvider; -import org.testng.annotations.Parameters; import org.testng.annotations.Test; -import java.util.Arrays; - /** * Created by Vadim on 12/03/2015. */ -public class SubstitutionMatrixTest { +public class SubstitutionMatrixTest extends HtsjdkTest { SubstitutionMatrix m; long[][] freqs; diff --git a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java index 97a3d3c8d..5ace9ffc3 100644 --- a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java +++ b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java @@ -1,9 +1,10 @@ package htsjdk.samtools.fastq; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public final class FastqRecordTest { +public final class FastqRecordTest extends HtsjdkTest { @Test public void testBasic() { @@ -206,4 +207,4 @@ public void testNotEqualLengths() { new FastqRecord("header", seqLine1, "qualHeaderPrefix", qualLine1); //Note: this does not blow up now but it will once we enforce that seqLine and qualLine be the same length } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java b/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java index eba5c5b9f..22549e904 100644 --- a/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java +++ b/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.fastq; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -35,7 +36,7 @@ /** * test fastq */ -public class FastqWriterTest { +public class FastqWriterTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/util/QualityEncodingDetectorTest"); @DataProvider(name = "fastqsource") diff --git a/src/test/java/htsjdk/samtools/filter/FailsVendorReadQualityFilterTest.java b/src/test/java/htsjdk/samtools/filter/FailsVendorReadQualityFilterTest.java index cb2cb0545..ed83f094b 100644 --- a/src/test/java/htsjdk/samtools/filter/FailsVendorReadQualityFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/FailsVendorReadQualityFilterTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class FailsVendorReadQualityFilterTest { +public class FailsVendorReadQualityFilterTest extends HtsjdkTest { private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); private final FailsVendorReadQualityFilter filter = new FailsVendorReadQualityFilter(); diff --git a/src/test/java/htsjdk/samtools/filter/InsertSizeFilterTest.java b/src/test/java/htsjdk/samtools/filter/InsertSizeFilterTest.java index fc4937da4..48d8edc15 100644 --- a/src/test/java/htsjdk/samtools/filter/InsertSizeFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/InsertSizeFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; import org.testng.Assert; @@ -7,7 +8,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class InsertSizeFilterTest { +public class InsertSizeFilterTest extends HtsjdkTest { private static final int READ_LENGTH = 20; private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); diff --git a/src/test/java/htsjdk/samtools/filter/IntervalKeepPairFilterTest.java b/src/test/java/htsjdk/samtools/filter/IntervalKeepPairFilterTest.java index 3d30255f5..7d3c23e79 100644 --- a/src/test/java/htsjdk/samtools/filter/IntervalKeepPairFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/IntervalKeepPairFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecordSetBuilder; import htsjdk.samtools.util.CollectionUtil; import org.testng.Assert; @@ -11,7 +12,7 @@ import java.util.ArrayList; import java.util.stream.StreamSupport; -public class IntervalKeepPairFilterTest { +public class IntervalKeepPairFilterTest extends HtsjdkTest { private static final int READ_LENGTH = 151; private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); diff --git a/src/test/java/htsjdk/samtools/filter/JavascriptSamRecordFilterTest.java b/src/test/java/htsjdk/samtools/filter/JavascriptSamRecordFilterTest.java index 78355760a..043f24d46 100644 --- a/src/test/java/htsjdk/samtools/filter/JavascriptSamRecordFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/JavascriptSamRecordFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecordIterator; import htsjdk.samtools.SamReader; import htsjdk.samtools.SamReaderFactory; @@ -39,7 +40,7 @@ * @author Pierre Lindenbaum PhD Institut du Thorax - INSERM - Nantes - France */ -public class JavascriptSamRecordFilterTest { +public class JavascriptSamRecordFilterTest extends HtsjdkTest { final File testDir = new File("./src/test/resources/htsjdk/samtools"); @DataProvider diff --git a/src/test/java/htsjdk/samtools/filter/MappingQualityFilterTest.java b/src/test/java/htsjdk/samtools/filter/MappingQualityFilterTest.java index 2bffcd64a..9d9f7b819 100644 --- a/src/test/java/htsjdk/samtools/filter/MappingQualityFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/MappingQualityFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; import org.testng.Assert; @@ -7,7 +8,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class MappingQualityFilterTest { +public class MappingQualityFilterTest extends HtsjdkTest { private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); @BeforeTest diff --git a/src/test/java/htsjdk/samtools/filter/OverclippedReadFilterTest.java b/src/test/java/htsjdk/samtools/filter/OverclippedReadFilterTest.java index bff84918c..e154e40ec 100644 --- a/src/test/java/htsjdk/samtools/filter/OverclippedReadFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/OverclippedReadFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.Cigar; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; @@ -31,7 +32,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class OverclippedReadFilterTest { +public class OverclippedReadFilterTest extends HtsjdkTest { private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); private final int unclippedBasesThreshold = 30; diff --git a/src/test/java/htsjdk/samtools/filter/SolexaNoiseFilterTest.java b/src/test/java/htsjdk/samtools/filter/SolexaNoiseFilterTest.java index 96fa324b9..5ea20d406 100644 --- a/src/test/java/htsjdk/samtools/filter/SolexaNoiseFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/SolexaNoiseFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; import org.testng.Assert; @@ -32,7 +33,7 @@ /** * Basic test for the SolexaNoiseFilter */ -public class SolexaNoiseFilterTest { +public class SolexaNoiseFilterTest extends HtsjdkTest { private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); private final SolexaNoiseFilter filter = new SolexaNoiseFilter(); diff --git a/src/test/java/htsjdk/samtools/filter/TagFilterTest.java b/src/test/java/htsjdk/samtools/filter/TagFilterTest.java index 6e0c70293..d885cbe9f 100644 --- a/src/test/java/htsjdk/samtools/filter/TagFilterTest.java +++ b/src/test/java/htsjdk/samtools/filter/TagFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.filter; +import htsjdk.HtsjdkTest; import htsjdk.samtools.ReservedTagConstants; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; @@ -36,7 +37,7 @@ /** * Tests for the TagFilter class */ -public class TagFilterTest { +public class TagFilterTest extends HtsjdkTest { private final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); @@ -74,4 +75,4 @@ public void testTagFilter(final String testName, final String tag, final List */ -public class AbstractLocusInfoTest { +public class AbstractLocusInfoTest extends HtsjdkTest { private final byte[] qualities = {30, 50, 50, 60, 60, 70, 70, 70, 80, 90, 30, 50, 50, 60, 60, 70, 70, 70, 80, 90}; private byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C', 'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; private EdgingRecordAndOffset typedRecordAndOffset; diff --git a/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java b/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java index 0c08436e5..d1e2f0f2e 100644 --- a/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java +++ b/src/test/java/htsjdk/samtools/util/AbstractLocusIteratorTestTemplate.java @@ -25,6 +25,7 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecordSetBuilder; import htsjdk.samtools.SAMSequenceDictionary; @@ -36,7 +37,7 @@ * @author Mariia_Zueva@epam.com, EPAM Systems, Inc. * */ -public abstract class AbstractLocusIteratorTestTemplate { +public abstract class AbstractLocusIteratorTestTemplate extends HtsjdkTest { /** Coverage for tests with the same reads */ final static int coverage = 2; @@ -65,4 +66,4 @@ static SAMRecordSetBuilder getRecordBuilder() { public abstract void testEmitUncoveredLoci(); public abstract void testSimpleGappedAlignment(); public abstract void testOverlappingGappedAlignmentsWithoutIndels(); -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java index 568c84c7c..8993e417a 100644 --- a/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java +++ b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import org.testng.annotations.BeforeTest; @@ -36,7 +37,7 @@ * */ -public class AbstractRecordAndOffsetTest { +public class AbstractRecordAndOffsetTest extends HtsjdkTest { private final byte[] qualities = {30, 40, 50, 60, 70, 80 ,90, 70, 80, 90}; private byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; diff --git a/src/test/java/htsjdk/samtools/util/AsyncBlockCompressedInputStreamTest.java b/src/test/java/htsjdk/samtools/util/AsyncBlockCompressedInputStreamTest.java index 957a86942..a1f9881a0 100644 --- a/src/test/java/htsjdk/samtools/util/AsyncBlockCompressedInputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/AsyncBlockCompressedInputStreamTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -32,7 +33,7 @@ import java.util.ArrayList; import java.util.List; -public class AsyncBlockCompressedInputStreamTest { +public class AsyncBlockCompressedInputStreamTest extends HtsjdkTest { private final File BAM_FILE = new File("src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"); @Test public void testAsync() throws Exception { diff --git a/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java b/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java index 817c60e54..ce4d44599 100644 --- a/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java @@ -23,10 +23,11 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class AsyncBufferedIteratorTest { +public class AsyncBufferedIteratorTest extends HtsjdkTest { private static class TestCloseableIterator implements CloseableIterator { private int[] results; private volatile int offset = 0; diff --git a/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java b/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java index c807ceffb..1d2c3043f 100644 --- a/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java +++ b/src/test/java/htsjdk/samtools/util/AsyncWriterTest.java @@ -23,10 +23,11 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class AsyncWriterTest { +public class AsyncWriterTest extends HtsjdkTest { private static class MyException extends RuntimeException { final Integer item; public MyException(Integer item) { diff --git a/src/test/java/htsjdk/samtools/util/BinaryCodecTest.java b/src/test/java/htsjdk/samtools/util/BinaryCodecTest.java index 91e114729..b59c9527d 100644 --- a/src/test/java/htsjdk/samtools/util/BinaryCodecTest.java +++ b/src/test/java/htsjdk/samtools/util/BinaryCodecTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -44,7 +45,7 @@ * the Broad Institute nor MIT can be responsible for its use, misuse, or functionality. */ -public class BinaryCodecTest { +public class BinaryCodecTest extends HtsjdkTest { public final static String TEST_BASENAME = "htsjdk-BinaryCodecTest"; @Test diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedFilePointerUtilTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedFilePointerUtilTest.java index 850b4bf62..38c3ec374 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedFilePointerUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedFilePointerUtilTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -31,8 +32,7 @@ import java.util.List; -public class BlockCompressedFilePointerUtilTest -{ +public class BlockCompressedFilePointerUtilTest extends HtsjdkTest { @Test public void basicTest() { diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedInputStreamTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedInputStreamTest.java index f4ce2cf52..4c9d532d0 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedInputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedInputStreamTest.java @@ -1,24 +1,21 @@ package htsjdk.samtools.util; -import java.io.*; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.Inflater; - -import java.io.File; -import java.io.FileInputStream; -import java.nio.file.Files; -import java.util.Arrays; - +import htsjdk.HtsjdkTest; +import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.util.zip.InflaterFactory; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import htsjdk.samtools.seekablestream.SeekableFileStream; +import java.io.*; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.zip.Inflater; -public class BlockCompressedInputStreamTest { +public class BlockCompressedInputStreamTest extends HtsjdkTest { // random data pulled from /dev/random then compressed using bgzip from tabix private static final File BLOCK_UNCOMPRESSED = new File("src/test/resources/htsjdk/samtools/util/random.bin"); private static final File BLOCK_COMPRESSED = new File("src/test/resources/htsjdk/samtools/util/random.bin.gz"); diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java index 69d72565f..a678c8dca 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.FileTruncatedException; import htsjdk.samtools.util.zip.DeflaterFactory; import org.testng.Assert; @@ -39,7 +40,7 @@ import java.util.Random; import java.util.zip.Deflater; -public class BlockCompressedOutputStreamTest { +public class BlockCompressedOutputStreamTest extends HtsjdkTest { private static final String HTSJDK_TRIBBLE_RESOURCES = "src/test/resources/htsjdk/tribble/"; diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java index 5b5837229..d9d20ccef 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -31,7 +32,7 @@ /** * @author alecw@broadinstitute.org */ -public class BlockCompressedTerminatorTest { +public class BlockCompressedTerminatorTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/util"); @Test diff --git a/src/test/java/htsjdk/samtools/util/CigarUtilTest.java b/src/test/java/htsjdk/samtools/util/CigarUtilTest.java index 0aca3951a..6fe7b7199 100644 --- a/src/test/java/htsjdk/samtools/util/CigarUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/CigarUtilTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.Cigar; import htsjdk.samtools.CigarElement; import htsjdk.samtools.TextCigarCodec; @@ -40,7 +41,7 @@ * * @author Martha Borkan mborkan@broadinstitute.org */ -public class CigarUtilTest { +public class CigarUtilTest extends HtsjdkTest { @Test(dataProvider="clipData") public void basicTest(final String testName, final int start, final String inputCigar, final boolean negativeStrand, diff --git a/src/test/java/htsjdk/samtools/util/CloseableIteratorTest.java b/src/test/java/htsjdk/samtools/util/CloseableIteratorTest.java index b96d1f67c..102b82436 100644 --- a/src/test/java/htsjdk/samtools/util/CloseableIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/CloseableIteratorTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -8,7 +9,7 @@ import java.util.List; import java.util.stream.Collectors; -public class CloseableIteratorTest { +public class CloseableIteratorTest extends HtsjdkTest { @Test public void testToList() { final List expected = Arrays.asList(1,2,3,4,5); diff --git a/src/test/java/htsjdk/samtools/util/CodeUtilTest.java b/src/test/java/htsjdk/samtools/util/CodeUtilTest.java index e8b9957d2..c4978c196 100644 --- a/src/test/java/htsjdk/samtools/util/CodeUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/CodeUtilTest.java @@ -1,9 +1,10 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class CodeUtilTest { +public class CodeUtilTest extends HtsjdkTest { @Test public void getOrElseTest() { diff --git a/src/test/java/htsjdk/samtools/util/ComparableTupleTest.java b/src/test/java/htsjdk/samtools/util/ComparableTupleTest.java index 7e8b082a5..708058d70 100644 --- a/src/test/java/htsjdk/samtools/util/ComparableTupleTest.java +++ b/src/test/java/htsjdk/samtools/util/ComparableTupleTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -8,7 +9,7 @@ /** * Created by farjoun on 1/28/16. */ -public class ComparableTupleTest { +public class ComparableTupleTest extends HtsjdkTest { private enum Tenum { Hi, diff --git a/src/test/java/htsjdk/samtools/util/CoordSpanInputSteamTest.java b/src/test/java/htsjdk/samtools/util/CoordSpanInputSteamTest.java index 1b9088220..07de15873 100644 --- a/src/test/java/htsjdk/samtools/util/CoordSpanInputSteamTest.java +++ b/src/test/java/htsjdk/samtools/util/CoordSpanInputSteamTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.seekablestream.ByteArraySeekableStream; import org.testng.Assert; import org.testng.annotations.Test; @@ -15,7 +16,7 @@ /** * Created by vadim on 25/03/2015. */ -public class CoordSpanInputSteamTest { +public class CoordSpanInputSteamTest extends HtsjdkTest { @Test public void test_first_3_bytes() throws IOException { diff --git a/src/test/java/htsjdk/samtools/util/DateParserTest.java b/src/test/java/htsjdk/samtools/util/DateParserTest.java index 04cfa7819..11ab2a6f8 100644 --- a/src/test/java/htsjdk/samtools/util/DateParserTest.java +++ b/src/test/java/htsjdk/samtools/util/DateParserTest.java @@ -72,6 +72,7 @@ This W3C work (including software, documents, or other related items) is package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -88,7 +89,7 @@ This W3C work (including software, documents, or other related items) is * @author bmahe@w3.org */ -public class DateParserTest { +public class DateParserTest extends HtsjdkTest { private static void test(final String isodate) { Date date = DateParser.parse(isodate); @@ -147,4 +148,4 @@ public static void assertDatesAreClose(final Date lhs, final Date rhs) { Assert.assertEquals(lhs.getSeconds(), rhs.getSeconds()); Assert.assertEquals(lhs.getTimezoneOffset(), rhs.getTimezoneOffset()); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java b/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java index a4f6478b4..eeca090d7 100644 --- a/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java +++ b/src/test/java/htsjdk/samtools/util/EdgingRecordAndOffsetTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import org.testng.annotations.BeforeTest; @@ -39,7 +40,7 @@ * */ -public class EdgingRecordAndOffsetTest { +public class EdgingRecordAndOffsetTest extends HtsjdkTest { private final byte[] qualities = {30, 50, 50, 60, 60, 70 ,70, 70, 80, 90}; private final byte[] bases = {'A', 'C', 'G', 'T', 'A', 'C', 'G', 'T', 'T', 'C'}; private SAMRecord record; diff --git a/src/test/java/htsjdk/samtools/util/HistogramTest.java b/src/test/java/htsjdk/samtools/util/HistogramTest.java index 62b1441ac..ef4446958 100644 --- a/src/test/java/htsjdk/samtools/util/HistogramTest.java +++ b/src/test/java/htsjdk/samtools/util/HistogramTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -12,7 +13,7 @@ import static java.lang.Math.abs; import static java.lang.StrictMath.pow; -public class HistogramTest { +public class HistogramTest extends HtsjdkTest { @Test(dataProvider = "histogramData") public void testHistogramFunctions(final int[] values, final double mean, final double stdev, final Integer trimByWidth) { diff --git a/src/test/java/htsjdk/samtools/util/IntervalListTest.java b/src/test/java/htsjdk/samtools/util/IntervalListTest.java index 3d919e881..613afde45 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalListTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalListTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMSequenceRecord; import htsjdk.variant.vcf.VCFFileReader; @@ -45,7 +46,7 @@ /** * Tests the IntervalList class */ -public class IntervalListTest { +public class IntervalListTest extends HtsjdkTest { final SAMFileHeader fileHeader; final IntervalList list1, list2, list3; diff --git a/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java b/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java index 533f9652e..5e975f917 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalTreeMapTest.java @@ -23,12 +23,13 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; import java.util.Iterator; -public class IntervalTreeMapTest { +public class IntervalTreeMapTest extends HtsjdkTest { @Test public void testBasic() { IntervalTreeMap m=new IntervalTreeMap(); diff --git a/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java b/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java index 34cb3c5be..dcd225ec0 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalTreeTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; @@ -36,7 +37,7 @@ * @author alecw@broadinstitute.org */ @Test(singleThreaded=true) // to assure that the common resources aren't clobbered -public class IntervalTreeTest { +public class IntervalTreeTest extends HtsjdkTest { @Test public void testNoMatches() { diff --git a/src/test/java/htsjdk/samtools/util/IoUtilTest.java b/src/test/java/htsjdk/samtools/util/IoUtilTest.java index 0e4cd7a1c..645d20d42 100644 --- a/src/test/java/htsjdk/samtools/util/IoUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/IoUtilTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -41,7 +42,7 @@ import java.util.Arrays; import java.util.List; -public class IoUtilTest { +public class IoUtilTest extends HtsjdkTest { private static final File SLURP_TEST_FILE = new File("src/test/resources/htsjdk/samtools/io/slurptest.txt"); private static final File EMPTY_FILE = new File("src/test/resources/htsjdk/samtools/io/empty.txt"); diff --git a/src/test/java/htsjdk/samtools/util/Iso8601DateTest.java b/src/test/java/htsjdk/samtools/util/Iso8601DateTest.java index ce0ae08c1..93b9d6544 100644 --- a/src/test/java/htsjdk/samtools/util/Iso8601DateTest.java +++ b/src/test/java/htsjdk/samtools/util/Iso8601DateTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -31,7 +32,7 @@ /** * @author alecw@broadinstitute.org */ -public class Iso8601DateTest { +public class Iso8601DateTest extends HtsjdkTest { @Test public void testBasic() { final String dateStr = "2008-12-15"; diff --git a/src/test/java/htsjdk/samtools/util/IupacTest.java b/src/test/java/htsjdk/samtools/util/IupacTest.java index 64b78c003..86b0a410e 100644 --- a/src/test/java/htsjdk/samtools/util/IupacTest.java +++ b/src/test/java/htsjdk/samtools/util/IupacTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.BamFileIoUtils; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMFileWriter; @@ -38,7 +39,7 @@ import java.io.File; import java.util.Arrays; -public class IupacTest { +public class IupacTest extends HtsjdkTest { @Test(dataProvider = "basicDataProvider") public void basic(final String tempFileExtension) throws Exception { final File outputFile = File.createTempFile("iupacTest.", tempFileExtension); diff --git a/src/test/java/htsjdk/samtools/util/MergingIteratorTest.java b/src/test/java/htsjdk/samtools/util/MergingIteratorTest.java index d36bb6d3b..e5964acf7 100644 --- a/src/test/java/htsjdk/samtools/util/MergingIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/MergingIteratorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -34,7 +35,7 @@ import java.util.LinkedList; import java.util.Queue; -public class MergingIteratorTest { +public class MergingIteratorTest extends HtsjdkTest { private static class QueueBackedIterator implements CloseableIterator { diff --git a/src/test/java/htsjdk/samtools/util/OverlapDetectorTest.java b/src/test/java/htsjdk/samtools/util/OverlapDetectorTest.java index ecde96560..d8adf2e2d 100644 --- a/src/test/java/htsjdk/samtools/util/OverlapDetectorTest.java +++ b/src/test/java/htsjdk/samtools/util/OverlapDetectorTest.java @@ -1,12 +1,13 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.*; -public class OverlapDetectorTest { +public class OverlapDetectorTest extends HtsjdkTest { @DataProvider(name="intervalsMultipleContigs") public Object[][] intervalsMultipleContigs(){ diff --git a/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java index af2acf59e..939c74858 100644 --- a/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/PositionalOutputStreamTest.java @@ -24,6 +24,7 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -33,7 +34,7 @@ /** * @author Daniel Gomez-Sanchez (magicDGS) */ -public class PositionalOutputStreamTest { +public class PositionalOutputStreamTest extends HtsjdkTest { @Test public void basicPositionTest() throws Exception { @@ -59,4 +60,4 @@ public void write(int b) throws IOException {} Assert.assertEquals(wrapped.getPosition(), position); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/samtools/util/QualityEncodingDetectorTest.java b/src/test/java/htsjdk/samtools/util/QualityEncodingDetectorTest.java index 9e014d7b7..071312d9b 100644 --- a/src/test/java/htsjdk/samtools/util/QualityEncodingDetectorTest.java +++ b/src/test/java/htsjdk/samtools/util/QualityEncodingDetectorTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMException; import htsjdk.samtools.SAMRecordSetBuilder; import htsjdk.samtools.SamReader; @@ -13,7 +14,7 @@ import java.util.Arrays; import java.util.List; -public class QualityEncodingDetectorTest { +public class QualityEncodingDetectorTest extends HtsjdkTest { private static class Testcase { private final File f; diff --git a/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java b/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java index e4e9ef993..0e0c9b265 100644 --- a/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java +++ b/src/test/java/htsjdk/samtools/util/RelativeIso8601DateTest.java @@ -1,5 +1,6 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; @@ -9,7 +10,7 @@ /** @author mccowan */ -public class RelativeIso8601DateTest { +public class RelativeIso8601DateTest extends HtsjdkTest { // 1 second resolution is ISO date private final static double DELTA_FOR_TIME = 1000; diff --git a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java index 7c96b742d..e57b8fd08 100644 --- a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.*; import htsjdk.samtools.reference.ReferenceSequenceFile; import htsjdk.samtools.reference.ReferenceSequenceFileFactory; @@ -36,7 +37,7 @@ /** * @author alecw@broadinstitute.org */ -public class SequenceUtilTest { +public class SequenceUtilTest extends HtsjdkTest { private static final String HEADER = "@HD\tVN:1.0\tSO:unsorted\n"; private static final String SEQUENCE_NAME= "@SQ\tSN:phix174.seq\tLN:5386\tUR:/seq/references/PhiX174/v0/PhiX174.fasta\tAS:PhiX174\tM5:3332ed720ac7eaa9b3655c06f6b9e196"; diff --git a/src/test/java/htsjdk/samtools/util/SolexaQualityConverterTest.java b/src/test/java/htsjdk/samtools/util/SolexaQualityConverterTest.java index 09cc82902..1e4e1464a 100644 --- a/src/test/java/htsjdk/samtools/util/SolexaQualityConverterTest.java +++ b/src/test/java/htsjdk/samtools/util/SolexaQualityConverterTest.java @@ -1,12 +1,13 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Arrays; -public class SolexaQualityConverterTest { +public class SolexaQualityConverterTest extends HtsjdkTest { //declared as a staic variable because we reuse it in IlluminaUtilTest public static Object[][] SOLEXA_QUALS_TO_PHRED_SCORE = new Object[][] { new Object[]{new byte[]{}, new byte[]{}}, diff --git a/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java b/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java index dc9e063cd..29f012084 100644 --- a/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java +++ b/src/test/java/htsjdk/samtools/util/SortingCollectionTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; @@ -41,7 +42,7 @@ import java.util.Iterator; import java.util.Random; -public class SortingCollectionTest { +public class SortingCollectionTest extends HtsjdkTest { // Create a separate directory for files so it is possible to confirm that the directory is emptied protected File tmpDir() { return new File(System.getProperty("java.io.tmpdir") + "/" + System.getProperty("user.name"), getClass().getSimpleName()); diff --git a/src/test/java/htsjdk/samtools/util/SortingLongCollectionTest.java b/src/test/java/htsjdk/samtools/util/SortingLongCollectionTest.java index 4817ef5b1..bcfa77e9c 100644 --- a/src/test/java/htsjdk/samtools/util/SortingLongCollectionTest.java +++ b/src/test/java/htsjdk/samtools/util/SortingLongCollectionTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; @@ -36,7 +37,7 @@ /** * @author alecw@broadinstitute.org */ -public class SortingLongCollectionTest { +public class SortingLongCollectionTest extends HtsjdkTest { // Create a separate directory for files so it is possible to confirm that the directory is emptied private final File tmpDir = new File(System.getProperty("java.io.tmpdir") + "/" + System.getProperty("user.name"), "SortingCollectionTest"); diff --git a/src/test/java/htsjdk/samtools/util/StringLineReaderTest.java b/src/test/java/htsjdk/samtools/util/StringLineReaderTest.java index 9919f891b..f90565024 100644 --- a/src/test/java/htsjdk/samtools/util/StringLineReaderTest.java +++ b/src/test/java/htsjdk/samtools/util/StringLineReaderTest.java @@ -23,10 +23,11 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class StringLineReaderTest { +public class StringLineReaderTest extends HtsjdkTest { private static final String[] TERMINATORS = {"\r", "\n", "\r\n"}; private static final boolean[] LAST_LINE_TERMINATED = {false, true}; diff --git a/src/test/java/htsjdk/samtools/util/StringUtilTest.java b/src/test/java/htsjdk/samtools/util/StringUtilTest.java deleted file mode 100644 index dbb2a0709..000000000 --- a/src/test/java/htsjdk/samtools/util/StringUtilTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2009 The Broad Institute - * - * 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 htsjdk.samtools.util; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -/** - * @author alecw@broadinstitute.org - */ -public class StringUtilTest { - @Test(dataProvider = "provider") - public void testSplit(final String input, final String[] expectedResult, final boolean concatenateExcess) { - String[] ret = new String[expectedResult.length]; - int tokensExpected; - for (tokensExpected = 0; tokensExpected < expectedResult.length && expectedResult[tokensExpected] != null; - ++tokensExpected) { - } - final int tokensFound; - if (concatenateExcess) { - tokensFound = StringUtil.splitConcatenateExcessTokens(input, ret, ':'); - } else { - tokensFound = StringUtil.split(input, ret, ':'); - } - Assert.assertEquals(tokensFound, tokensExpected); - Assert.assertEquals(ret, expectedResult); - } - - @DataProvider(name="provider") - public Object[][] splitScenarios() { - return new Object[][] { - {"A:BB:C", new String[]{"A", "BB", "C"}, false}, - {"A:BB:C", new String[]{"A", "BB", "C"}, true}, - {"A:BB", new String[]{"A", "BB", null}, false}, - {"A:BB", new String[]{"A", "BB", null}, true}, - {"A:BB:", new String[]{"A", "BB", null}, false}, - {"A:BB:", new String[]{"A", "BB", null}, true}, - {"A:BB:C:DDD", new String[]{"A", "BB", "C"}, false}, - {"A:BB:C:DDD", new String[]{"A", "BB", "C:DDD"}, true}, - {"A:", new String[]{"A", null, null}, false}, - {"A:", new String[]{"A", null, null}, true}, - {"A", new String[]{"A", null, null}, false}, - {"A", new String[]{"A", null, null}, true}, - {"A:BB:C", new String[]{"A", "BB", "C"}, false}, - {"A:BB:C:", new String[]{"A", "BB", "C:"}, true}, - }; - } - - @DataProvider(name="withinHammingDistanceProvider") - public Object[][] isWithinHammingDistanceProvider() { - return new Object[][] { - {"ATAC", "GCAT", 3, true}, - {"ATAC", "GCAT", 2, false}, - {"ATAC", "GCAT", 1, false}, - {"ATAC", "GCAT", 0, false} - }; - } - - @Test(dataProvider = "withinHammingDistanceProvider") - public void testIsWithinHammingDistance(final String s1, final String s2, final int maxHammingDistance, final boolean expectedResult) { - Assert.assertEquals(StringUtil.isWithinHammingDistance(s1, s2, maxHammingDistance), expectedResult); - } - - @DataProvider(name="withinHammingDistanceExceptionProvider") - public Object[][] isWithinHammingDistanceException() { - return new Object[][] { - {"ATAC", "GCT" , 3}, - {"ATAC", "AT" , 2}, - {"ATAC", "T" , 1}, - {"" , "GCAT", 0} - }; - } - - @Test(dataProvider = "withinHammingDistanceExceptionProvider", expectedExceptions = IllegalArgumentException.class) - public void testIsWithinHammingDistanceExceptions(final String s1, final String s2, final int maxHammingDistance) { - StringUtil.isWithinHammingDistance(s1, s2, maxHammingDistance); - } - - @Test(dataProvider = "withinHammingDistanceExceptionProvider", expectedExceptions = IllegalArgumentException.class) - public void testHammingDistanceExceptions(final String s1, final String s2, final int maxHammingDistance) { - StringUtil.hammingDistance(s1, s2); - } - - @DataProvider(name="hammingDistanceProvider") - public Object[][] hammingDistance() { - return new Object[][] { - {"ATAC" , "GCAT" , 3}, - {"ATAGC", "ATAGC", 0}, - {"ATAC" , "atac" , 4}, // Hamming distance is case sensitive. - {"" , "" , 0}, // Two empty strings should have Hamming distance of 0. - {"nAGTN", "nAGTN", 0} // Ensure that matching Ns are not counted as mismatches. - }; - } - - @Test(dataProvider = "hammingDistanceProvider") - public void testHammingDistance(final String s1, final String s2, final int expectedResult) { - Assert.assertEquals(StringUtil.hammingDistance(s1, s2), expectedResult); - } - -} diff --git a/src/test/java/htsjdk/samtools/util/TrimmingUtilTest.java b/src/test/java/htsjdk/samtools/util/TrimmingUtilTest.java index 12cffc671..811083976 100644 --- a/src/test/java/htsjdk/samtools/util/TrimmingUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/TrimmingUtilTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Tests for a simple phred-style quality trimming algorithm. */ -public class TrimmingUtilTest { +public class TrimmingUtilTest extends HtsjdkTest { @Test public void testEasyCases() { Assert.assertEquals(TrimmingUtil.findQualityTrimPoint(byteArray(30,30,30,30,30, 2, 2, 2, 2, 2), 15), 5); diff --git a/src/test/java/htsjdk/samtools/util/TupleTest.java b/src/test/java/htsjdk/samtools/util/TupleTest.java index bed4550f1..431466ddf 100644 --- a/src/test/java/htsjdk/samtools/util/TupleTest.java +++ b/src/test/java/htsjdk/samtools/util/TupleTest.java @@ -1,12 +1,13 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Created by farjoun on 1/29/16. */ -public class TupleTest { +public class TupleTest extends HtsjdkTest { @Test public void testEquals() throws Exception { @@ -59,4 +60,4 @@ public void testToString() throws Exception { Assert.assertEquals(new Tuple<>(null, null).toString(), "[null, null]"); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/tribble/AbstractFeatureReaderTest.java b/src/test/java/htsjdk/tribble/AbstractFeatureReaderTest.java index da0f84301..947d319fe 100644 --- a/src/test/java/htsjdk/tribble/AbstractFeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/AbstractFeatureReaderTest.java @@ -2,6 +2,7 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import htsjdk.HtsjdkTest; import htsjdk.samtools.FileTruncatedException; import htsjdk.samtools.util.TestUtil; import htsjdk.tribble.bed.BEDCodec; @@ -29,7 +30,7 @@ * @author jacob * @date 2013-Apr-10 */ -public class AbstractFeatureReaderTest { +public class AbstractFeatureReaderTest extends HtsjdkTest { final static String HTTP_INDEXED_VCF_PATH = TestUtil.BASE_URL_FOR_HTTP_TESTS + "ex2.vcf"; final static String LOCAL_MIRROR_HTTP_INDEXED_VCF_PATH = VariantBaseTest.variantTestDataRoot + "ex2.vcf"; diff --git a/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java b/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java index eff8939d8..eac19742a 100644 --- a/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java +++ b/src/test/java/htsjdk/tribble/BinaryFeaturesTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble; +import htsjdk.HtsjdkTest; import htsjdk.tribble.bed.BEDCodec; import htsjdk.tribble.example.ExampleBinaryCodec; import htsjdk.tribble.readers.LineIterator; @@ -13,7 +14,7 @@ import java.util.List; -public class BinaryFeaturesTest { +public class BinaryFeaturesTest extends HtsjdkTest { @DataProvider(name = "BinaryFeatureSources") public Object[][] createData1() { return new Object[][] { diff --git a/src/test/java/htsjdk/tribble/FeatureReaderTest.java b/src/test/java/htsjdk/tribble/FeatureReaderTest.java index d62693c19..f43b5b15d 100644 --- a/src/test/java/htsjdk/tribble/FeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/FeatureReaderTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble; +import htsjdk.HtsjdkTest; import htsjdk.samtools.seekablestream.SeekableFileStream; import htsjdk.samtools.util.CloserUtil; import htsjdk.samtools.util.LocationAware; @@ -22,7 +23,7 @@ import java.util.List; -public class FeatureReaderTest { +public class FeatureReaderTest extends HtsjdkTest { private final static File asciiBedFile = new File(TestUtils.DATA_DIR + "test.bed"); private File binaryBedFile; private final static File tabixBedFile = new File(TestUtils.DATA_DIR + "test.tabix.bed.gz"); diff --git a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java index 0223d41cd..37a5295dc 100644 --- a/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/TribbleIndexFeatureReaderTest.java @@ -1,7 +1,7 @@ package htsjdk.tribble; +import htsjdk.HtsjdkTest; import htsjdk.tribble.readers.LineIterator; -import htsjdk.tribble.TestUtils; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.vcf.VCFCodec; import org.testng.Assert; @@ -9,12 +9,9 @@ import org.testng.annotations.Test; import java.io.IOException; -import java.net.URISyntaxException; -import static org.testng.Assert.assertEquals; - -public class TribbleIndexFeatureReaderTest { +public class TribbleIndexFeatureReaderTest extends HtsjdkTest { @DataProvider(name = "featureFileStrings") public Object[][] createFeatureFileStrings() { diff --git a/src/test/java/htsjdk/tribble/TribbleTest.java b/src/test/java/htsjdk/tribble/TribbleTest.java index e8366c4b0..3874c7f71 100644 --- a/src/test/java/htsjdk/tribble/TribbleTest.java +++ b/src/test/java/htsjdk/tribble/TribbleTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble; +import htsjdk.HtsjdkTest; import htsjdk.tribble.util.TabixUtils; import org.testng.Assert; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ import java.io.File; -public class TribbleTest { +public class TribbleTest extends HtsjdkTest { @Test public void testStandardIndex() { diff --git a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java index dbf23a0e5..cc0255b62 100644 --- a/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java +++ b/src/test/java/htsjdk/tribble/bed/BEDCodecTest.java @@ -24,6 +24,7 @@ package htsjdk.tribble.bed; +import htsjdk.HtsjdkTest; import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.Feature; import htsjdk.tribble.TestUtils; @@ -43,7 +44,7 @@ import java.io.IOException; import java.util.List; -public class BEDCodecTest { +public class BEDCodecTest extends HtsjdkTest { @Test public void testSimpleDecode() { diff --git a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java index 016049f32..964a3c3d6 100644 --- a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java @@ -23,16 +23,14 @@ */ package htsjdk.tribble.index; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; -import htsjdk.samtools.util.IOUtil; import htsjdk.tribble.TestUtils; import htsjdk.tribble.TribbleException; import htsjdk.tribble.bed.BEDCodec; -import htsjdk.tribble.index.linear.LinearIndex; import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.index.tabix.TabixIndex; -import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.variant.vcf.VCFCodec; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; @@ -40,15 +38,13 @@ import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; -import java.io.OutputStream; import java.util.List; /** * User: jacob * Date: 2012-Aug-23 */ -public class IndexFactoryTest { +public class IndexFactoryTest extends HtsjdkTest { final File sortedBedFile = new File(TestUtils.DATA_DIR + "bed/Unigene.sample.bed"); final File unsortedBedFile = new File(TestUtils.DATA_DIR + "bed/unsorted.bed"); diff --git a/src/test/java/htsjdk/tribble/index/IndexTest.java b/src/test/java/htsjdk/tribble/index/IndexTest.java index 56200e672..06fb311a5 100644 --- a/src/test/java/htsjdk/tribble/index/IndexTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexTest.java @@ -2,15 +2,13 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import htsjdk.samtools.util.IOUtil; -import htsjdk.tribble.AbstractFeatureReader; +import htsjdk.HtsjdkTest; import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.TestUtils; import htsjdk.tribble.Tribble; import htsjdk.tribble.bed.BEDCodec; import htsjdk.tribble.index.interval.IntervalTreeIndex; import htsjdk.tribble.index.linear.LinearIndex; -import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.index.tabix.TabixIndex; import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.tribble.util.TabixUtils; @@ -22,16 +20,13 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.net.URISyntaxException; import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -public class IndexTest { +public class IndexTest extends HtsjdkTest { private final static String CHR = "1"; private final static File MassiveIndexFile = new File(TestUtils.DATA_DIR + "Tb.vcf.idx"); diff --git a/src/test/java/htsjdk/tribble/index/interval/IntervalTreeTest.java b/src/test/java/htsjdk/tribble/index/interval/IntervalTreeTest.java index ca4708933..9a8a0a68e 100644 --- a/src/test/java/htsjdk/tribble/index/interval/IntervalTreeTest.java +++ b/src/test/java/htsjdk/tribble/index/interval/IntervalTreeTest.java @@ -18,6 +18,7 @@ package htsjdk.tribble.index.interval; +import htsjdk.HtsjdkTest; import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.CloseableTribbleIterator; import htsjdk.tribble.FeatureReader; @@ -42,7 +43,7 @@ * User: jrobinso * Date: Mar 24, 2010 */ -public class IntervalTreeTest { +public class IntervalTreeTest extends HtsjdkTest { static IntervalTree tree; diff --git a/src/test/java/htsjdk/tribble/index/linear/LinearIndexTest.java b/src/test/java/htsjdk/tribble/index/linear/LinearIndexTest.java index 09f920e41..e20dc1589 100644 --- a/src/test/java/htsjdk/tribble/index/linear/LinearIndexTest.java +++ b/src/test/java/htsjdk/tribble/index/linear/LinearIndexTest.java @@ -18,6 +18,7 @@ package htsjdk.tribble.index.linear; +import htsjdk.HtsjdkTest; import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.CloseableTribbleIterator; import htsjdk.tribble.FeatureReader; @@ -38,7 +39,7 @@ import java.util.List; import java.util.Set; -public class LinearIndexTest { +public class LinearIndexTest extends HtsjdkTest { private static final File RANDOM_FILE = new File("notMeaningful"); private final static Block CHR1_B1 = new Block(1, 10); diff --git a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java index 6981b8751..0473a3d90 100644 --- a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java +++ b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java @@ -23,6 +23,7 @@ */ package htsjdk.tribble.index.tabix; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.BlockCompressedOutputStream; import htsjdk.tribble.index.IndexFactory; import htsjdk.tribble.util.LittleEndianOutputStream; @@ -40,7 +41,7 @@ import java.io.IOException; import java.util.Iterator; -public class TabixIndexTest { +public class TabixIndexTest extends HtsjdkTest { private static final File SMALL_TABIX_FILE = new File("src/test/resources/htsjdk/tribble/tabix/trioDup.vcf.gz.tbi"); private static final File BIGGER_TABIX_FILE = new File("src/test/resources/htsjdk/tribble/tabix/bigger.vcf.gz.tbi"); diff --git a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java index 822f6cf6a..b4aa6ba2c 100644 --- a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -15,17 +16,7 @@ * User: jacob * Date: 2012/05/09 */ -public class AsciiLineReaderTest { - @BeforeMethod - public void setUp() throws Exception { - - } - - @AfterMethod - public void tearDown() throws Exception { - - } - +public class AsciiLineReaderTest extends HtsjdkTest { /** * Test that we read the correct number of lines * from a file diff --git a/src/test/java/htsjdk/tribble/readers/LongLineBufferedReaderTest.java b/src/test/java/htsjdk/tribble/readers/LongLineBufferedReaderTest.java index 6c4c94673..3e498e17c 100644 --- a/src/test/java/htsjdk/tribble/readers/LongLineBufferedReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/LongLineBufferedReaderTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; import org.testng.Assert; import org.testng.annotations.Test; @@ -11,7 +12,7 @@ /** * @author mccowan */ -public class LongLineBufferedReaderTest { +public class LongLineBufferedReaderTest extends HtsjdkTest { /** * Test that we read the correct number of lines diff --git a/src/test/java/htsjdk/tribble/readers/PositionalBufferedStreamTest.java b/src/test/java/htsjdk/tribble/readers/PositionalBufferedStreamTest.java index 3dd7cf38e..8d9db2a7b 100644 --- a/src/test/java/htsjdk/tribble/readers/PositionalBufferedStreamTest.java +++ b/src/test/java/htsjdk/tribble/readers/PositionalBufferedStreamTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -20,7 +21,7 @@ * User: jacob * Date: 2012/05/09 */ -public class PositionalBufferedStreamTest { +public class PositionalBufferedStreamTest extends HtsjdkTest { InputStream FileIs; long expectedBytes; diff --git a/src/test/java/htsjdk/tribble/readers/ReaderTest.java b/src/test/java/htsjdk/tribble/readers/ReaderTest.java index d700e041b..7ac1d5787 100644 --- a/src/test/java/htsjdk/tribble/readers/ReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/ReaderTest.java @@ -1,6 +1,7 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -18,7 +19,7 @@ /** * Tests for streams and readers */ -public class ReaderTest { +public class ReaderTest extends HtsjdkTest { @BeforeClass public void setup() throws IOException { } diff --git a/src/test/java/htsjdk/tribble/readers/SynchronousLineReaderUnitTest.java b/src/test/java/htsjdk/tribble/readers/SynchronousLineReaderUnitTest.java index fbb5d188a..0c0deab41 100644 --- a/src/test/java/htsjdk/tribble/readers/SynchronousLineReaderUnitTest.java +++ b/src/test/java/htsjdk/tribble/readers/SynchronousLineReaderUnitTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; import org.testng.Assert; import org.testng.annotations.Test; @@ -12,7 +13,7 @@ /** * @author mccowan */ -public class SynchronousLineReaderUnitTest { +public class SynchronousLineReaderUnitTest extends HtsjdkTest { @Test public void testLineReaderIterator_streamConstructor() throws Exception { final File filePath = new File(TestUtils.DATA_DIR + "gwas/smallp.gwas"); diff --git a/src/test/java/htsjdk/tribble/readers/TabixReaderTest.java b/src/test/java/htsjdk/tribble/readers/TabixReaderTest.java index d7b36dfab..3b47f417b 100644 --- a/src/test/java/htsjdk/tribble/readers/TabixReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/TabixReaderTest.java @@ -1,6 +1,7 @@ package htsjdk.tribble.readers; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.TestUtil; import htsjdk.tribble.TestUtils; import org.testng.Assert; @@ -23,7 +24,7 @@ * Time: 8:57:40 PM * To change this template use File | Settings | File Templates. */ -public class TabixReaderTest { +public class TabixReaderTest extends HtsjdkTest { static String tabixFile = TestUtils.DATA_DIR + "tabix/trioDup.vcf.gz"; static TabixReader tabixReader; diff --git a/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java b/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java index 85f414e87..c974790dd 100644 --- a/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java +++ b/src/test/java/htsjdk/tribble/util/ParsingUtilsTest.java @@ -3,20 +3,15 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.IOUtil; -import java.io.BufferedWriter; -import java.io.File; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; import org.testng.Assert; import org.testng.annotations.Test; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; +import java.io.*; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.List; @@ -24,7 +19,7 @@ /** * Parsing utils tests */ -public class ParsingUtilsTest { +public class ParsingUtilsTest extends HtsjdkTest { static final String AVAILABLE_FTP_URL = "ftp://ftp.broadinstitute.org/pub/igv/TEST/test.txt"; static final String UNAVAILABLE_FTP_URL = "ftp://www.example.com/file.txt"; diff --git a/src/test/java/htsjdk/tribble/util/ftp/FTPClientTest.java b/src/test/java/htsjdk/tribble/util/ftp/FTPClientTest.java index 3979b0858..6b77f913e 100644 --- a/src/test/java/htsjdk/tribble/util/ftp/FTPClientTest.java +++ b/src/test/java/htsjdk/tribble/util/ftp/FTPClientTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.util.ftp; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.ftp.FTPClient; import htsjdk.samtools.util.ftp.FTPReply; import org.testng.Assert; @@ -15,7 +16,7 @@ * @author Jim Robinson * @since 10/3/11 */ -public class FTPClientTest { +public class FTPClientTest extends HtsjdkTest { static String host = "ftp.broadinstitute.org"; static String file = "/pub/igv/TEST/test.txt"; diff --git a/src/test/java/htsjdk/tribble/util/ftp/FTPUtilsTest.java b/src/test/java/htsjdk/tribble/util/ftp/FTPUtilsTest.java index a5f3b0e58..87000ee14 100644 --- a/src/test/java/htsjdk/tribble/util/ftp/FTPUtilsTest.java +++ b/src/test/java/htsjdk/tribble/util/ftp/FTPUtilsTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.util.ftp; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.ftp.FTPUtils; import org.testng.annotations.Test; @@ -12,7 +13,7 @@ * @author Jim Robinson * @since 10/4/11 */ -public class FTPUtilsTest { +public class FTPUtilsTest extends HtsjdkTest { @Test public void testResourceAvailable() throws Exception { diff --git a/src/test/java/htsjdk/tribble/util/popgen/HardyWeinbergCalculationTest.java b/src/test/java/htsjdk/tribble/util/popgen/HardyWeinbergCalculationTest.java index fcf1bea0b..d2b54555c 100644 --- a/src/test/java/htsjdk/tribble/util/popgen/HardyWeinbergCalculationTest.java +++ b/src/test/java/htsjdk/tribble/util/popgen/HardyWeinbergCalculationTest.java @@ -1,5 +1,6 @@ package htsjdk.tribble.util.popgen; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -7,7 +8,7 @@ /** * Created by farjoun on 7/18/14. */ -public class HardyWeinbergCalculationTest { +public class HardyWeinbergCalculationTest extends HtsjdkTest { @DataProvider public Object[][] testHwCalculateData() { diff --git a/src/test/java/htsjdk/variant/PrintVariantsExampleTest.java b/src/test/java/htsjdk/variant/PrintVariantsExampleTest.java index c82f2dbf3..9f273a94d 100644 --- a/src/test/java/htsjdk/variant/PrintVariantsExampleTest.java +++ b/src/test/java/htsjdk/variant/PrintVariantsExampleTest.java @@ -25,20 +25,19 @@ package htsjdk.variant; +import htsjdk.HtsjdkTest; import htsjdk.samtools.util.IOUtil; import htsjdk.variant.example.PrintVariantsExample; import org.testng.Assert; -import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; -import java.util.OptionalInt; import java.util.stream.IntStream; -public class PrintVariantsExampleTest { +public class PrintVariantsExampleTest extends HtsjdkTest { @Test public void testExampleWriteFile() throws IOException { final File tempFile = File.createTempFile("example", ".vcf"); diff --git a/src/test/java/htsjdk/variant/VariantBaseTest.java b/src/test/java/htsjdk/variant/VariantBaseTest.java index 87345a054..7a3417b52 100644 --- a/src/test/java/htsjdk/variant/VariantBaseTest.java +++ b/src/test/java/htsjdk/variant/VariantBaseTest.java @@ -25,6 +25,7 @@ package htsjdk.variant; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; import htsjdk.variant.variantcontext.Genotype; @@ -44,7 +45,7 @@ /** * Base class for test classes within org.broadinstitute.variant */ -public class VariantBaseTest { +public class VariantBaseTest extends HtsjdkTest { public static final String variantTestDataRoot = new File("src/test/resources/htsjdk/variant/").getAbsolutePath() + "/"; diff --git a/src/test/java/htsjdk/variant/utils/SAMSequenceDictionaryExtractorTest.java b/src/test/java/htsjdk/variant/utils/SAMSequenceDictionaryExtractorTest.java index 9fb13e802..af3241112 100644 --- a/src/test/java/htsjdk/variant/utils/SAMSequenceDictionaryExtractorTest.java +++ b/src/test/java/htsjdk/variant/utils/SAMSequenceDictionaryExtractorTest.java @@ -23,6 +23,7 @@ */ package htsjdk.variant.utils; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.util.SequenceUtil; import org.testng.annotations.DataProvider; @@ -34,7 +35,7 @@ /** * @author farjoun on 4/9/14. */ -public class SAMSequenceDictionaryExtractorTest { +public class SAMSequenceDictionaryExtractorTest extends HtsjdkTest { String path = "src/test/resources/htsjdk/variant/utils/SamSequenceDictionaryExtractor/"; @DataProvider(name = "testExtractDictionaries") diff --git a/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java b/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java index 613dec57a..b8476592e 100644 --- a/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java +++ b/src/test/java/htsjdk/variant/variantcontext/VariantContextTestProvider.java @@ -25,6 +25,7 @@ package htsjdk.variant.variantcontext; +import htsjdk.HtsjdkTest; import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.FeatureCodecHeader; import htsjdk.tribble.Tribble; @@ -69,7 +70,7 @@ * @author Your Name * @since Date created */ -public class VariantContextTestProvider { +public class VariantContextTestProvider extends HtsjdkTest { final private static boolean ENABLE_GENOTYPE_TESTS = true; final private static boolean ENABLE_A_AND_G_TESTS = true; final private static boolean ENABLE_VARARRAY_TESTS = true; @@ -1011,4 +1012,4 @@ public static void main( String argv[] ) { throw new RuntimeException(e); } } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/CompoundFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/CompoundFilterTest.java index 0a4985373..efa788efb 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/CompoundFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/CompoundFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.variantcontext.VariantContextBuilder; @@ -15,7 +16,7 @@ /** * Created by farjoun on 9/9/15. */ -public class CompoundFilterTest { +public class CompoundFilterTest extends HtsjdkTest { static AllPassFilter pass = new AllPassFilter(); static AllFailFilter fail = new AllFailFilter(); @@ -75,4 +76,4 @@ public void testCompoundFilter(final VariantContextFilter filter, final boolean shouldPass) { Assert.assertEquals(filter.test(vc), shouldPass, filter.toString()); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/FilteringVariantContextIteratorTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/FilteringVariantContextIteratorTest.java index d8decfdd9..eeb221378 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/FilteringVariantContextIteratorTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/FilteringVariantContextIteratorTest.java @@ -24,6 +24,7 @@ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; @@ -36,7 +37,7 @@ * Tests for testing the (VariantContext)FilteringVariantContextIterator, and the HeterozygosityFilter */ -public class FilteringVariantContextIteratorTest { +public class FilteringVariantContextIteratorTest extends HtsjdkTest { final File testDir = new File("src/test/resources/htsjdk/variant"); @DataProvider diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/GenotypeQualityFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/GenotypeQualityFilterTest.java index 809133ff3..a615f8140 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/GenotypeQualityFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/GenotypeQualityFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.GenotypeBuilder; import htsjdk.variant.variantcontext.VariantContext; @@ -37,7 +38,7 @@ import java.util.Iterator; import java.util.List; -public class GenotypeQualityFilterTest { +public class GenotypeQualityFilterTest extends HtsjdkTest { Allele refA = Allele.create("A", true); Allele G = Allele.create("G", false); diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/HeterozygosityFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/HeterozygosityFilterTest.java index b4cd3a84f..e2e988184 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/HeterozygosityFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/HeterozygosityFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.GenotypeBuilder; import htsjdk.variant.variantcontext.VariantContext; @@ -37,7 +38,7 @@ import java.util.Iterator; import java.util.List; -public class HeterozygosityFilterTest { +public class HeterozygosityFilterTest extends HtsjdkTest { Allele refA = Allele.create("A", true); Allele G = Allele.create("G", false); diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/JavascriptVariantFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/JavascriptVariantFilterTest.java index 3993b792f..7fb98c33b 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/JavascriptVariantFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/JavascriptVariantFilterTest.java @@ -23,6 +23,7 @@ */ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; @@ -36,7 +37,7 @@ * @author Pierre Lindenbaum PhD Institut du Thorax - INSERM - Nantes - France */ -public class JavascriptVariantFilterTest { +public class JavascriptVariantFilterTest extends HtsjdkTest { final File testDir = new File("src/test/resources/htsjdk/variant"); @DataProvider diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/PassingVariantFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/PassingVariantFilterTest.java index 3cbb60ca3..da2826495 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/PassingVariantFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/PassingVariantFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.variantcontext.VariantContextBuilder; @@ -16,7 +17,7 @@ /** * Created by farjoun on 9/10/15. */ -public class PassingVariantFilterTest { +public class PassingVariantFilterTest extends HtsjdkTest { Allele refA = Allele.create("A", true); Allele G = Allele.create("G", false); @@ -43,4 +44,4 @@ public void testPassingVariantFilter(final VariantContext vc, final boolean shou Assert.assertEquals(passingVariantFilter.test(vc), shouldPass, vc.toString()); } -} \ No newline at end of file +} diff --git a/src/test/java/htsjdk/variant/variantcontext/filter/SnpFilterTest.java b/src/test/java/htsjdk/variant/variantcontext/filter/SnpFilterTest.java index 74f1bb5de..e091ca0b6 100644 --- a/src/test/java/htsjdk/variant/variantcontext/filter/SnpFilterTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/filter/SnpFilterTest.java @@ -1,5 +1,6 @@ package htsjdk.variant.variantcontext.filter; +import htsjdk.HtsjdkTest; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.variantcontext.VariantContextBuilder; @@ -16,7 +17,7 @@ /** * Created by farjoun on 9/9/15. */ -public class SnpFilterTest { +public class SnpFilterTest extends HtsjdkTest { Allele refA = Allele.create("A", true); Allele refAG = Allele.create("AG", true); diff --git a/src/test/java/htsjdk/variant/variantcontext/writer/TabixOnTheFlyIndexCreationTest.java b/src/test/java/htsjdk/variant/variantcontext/writer/TabixOnTheFlyIndexCreationTest.java index 2fd1520ba..f8c8fd193 100644 --- a/src/test/java/htsjdk/variant/variantcontext/writer/TabixOnTheFlyIndexCreationTest.java +++ b/src/test/java/htsjdk/variant/variantcontext/writer/TabixOnTheFlyIndexCreationTest.java @@ -23,6 +23,7 @@ */ package htsjdk.variant.variantcontext.writer; +import htsjdk.HtsjdkTest; import htsjdk.tribble.AbstractFeatureReader; import htsjdk.tribble.CloseableTribbleIterator; import htsjdk.tribble.FeatureReader; @@ -36,7 +37,7 @@ import java.io.File; import java.util.EnumSet; -public class TabixOnTheFlyIndexCreationTest { +public class TabixOnTheFlyIndexCreationTest extends HtsjdkTest { private static final File SMALL_VCF = new File("src/test/resources/htsjdk/tribble/tabix/trioDup.vcf.gz"); @Test public void simpleTest() throws Exception { diff --git a/src/test/java/htsjdk/variant/vcf/VCFEncoderTest.java b/src/test/java/htsjdk/variant/vcf/VCFEncoderTest.java index 2c4ff0f08..6d4c23b9d 100644 --- a/src/test/java/htsjdk/variant/vcf/VCFEncoderTest.java +++ b/src/test/java/htsjdk/variant/vcf/VCFEncoderTest.java @@ -1,5 +1,6 @@ package htsjdk.variant.vcf; +import htsjdk.HtsjdkTest; import htsjdk.tribble.util.ParsingUtils; import htsjdk.variant.variantcontext.Allele; import htsjdk.variant.variantcontext.GenotypeBuilder; @@ -18,7 +19,7 @@ import java.util.Set; import java.util.TreeSet; -public class VCFEncoderTest { +public class VCFEncoderTest extends HtsjdkTest { @DataProvider(name = "VCFWriterDoubleFormatTestData") public Object[][] makeVCFWriterDoubleFormatTestData() { diff --git a/src/test/scala/htsjdk/UnitSpec.scala b/src/test/scala/htsjdk/UnitSpec.scala new file mode 100644 index 000000000..db533a12e --- /dev/null +++ b/src/test/scala/htsjdk/UnitSpec.scala @@ -0,0 +1,6 @@ +package htsjdk + +import org.scalatest.{FlatSpec, Matchers} + +/** Base class for all Scala tests. */ +class UnitSpec extends FlatSpec with Matchers diff --git a/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala b/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala new file mode 100644 index 000000000..6962e3674 --- /dev/null +++ b/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala @@ -0,0 +1,134 @@ +/* + * The MIT License + * + * Copyright (c) 2017 The Broad Institute + * + * 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 htsjdk.samtools.util + +import htsjdk.UnitSpec + +class StringUtilTest extends UnitSpec { + "StringUtil.split" should "behave like String.split(char)" in { + Seq("A:BB:C", "A:BB", "A:BB:", "A:BB:C:DDD", "A:", "A", "A:BB:C").foreach { s => + val arr = new Array[String](10) + val count = StringUtil.split(s, arr, ':') + arr.take(count) shouldBe s.split(':') + } + } + + "StringUtil.splitConcatenateExcessTokens" should "behave like String.split(regex, limit)" in { + Seq("A:BB:C", "A:BB", "A:BB:", "A:BB:C:DDD", "A:", "A", "A:BB:C:").foreach { s => + val arr = new Array[String](3) + val count = StringUtil.splitConcatenateExcessTokens(s, arr, ':') + arr.take(count) shouldBe s.split(":", 3).filter(_.nonEmpty) + } + } + + "StringUtil.join" should "join tokens with a separator" in { + StringUtil.join(",", 1, "hello", 'T') shouldBe "1,hello,T" + StringUtil.join(",") shouldBe "" + } + + "StringUtil.hammingDistance" should "return zero for two empty sequences" in { + StringUtil.hammingDistance("", "") shouldBe 0 + } + + Seq(("ATAC", "GCAT", 3), ("ATAGC", "ATAGC", 0)).foreach { case (s1, s2, distance) => + it should s"return distance $distance between $s1 and $s2" in { + StringUtil.hammingDistance(s1, s2) shouldBe distance + } + } + + it should "be case sensitive" in { + StringUtil.hammingDistance("ATAC", "atac") shouldBe 4 + } + + it should "count Ns as matching when computing distance" in { + StringUtil.hammingDistance("nAGTN", "nAGTN") shouldBe 0 + } + + it should "throw an exception if two strings of different lengths are provided" in { + an[Exception] shouldBe thrownBy { StringUtil.hammingDistance("", "ABC")} + an[Exception] shouldBe thrownBy { StringUtil.hammingDistance("Abc", "wxyz")} + } + + "StringUtil.isWithinHammingDistance" should "agree with StringUtil.hammingDistance" in { + Seq(("ATAC", "GCAT", 3), ("ATAC", "GCAT", 2), ("ATAC", "GCAT", 1), ("ATAC", "GCAT", 0)).foreach { case (s1, s2, within) => + StringUtil.isWithinHammingDistance(s1, s2, within) shouldBe (StringUtil.hammingDistance(s1, s2) <= within) + } + } + + it should "throw an exception if the two strings are of different lengths" in { + an[Exception] shouldBe thrownBy { StringUtil.isWithinHammingDistance("", "ABC", 2)} + an[Exception] shouldBe thrownBy { StringUtil.isWithinHammingDistance("Abc", "wxyz", 2)} + } + + "StringUtil.toLowerCase(byte)" should "work just like Character.toLowerCase" in { + 0 to 127 foreach {i => StringUtil.toLowerCase(i.toByte) shouldBe i.toChar.toLower.toByte } + } + + "StringUtil.toUpperCase(byte)" should "work just like Character.toUpperCase" in { + 0 to 127 foreach {i => StringUtil.toUpperCase(i.toByte) shouldBe i.toChar.toUpper.toByte } + } + + "StringUtil.toUpperCase(byte[])" should "do upper case characters" in { + val seq = "atACgtaCGTgatcCAtATATgATtatgacNryuAN" + val bytes = seq.getBytes + StringUtil.toUpperCase(bytes) + bytes shouldBe seq.toUpperCase.getBytes + } + + "StringUtil.assertCharactersNotInString" should "catch illegal characters" in { + an[Exception] shouldBe thrownBy { + StringUtil.assertCharactersNotInString("Hello World!", ' ', '!', '_') + } + } + + it should "not fail when there are no illegal characters present" in { + StringUtil.assertCharactersNotInString("HelloWorld", ' ', '!', '_') + } + + val textForWrapping = + """This is a little bit + |of text with nice short + |lines. + """.stripMargin.trim + + "StringUtil.wordWrap" should "not wrap when lines are shorter than the given length" in { + StringUtil.wordWrap(textForWrapping, 50) shouldBe textForWrapping + } + + it should "wrap text when lines are longer than length give" in { + val result = StringUtil.wordWrap(textForWrapping, 15) + result.lines.size shouldBe 5 + result.lines.foreach(line => line.length should be <= 15) + } + + "StringUtil.intValuesToString(int[])" should "generate a CSV string of ints" in { + val ints = Array[Int](1, 2, 3, 11, 22, 33, Int.MinValue, 0, Int.MaxValue) + StringUtil.intValuesToString(ints) shouldBe ints.mkString(", ") + } + + "StringUtil.intValuesToString(short[])" should "generate a CSV string of ints" in { + val ints = Array[Short](1, 2, 3, 11, 22, 33, Short.MinValue, 0, Short.MaxValue) + StringUtil.intValuesToString(ints) shouldBe ints.mkString(", ") + } +} From e1a37cfbed04ce47b5e452853c59f8df3d11332f Mon Sep 17 00:00:00 2001 From: Yulia_Kamyshova Date: Sun, 16 Apr 2017 05:44:11 +0300 Subject: [PATCH 094/137] replace method getRefPos() into abstract class EdgingRecordAndOffset and delete constructor in AbstractRecordAndOffset (#849) --- .../htsjdk/samtools/util/AbstractRecordAndOffset.java | 17 ----------------- .../htsjdk/samtools/util/EdgingRecordAndOffset.java | 2 ++ .../samtools/util/AbstractRecordAndOffsetTest.java | 3 +-- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java b/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java index 28b9d34b3..e76b66683 100644 --- a/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java +++ b/src/main/java/htsjdk/samtools/util/AbstractRecordAndOffset.java @@ -49,16 +49,6 @@ /** * @param record inner SAMRecord * @param offset from the start of the read - * @param length of alignment block - * @param refPos corresponding to read offset reference position - */ - public AbstractRecordAndOffset(final SAMRecord record, final int offset, int length, int refPos) { - this(record, offset); - } - - /** - * @param record inner SAMRecord - * @param offset from the start of the read */ public AbstractRecordAndOffset(final SAMRecord record, final int offset) { this.offset = offset; @@ -94,13 +84,6 @@ public int getLength() { } /** - * @return the position in reference sequence, to which the start of alignment block is aligned. - */ - public int getRefPos() { - return -1; - } - - /** * @return read name of inner SAMRecord. */ public String getReadName() { diff --git a/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java index 85beb66f0..df282b00f 100644 --- a/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java +++ b/src/main/java/htsjdk/samtools/util/EdgingRecordAndOffset.java @@ -56,6 +56,8 @@ private EdgingRecordAndOffset(SAMRecord record, int offset) { public abstract byte getBaseQuality(int position); + public abstract int getRefPos(); + public static EdgingRecordAndOffset createBeginRecord(SAMRecord record, int offset, int length, int refPos) { return new StartEdgingRecordAndOffset(record, offset, length, refPos); } diff --git a/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java index 8993e417a..372a590d3 100644 --- a/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java +++ b/src/test/java/htsjdk/samtools/util/AbstractRecordAndOffsetTest.java @@ -53,12 +53,11 @@ public void setUp(){ @Test public void testConstructor(){ - AbstractRecordAndOffset abstractRecordAndOffset = new AbstractRecordAndOffset(record, 0, 10, 3); + AbstractRecordAndOffset abstractRecordAndOffset = new AbstractRecordAndOffset(record, 0); assertArrayEquals(qualities, abstractRecordAndOffset.getBaseQualities()); assertArrayEquals(bases, abstractRecordAndOffset.getRecord().getReadBases()); assertEquals('A', abstractRecordAndOffset.getReadBase()); assertEquals(30, abstractRecordAndOffset.getBaseQuality()); assertEquals(0, abstractRecordAndOffset.getOffset()); - assertEquals(-1, abstractRecordAndOffset.getRefPos()); } } From 8c090cea90c414338fc979741097ff1bdd63e3eb Mon Sep 17 00:00:00 2001 From: Steve Huang Date: Sat, 15 Apr 2017 22:57:26 -0400 Subject: [PATCH 095/137] check for negative length in CigarElement ctor (#839) * added check for negative length in CigarElement ctor * added test --- src/main/java/htsjdk/samtools/CigarElement.java | 1 + .../java/htsjdk/samtools/util/CigarElementUnitTest.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java diff --git a/src/main/java/htsjdk/samtools/CigarElement.java b/src/main/java/htsjdk/samtools/CigarElement.java index c645e6cc2..016956c56 100644 --- a/src/main/java/htsjdk/samtools/CigarElement.java +++ b/src/main/java/htsjdk/samtools/CigarElement.java @@ -36,6 +36,7 @@ private final CigarOperator operator; public CigarElement(final int length, final CigarOperator operator) { + if (length < 0) throw new IllegalArgumentException(String.format("Cigar element being constructed with negative length: %d and operation: %s" , length, operator.name())); this.length = length; this.operator = operator; } diff --git a/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java b/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java new file mode 100644 index 000000000..54cfdedfc --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java @@ -0,0 +1,14 @@ +package htsjdk.samtools.util; + + +import htsjdk.samtools.CigarElement; +import htsjdk.samtools.CigarOperator; +import org.testng.annotations.Test; + +public class CigarElementUnitTest { + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeLengthCheck(){ + final CigarElement element = new CigarElement(-1, CigarOperator.M); + } +} From a44a5cd90e6271060d2c0b65af48cdffc6d15bb0 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Sun, 16 Apr 2017 14:08:28 -0400 Subject: [PATCH 096/137] Expose a couple of protected methods and replace hard coded strings with an enum. (#854) --- .../java/htsjdk/samtools/fastq/FastqReader.java | 48 ++++--- .../htsjdk/samtools/fastq/FastqRecordTest.java | 13 ++ .../htsjdk/samtools/fastq/FastqWriterTest.java | 74 ---------- src/test/scala/htsjdk/UnitSpec.scala | 21 ++- .../samtools/fastq/FastqReaderWriterTest.scala | 153 +++++++++++++++++++++ 5 files changed, 217 insertions(+), 92 deletions(-) delete mode 100644 src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java create mode 100644 src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala diff --git a/src/main/java/htsjdk/samtools/fastq/FastqReader.java b/src/main/java/htsjdk/samtools/fastq/FastqReader.java index 7988712f3..d5d8f1889 100755 --- a/src/main/java/htsjdk/samtools/fastq/FastqReader.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqReader.java @@ -41,6 +41,22 @@ * directly. It is provided so that this class can be used in Java for-each loop. */ public class FastqReader implements Iterator, Iterable, Closeable { + /** Enum of the types of lines we see in Fastq. */ + protected enum LineType { + SequenceHeader("Sequence Header"), + SequenceLine("Sequence Line"), + QualityHeader("Quality Header"), + QualityLine("Quality Line"); + + private String printable; + + LineType(String printable) { + this.printable = printable; + } + + @Override public String toString() { return this.printable; } + } + final private File fastqFile; final private BufferedReader reader; private FastqRecord nextRecord; @@ -58,10 +74,7 @@ public FastqReader(final File file) { * @param skipBlankLines should we skip blank lines ? */ public FastqReader(final File file, final boolean skipBlankLines) { - this.skipBlankLines=skipBlankLines; - fastqFile = file; - reader = IOUtil.openFileForBufferedReading(fastqFile); - nextRecord = readNextRecord(); + this(file, IOUtil.openFileForBufferedReading(file), skipBlankLines); } public FastqReader(final BufferedReader reader) { @@ -87,7 +100,6 @@ public FastqReader(final File file, final BufferedReader reader) { private FastqRecord readNextRecord() { try { - // Read sequence header final String seqHeader = readLineConditionallySkippingBlanks(); if (seqHeader == null) return null ; @@ -95,23 +107,23 @@ private FastqRecord readNextRecord() { throw new SAMException(error("Missing sequence header")); } if (!seqHeader.startsWith(FastqConstants.SEQUENCE_HEADER)) { - throw new SAMException(error("Sequence header must start with "+ FastqConstants.SEQUENCE_HEADER+": "+seqHeader)); + throw new SAMException(error("Sequence header must start with " + FastqConstants.SEQUENCE_HEADER + ": " + seqHeader)); } // Read sequence line final String seqLine = readLineConditionallySkippingBlanks(); - checkLine(seqLine,"sequence line"); + checkLine(seqLine, LineType.SequenceLine); // Read quality header final String qualHeader = readLineConditionallySkippingBlanks(); - checkLine(qualHeader,"quality header"); + checkLine(qualHeader, LineType.QualityHeader); if (!qualHeader.startsWith(FastqConstants.QUALITY_HEADER)) { - throw new SAMException(error("Quality header must start with "+ FastqConstants.QUALITY_HEADER+": "+qualHeader)); + throw new SAMException(error("Quality header must start with " + FastqConstants.QUALITY_HEADER + ": "+ qualHeader)); } // Read quality line final String qualLine = readLineConditionallySkippingBlanks(); - checkLine(qualLine,"quality line"); + checkLine(qualLine, LineType.QualityLine); // Check sequence and quality lines are same length if (seqLine.length() != qualLine.length()) { @@ -165,21 +177,23 @@ public void close() { try { reader.close(); } catch (IOException e) { - throw new SAMException("IO problem in fastq file "+getAbsolutePath(), e); + throw new SAMException("IO problem in fastq file " + getAbsolutePath(), e); } } - private void checkLine(final String line, final String kind) { + /** Checks that the line is neither null (representing EOF) or empty (blank line in file). */ + protected void checkLine(final String line, final LineType kind) { if (line == null) { - throw new SAMException(error("File is too short - missing "+kind+" line")); + throw new SAMException(error("File is too short - missing " + kind)); } if (StringUtil.isBlank(line)) { - throw new SAMException(error("Missing "+kind)); + throw new SAMException(error("Missing " + kind)); } } - private String error(final String msg) { - return msg + " at line "+line+" in fastq "+getAbsolutePath(); + /** Generates an error message with line number information. */ + protected String error(final String msg) { + return msg + " at line " + line + " in fastq " + getAbsolutePath(); } private String getAbsolutePath() { @@ -198,6 +212,6 @@ private String readLineConditionallySkippingBlanks() throws IOException { @Override public String toString() { - return "FastqReader["+(this.fastqFile == null?"":this.fastqFile)+ " Line:"+getLineNumber()+"]"; + return "FastqReader[" + (this.fastqFile == null ? "" : this.fastqFile) + " Line:" + getLineNumber() + "]"; } } diff --git a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java index 5ace9ffc3..9a47a8688 100644 --- a/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java +++ b/src/test/java/htsjdk/samtools/fastq/FastqRecordTest.java @@ -1,9 +1,12 @@ package htsjdk.samtools.fastq; import htsjdk.HtsjdkTest; +import htsjdk.samtools.util.TestUtil; import org.testng.Assert; import org.testng.annotations.Test; +import java.util.ArrayList; + public final class FastqRecordTest extends HtsjdkTest { @Test @@ -207,4 +210,14 @@ public void testNotEqualLengths() { new FastqRecord("header", seqLine1, "qualHeaderPrefix", qualLine1); //Note: this does not blow up now but it will once we enforce that seqLine and qualLine be the same length } + + @Test + public void testFastqSerialize() throws Exception { + final ArrayList records = new ArrayList<>(); + records.add(new FastqRecord("q1", "ACGTACGT", "", "########")); + records.add(new FastqRecord("q2", "CCAGCGTAATA", "", "????????###")); + records.add(new FastqRecord("q3", "NNNNNNNNNNNN", "", "############")); + + Assert.assertEquals(TestUtil.serializeAndDeserialize(records),records); + } } diff --git a/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java b/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java deleted file mode 100644 index 22549e904..000000000 --- a/src/test/java/htsjdk/samtools/fastq/FastqWriterTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * The MIT License - * - * Pierre Lindenbaum PhD - * - * 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 htsjdk.samtools.fastq; - -import htsjdk.HtsjdkTest; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import htsjdk.samtools.util.TestUtil; - -import java.io.File; -import java.util.ArrayList; - -/** - * test fastq - */ -public class FastqWriterTest extends HtsjdkTest { - private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/util/QualityEncodingDetectorTest"); - - @DataProvider(name = "fastqsource") - public Object[][] createTestData() { - return new Object[][]{ - {"solexa_full_range_as_solexa.fastq"}, - {"5k-30BB2AAXX.3.aligned.sam.fastq"} - }; - } - - @Test(dataProvider = "fastqsource") - public void testReadReadWriteFastq(final String basename) throws Exception { - final File tmpFile = File.createTempFile("test.", ".fastq"); - tmpFile.deleteOnExit(); - final FastqReader fastqReader = new FastqReader(new File(TEST_DATA_DIR,basename)); - final FastqWriterFactory writerFactory = new FastqWriterFactory(); - final FastqWriter fastqWriter = writerFactory.newWriter(tmpFile); - for(final FastqRecord rec: fastqReader) fastqWriter.write(rec); - fastqWriter.close(); - fastqReader.close(); - } - - @Test(dataProvider = "fastqsource") - public void testFastqSerialize(final String basename) throws Exception { - //write - final ArrayList records = new ArrayList<>(); - final FastqReader fastqReader = new FastqReader(new File(TEST_DATA_DIR,basename)); - for(final FastqRecord rec: fastqReader) { - records.add(rec); - if(records.size()>100) break; - } - fastqReader.close(); - Assert.assertEquals(TestUtil.serializeAndDeserialize(records),records); - } -} diff --git a/src/test/scala/htsjdk/UnitSpec.scala b/src/test/scala/htsjdk/UnitSpec.scala index db533a12e..a2995d56c 100644 --- a/src/test/scala/htsjdk/UnitSpec.scala +++ b/src/test/scala/htsjdk/UnitSpec.scala @@ -1,6 +1,25 @@ package htsjdk +import java.nio.file.{Files, Path} + import org.scalatest.{FlatSpec, Matchers} /** Base class for all Scala tests. */ -class UnitSpec extends FlatSpec with Matchers +class UnitSpec extends FlatSpec with Matchers { + /** Make a temporary file that will get cleaned up at the end of testing. */ + protected def makeTempFile(prefix: String, suffix: String): Path = { + val path = Files.createTempFile(prefix, suffix) + path.toFile.deleteOnExit() + path + } + + /** Implicit conversion from Java to Scala iterator. */ + implicit def javaIteratorAsScalaIterator[A](iter: java.util.Iterator[A]): Iterator[A] = { + scala.collection.JavaConverters.asScalaIterator(iter) + } + + /** Implicit conversion from Java to Scala iterable. */ + implicit def javaIterableAsScalaIterable[A](iterable: java.lang.Iterable[A]): Iterable[A] = { + scala.collection.JavaConverters.iterableAsScalaIterable(iterable) + } +} diff --git a/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala b/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala new file mode 100644 index 000000000..60e08efbd --- /dev/null +++ b/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala @@ -0,0 +1,153 @@ +package htsjdk.samtools.fastq + +import java.io.{BufferedReader, File, StringReader} + +import htsjdk.UnitSpec +import htsjdk.samtools.SAMUtils +import htsjdk.samtools.util.IOUtil + +import scala.util.Random + +class FastqReaderWriterTest extends UnitSpec { + private val rng = new Random() + private val Bases = Array('A', 'C', 'G', 'T') + + /** Generates a random string of bases of the desired length. */ + def bases(length: Int): String = { + val chs = new Array[Char](length) + chs.indices.foreach(i => chs(i) = Bases(rng.nextInt(Bases.length))) + new String(chs) + } + + /** Generates a FastqRecord with random bases at a given length. */ + def fq(name: String, length: Int, qual: Int = 30): FastqRecord = { + new FastqRecord(name, bases(length), "", SAMUtils.phredToFastq(qual).toString * length) + } + + "FastqWriter" should "write four lines per record to file" in { + val path = makeTempFile("test.", ".fastq") + val out = new FastqWriterFactory().newWriter(path.toFile) + val recs = Seq(fq("q1", 50), fq("q2", 48), fq("q3", 55)) + val Seq(q1, q2, q3) = recs + + recs.foreach(rec => out.write(rec)) + out.close() + + val lines = IOUtil.slurpLines(path.toFile) + lines should have size 12 + + lines.get(0) shouldBe "@q1" + lines.get(1) shouldBe q1.getReadString + lines.get(4) shouldBe "@q2" + lines.get(5) shouldBe q2.getReadString + lines.get(8) shouldBe "@q3" + lines.get(9) shouldBe q3.getReadString + } + + it should "write a record with only a single base" in { + val path = makeTempFile("test.", ".fastq") + val out = new FastqWriterFactory().newWriter(path.toFile) + out.write(fq("q1", 1)) + out.close() + val lines = IOUtil.slurpLines(path.toFile) + lines.get(1) should have length 1 + lines.get(3) should have length 1 + } + + it should "write a record with zero-length bases and quals" in { + val path = makeTempFile("test.", ".fastq") + val out = new FastqWriterFactory().newWriter(path.toFile) + out.write(fq("q1", 0)) + out.close() + val lines = IOUtil.slurpLines(path.toFile) + lines.get(1) should have length 0 + lines.get(3) should have length 0 + } + + + "FastqReader" should "read back a fastq file written by FastqWriter" in { + val path = makeTempFile("test.", ".fastq") + val out = new FastqWriterFactory().newWriter(path.toFile) + val recs = Seq(fq("q1", 50), fq("q2", 100), fq("q3", 150)) + recs.foreach(rec => out.write(rec)) + out.close() + + val in = new FastqReader(path.toFile) + val recs2 = in.iterator().toList + in.close() + recs2 should contain theSameElementsInOrderAs recs + } + + it should "throw an exception if the input fastq is garbled" in { + val fastq = + """ + |@q1 + |AACCGGTT + |+ + |######## + |@q2 + |ACGT + |#### + """.stripMargin.trim + + val in = new FastqReader(null, new BufferedReader(new StringReader(fastq))) + an[Exception] shouldBe thrownBy { in.next() } + } + + it should "throw an exception if the input file doesn't exist" in { + an[Exception] shouldBe thrownBy { new FastqReader(new File("/some/path/that/shouldnt/exist.fq"))} + } + + it should "read an empty file just fine" in { + val path = makeTempFile("empty.", ".fastq") + val in = new FastqReader(path.toFile) + while (in.hasNext) in.next() + an[Exception] shouldBe thrownBy { in.next() } + in.close() + } + + it should "fail on a truncated file" in { + val fastq = + """ + |@q1 + |AACCGGTT + |+ + |######## + """.stripMargin.trim + + Range.inclusive(1, 3).foreach { n => + val text = fastq.lines.take(n).mkString("\n") + val reader = new BufferedReader(new StringReader(text)) + an[Exception] shouldBe thrownBy { new FastqReader(null, reader).iterator().toSeq } + } + } + + it should "fail if the seq and qual lines are different lengths" in { + val fastq = + """ + |@q1 + |AACC + |+ + |######## + """.stripMargin.trim + + val reader = new BufferedReader(new StringReader(fastq)) + an[Exception] shouldBe thrownBy { new FastqReader(null, reader).iterator().toSeq } + } + + it should "fail if either header line is empty" in { + val fastq = + """ + |@q1 + |AACC + |+q1 + |######## + """.stripMargin.trim + + val noSeqHeader = new BufferedReader(new StringReader(fastq.replace("@q1", ""))) + val noQualHeader = new BufferedReader(new StringReader(fastq.replace("+q1", ""))) + an[Exception] shouldBe thrownBy { new FastqReader(noSeqHeader).iterator().toSeq } + an[Exception] shouldBe thrownBy { new FastqReader(noQualHeader).iterator().toSeq } + } + +} From 4fa89b16b8a836c8e3c52c24cfa07ddd55e78776 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Tue, 18 Apr 2017 14:38:58 -0400 Subject: [PATCH 097/137] =?UTF-8?q?Adding=20a=20pre-test=20check=20that=20?= =?UTF-8?q?looks=20for=20java=20files=20in=20the=20test/scala=20d=E2=80=A6?= =?UTF-8?q?=20(#857)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding a pre-test check that looks for java files in the test/scala directory and for scala files anywhere else (and fails if it finds either of these) --- build.gradle | 7 ++++++- scripts/checkScalaAndJavaFiles.sh | 17 +++++++++++++++++ .../scala/htsjdk/samtools/util/StringUtilTest.scala | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100755 scripts/checkScalaAndJavaFiles.sh diff --git a/build.gradle b/build.gradle index 9760a79fa..a60afe5f5 100644 --- a/build.gradle +++ b/build.gradle @@ -81,6 +81,11 @@ tasks.withType(Test) { task -> task.jvmArgs '-Djava.awt.headless=true' //this prevents awt from displaying a java icon while the tests are running } +task findScalaAndJavaTypes(type: Exec) { + description = "Check that Scala files only exist in the scala test dir and that java files do not reside in the scala test dir." + commandLine './scripts/checkScalaAndJavaFiles.sh' +} + test { description = "Runs the unit tests other than the SRA tests" @@ -98,7 +103,7 @@ test { if (System.env.CI == "false") exclude "sra" if (!OperatingSystem.current().isUnix()) exclude "unix" } -} +} dependsOn findScalaAndJavaTypes task testSRA(type: Test) { description = "Run the SRA tests" diff --git a/scripts/checkScalaAndJavaFiles.sh b/scripts/checkScalaAndJavaFiles.sh new file mode 100755 index 000000000..adadfd31c --- /dev/null +++ b/scripts/checkScalaAndJavaFiles.sh @@ -0,0 +1,17 @@ +#/bin/bash + +# Check that Scala files only exist in the scala test dir and +# that java files do not reside in the scala test dir + +if `find src | grep -v '^src/test/scala' | grep -q '\.scala$' ` ; then + echo 'Found scala file(s) outside of scala test directory'; + find src | grep -v '^src/test/scala' | grep '\.scala$' + exit 1; +fi + +if `find src/test/scala | grep -q '\.java$' ` ; then + echo 'Found java file(s) in scala test directory'; + find src/test/scala | grep '\.java$' + exit 1; +fi + diff --git a/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala b/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala index 6962e3674..35957d681 100644 --- a/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala +++ b/src/test/scala/htsjdk/samtools/util/StringUtilTest.scala @@ -106,7 +106,7 @@ class StringUtilTest extends UnitSpec { StringUtil.assertCharactersNotInString("HelloWorld", ' ', '!', '_') } - val textForWrapping = + val textForWrapping: String = """This is a little bit |of text with nice short |lines. From 78da17516d88e75b6874f69e5542f3625501bd98 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Tue, 18 Apr 2017 14:57:10 -0400 Subject: [PATCH 098/137] removing scripts/release_picard.sh (#858) deleting scripts/release_picard.sh since this script is no longer used --- scripts/release_picard.sh | 152 ---------------------------------------------- 1 file changed, 152 deletions(-) delete mode 100755 scripts/release_picard.sh diff --git a/scripts/release_picard.sh b/scripts/release_picard.sh deleted file mode 100755 index 732234ab1..000000000 --- a/scripts/release_picard.sh +++ /dev/null @@ -1,152 +0,0 @@ -#! /bin/bash - -# The MIT License -# -# Copyright (c) $today.year The Broad Institute -# -# 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 - -PROGNAME=`basename $0` -USERNAME=alecw - -function usage () { - echo "USAGE: $PROGNAME " >&2 - echo "Tags Github Picard source, checks out and builds sources, uploads build results to Sourceforge.">&2 - echo "-t Build in . Default: $TMPDIR." >&2 - echo "-u Sourceforge username. Default: $USERNAME." >&2 -} - -function tag_exists() { - git tag | grep -q "$1$" - if test $? = 0 - then return 0 - else return 1 - fi -} - -function remote_does_not_exist() { - git ls-remote $1 2>/dev/null 1>/dev/null - if test $? = 0 - then return 1 - else return 0 - fi -} - -function remote_tag_does_not_exist() { - git ls-remote --tags $2 | grep -q "$1$"; - if test $? = 0 - then return 0 - else return 1 - fi -} - -set -e - -while getopts "ht:u:" options; do - case $options in - u ) USERNAME=$OPTARG;; - t ) TMPDIR=$OPTARG;; - h ) usage;; - \? ) usage - exit 1;; - * ) usage - exit 1;; - - esac -done -shift $(($OPTIND - 1)) - -if (( $# != 1 )) - then echo "ERROR: Incorrect number of arguments." >&2 - usage - exit 1 -fi - -if [[ x"$EDITOR" == x ]] -then echo "EDITOR environment variable must be set." >&2 - exit 1 -fi - -# Require actual Java 1.6. This is not necessary for compiling, because can run 1.7 with -target 1.6, -# but this is necessary in order to force unit tests to run with 1.6. -(echo $JAVA_HOME | fgrep -q 1.6 ) || { echo "JAVA_HOME $JAVA_HOME is not 1.6" ; exit 1; } -java_version=`java -version 2>&1 | fgrep -i version` -(echo $java_version | fgrep -q 1.6. ) || { echo "java -version: $java_version is not 1.6"; exit 1; } - -GITROOT=git@github.com:samtools/htsjdk.git -REMOTE=origin - -RELEASE_ID=$1 - -# Since releases are lexically sorted, need to filter in order to have 1.1xx be at the bottom. -PREV_RELEASE_ID=`git ls-remote --tags | grep -v "{}$" | awk '{print $2}' | sed -e "s_.*/__g" | egrep '[.]\d\d\d' | tail -1` - -if [[ -e $TMPDIR/htsjdk ]] -then echo "$TMPDIR/htsjdk already exists. Please remove or specify a different TMPDIR." >&2 - exit 1 -fi -cd $TMPDIR - -# clone -git clone $GITROOT htsjdk -cd htsjdk -ant clean # Shouldn't be necessary, but no harm - -# tag must not exist -if tag_exists $RELEASE_ID -then echo "ERROR: Tag $RELEASE_ID locally already exists" - exit 1 -fi - -# remote must exist -if remote_does_not_exist $REMOTE -then echo "ERROR: Remote $REMOTE does not exist" - exit 1 -fi - -# tag at remote must not exist -if remote_tag_does_not_exist $RELEASE_ID $REMOTE -then echo "ERROR: Tag $RELEASE_ID at remote $REMOTE already exists" - exit 1 -fi - -# tag the branch locally then push to remote -echo Tagging master as $tag and pushing the tag to $remote -# NB: we could use annotated tags in the future to store release notes, etc. -git tag $tag -git push $remote $tag # TODO: should we check this return value in case someone made a tag since we last checked? - -ant -lib lib/ant test - -ant -lib lib/ant clean all javadoc - -mkdir -p deploy/picard-tools/$RELEASE_ID - -mkdir -p deploy/htsjdk/$RELEASE_ID -cp dist/htsjdk-$RELEASE_ID.jar deploy/htsjdk/$RELEASE_ID/ - -# Make all files to be pushed to Sourceforge writable by group so that another Picard admin can overwrite them. - -chmod -R gu+rw javadoc deploy dist - -find javadoc deploy dist -type d -exec chmod g+s '{}' ';' - -scp -p -r javadoc $USERNAME,picard@web.sourceforge.net:htdocs - -cd deploy -scp -p -r htsjdk/$RELEASE_ID $USERNAME,picard@web.sourceforge.net:/home/frs/project/p/pi/picard/htsjdk/ From da2b76b95fa806c4d19a28ff47e879b0432d22dd Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Tue, 18 Apr 2017 17:26:59 -0400 Subject: [PATCH 099/137] Added the ability to query the length of the line terminator to AsciiLineReader. (#843) --- .../htsjdk/tribble/readers/AsciiLineReader.java | 20 ++++++++++++-- .../tribble/readers/AsciiLineReaderTest.java | 32 ++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java index ad66a17c7..39c7d409c 100644 --- a/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java +++ b/src/main/java/htsjdk/tribble/readers/AsciiLineReader.java @@ -40,8 +40,9 @@ private static final byte LINEFEED = (byte) ('\n' & 0xff); private static final byte CARRIAGE_RETURN = (byte) ('\r' & 0xff); - PositionalBufferedStream is; - char[] lineBuffer; + private final PositionalBufferedStream is; + private char[] lineBuffer; + private int lineTerminatorLength = -1; public AsciiLineReader(final InputStream is){ this(new PositionalBufferedStream(is)); @@ -65,6 +66,16 @@ public long getPosition(){ return is.getPosition(); } + /** Returns the length of the line terminator read after the last read line. Returns either: + * -1 if no line has been read + * 0 after the last line if the last line in the file had no CR or LF line ending + * 1 if the line ended with CR or LF + * 2 if the line ended with CR and LF + */ + public int getLineTerminatorLength() { + return this.lineTerminatorLength; + } + /** * Read a line of text. A line is considered to be terminated by any one * of a line feed ('\n'), a carriage return ('\r'), or a carriage return @@ -83,6 +94,7 @@ public final String readLine(final PositionalBufferedStream stream) throws IOExc if (b == -1) { // eof reached. Return the last line, or null if this is a new line if (linePosition > 0) { + this.lineTerminatorLength = 0; return new String(lineBuffer, 0, linePosition); } else { return null; @@ -93,6 +105,10 @@ public final String readLine(final PositionalBufferedStream stream) throws IOExc if (c == LINEFEED || c == CARRIAGE_RETURN) { if (c == CARRIAGE_RETURN && stream.peek() == LINEFEED) { stream.read(); // <= skip the trailing \n in case of \r\n termination + this.lineTerminatorLength = 2; + } + else { + this.lineTerminatorLength = 1; } return new String(lineBuffer, 0, linePosition); diff --git a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java index b4aa6ba2c..b0a8de371 100644 --- a/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java +++ b/src/test/java/htsjdk/tribble/readers/AsciiLineReaderTest.java @@ -2,10 +2,10 @@ import htsjdk.HtsjdkTest; import htsjdk.tribble.TestUtils; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; +import org.testng.Assert; import org.testng.annotations.Test; +import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.InputStream; @@ -40,4 +40,32 @@ public void testReadLines() throws Exception { assertEquals(expectedNumber, actualLines); } + + @Test public void voidTestLineEndingLength() throws Exception { + final String input = "Hello\nThis\rIs A Silly Test\r\nSo There"; + final InputStream is = new ByteArrayInputStream(input.getBytes()); + final AsciiLineReader in = new AsciiLineReader(is); + + Assert.assertEquals(in.getLineTerminatorLength(), -1); + Assert.assertEquals(in.readLine(), "Hello"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "This"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "Is A Silly Test"); + Assert.assertEquals(in.getLineTerminatorLength(), 2); + Assert.assertEquals(in.readLine(), "So There"); + Assert.assertEquals(in.getLineTerminatorLength(), 0); + } + + @Test public void voidTestLineEndingLengthAtEof() throws Exception { + final String input = "Hello\nWorld\r\n"; + final InputStream is = new ByteArrayInputStream(input.getBytes()); + final AsciiLineReader in = new AsciiLineReader(is); + + Assert.assertEquals(in.getLineTerminatorLength(), -1); + Assert.assertEquals(in.readLine(), "Hello"); + Assert.assertEquals(in.getLineTerminatorLength(), 1); + Assert.assertEquals(in.readLine(), "World"); + Assert.assertEquals(in.getLineTerminatorLength(), 2); + } } From 6fb6cca3be85cc0551ab1ce0ba367313c7320fe0 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Fri, 14 Apr 2017 22:12:06 -0400 Subject: [PATCH 100/137] Count the number of warnings and errors during SAM file validation --- .../java/htsjdk/samtools/SamFileValidator.java | 36 +++++++++++++++++++++- .../java/htsjdk/samtools/ValidateSamFileTest.java | 12 +++++--- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index 5f82bd194..d0b745e7f 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -96,6 +96,8 @@ private boolean bisulfiteSequenced; private IndexValidationStringency indexValidationStringency; private boolean sequenceDictionaryEmptyAndNoWarningEmitted; + private int numWarnings; + private int numErrors; private final int maxTempFiles; @@ -111,6 +113,8 @@ public SamFileValidator(final PrintWriter out, final int maxTempFiles) { this.ignoreWarnings = false; this.bisulfiteSequenced = false; this.sequenceDictionaryEmptyAndNoWarningEmitted = false; + this.numWarnings = 0; + this.numErrors = 0; } Histogram getErrorsByType() { @@ -566,11 +570,41 @@ private void validateHeader(final SAMFileHeader fileHeader) { } } + /** + * Number of warnings during SAM file validation + * + * @return number of warnings + */ + public int getNumWarnings() { + return this.numWarnings; + } + + /** + * Number of errors during SAM file validation + * + * @return number of errors + */ + public int getNumErrors() { + return this.numErrors; + } + private void addError(final SAMValidationError error) { // Just ignore an error if it's of a type we're not interested in if (this.errorsToIgnore.contains(error.getType())) return; - if (this.ignoreWarnings && error.getType().severity == SAMValidationError.Severity.WARNING) return; + switch (error.getType().severity) { + case WARNING: + if ( this.ignoreWarnings ) { + return; + } + this.numWarnings++; + break; + case ERROR: + this.numErrors++; + break; + default: + throw new SAMException("Unknown SAM validation error severity: " + error.getType().severity); + } this.errorsByType.increment(error.getType()); if (verbose) { diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index f227332d2..16bd6e1ce 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -119,7 +119,9 @@ public void testVerbose() throws IOException { validator.validateSamFileVerbose(samBuilder.getSamReader(), null); final int lineCount = results.toString().split("\n").length; - Assert.assertEquals(lineCount, 11); + Assert.assertEquals(lineCount, 11); // 1 extra message added to indicate maximum number of errors + Assert.assertEquals(validator.getNumErrors(), 6); + Assert.assertEquals(validator.getNumWarnings(), 4); } @Test @@ -522,16 +524,18 @@ public void tagCorrectlyProcessTest(byte[] bytesFromFile, @DataProvider(name = "validateBamFileTerminationData") public Object[][] validateBamFileTerminationData() throws IOException { return new Object[][]{ - {getBrokenFile(TERMINATION_GZIP_BLOCK_SIZE), SAMValidationError.Type.BAM_FILE_MISSING_TERMINATOR_BLOCK}, - {getBrokenFile(RANDOM_NUMBER_TRUNC_BYTE), SAMValidationError.Type.TRUNCATED_FILE} + {getBrokenFile(TERMINATION_GZIP_BLOCK_SIZE), SAMValidationError.Type.BAM_FILE_MISSING_TERMINATOR_BLOCK, 1, 0}, + {getBrokenFile(RANDOM_NUMBER_TRUNC_BYTE), SAMValidationError.Type.TRUNCATED_FILE, 0, 1} }; } @Test(dataProvider = "validateBamFileTerminationData") - public void validateBamFileTerminationTest(File file, SAMValidationError.Type errorType) throws IOException { + public void validateBamFileTerminationTest(final File file, final SAMValidationError.Type errorType, final int numWarnings, final int numErrors) throws IOException { final SamFileValidator samFileValidator = new SamFileValidator(new PrintWriter(System.out), 8000); samFileValidator.validateBamFileTermination(file); Assert.assertEquals(samFileValidator.getErrorsByType().get(errorType).getValue(), 1.0); + Assert.assertEquals(samFileValidator.getNumWarnings(), numWarnings); + Assert.assertEquals(samFileValidator.getNumErrors(), numErrors); } private Histogram executeValidation(final SamReader samReader, final ReferenceSequenceFile reference, From ee21d8144dbc6d1fa08120d2316be4237f8bfa47 Mon Sep 17 00:00:00 2001 From: Nils Homer Date: Thu, 20 Apr 2017 14:14:31 -0400 Subject: [PATCH 101/137] Add fromPath to IntervalList. (#846) * Add fromPath to IntervalList. --- src/main/java/htsjdk/samtools/util/IOUtil.java | 7 ++++++- src/main/java/htsjdk/samtools/util/IntervalList.java | 14 ++++++++++++-- src/test/java/htsjdk/samtools/util/IntervalListTest.java | 12 ++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/IOUtil.java b/src/main/java/htsjdk/samtools/util/IOUtil.java index 0903a09f0..373bd5f24 100644 --- a/src/main/java/htsjdk/samtools/util/IOUtil.java +++ b/src/main/java/htsjdk/samtools/util/IOUtil.java @@ -778,9 +778,14 @@ public static File createTempDir(final String prefix, final String suffix) { /** Checks that a file exists and is readable, and then returns a buffered reader for it. */ public static BufferedReader openFileForBufferedReading(final File file) { - return new BufferedReader(new InputStreamReader(openFileForReading(file)), Defaults.NON_ZERO_BUFFER_SIZE); + return openFileForBufferedReading(file.toPath()); } + /** Checks that a path exists and is readable, and then returns a buffered reader for it. */ + public static BufferedReader openFileForBufferedReading(final Path path) { + return new BufferedReader(new InputStreamReader(openFileForReading(path)), Defaults.NON_ZERO_BUFFER_SIZE); + } + /** Takes a string and replaces any characters that are not safe for filenames with an underscore */ public static String makeFileNameSafe(final String str) { return str.trim().replaceAll("[\\s!\"#$%&'()*/:;<=>?@\\[\\]\\\\^`{|}~]", "_"); diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index f9bfe05ca..929671a84 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -34,6 +34,7 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -395,12 +396,21 @@ public static IntervalList copyOf(final IntervalList list){ * @return an IntervalList object that contains the headers and intervals from the file */ public static IntervalList fromFile(final File file) { - final BufferedReader reader= IOUtil.openFileForBufferedReading(file); + return fromPath(file.toPath()); + } + + /** + * Parses an interval list from a path. + * @param path the path containing the intervals + * @return an IntervalList object that contains the headers and intervals from the path + */ + public static IntervalList fromPath(final Path path) { + final BufferedReader reader = IOUtil.openFileForBufferedReading(path); final IntervalList list = fromReader(reader); try { reader.close(); } catch (final IOException e) { - throw new SAMException(String.format("Failed to close file %s after reading",file)); + throw new SAMException(String.format("Failed to close file %s after reading", path.toUri().toString())); } return list; diff --git a/src/test/java/htsjdk/samtools/util/IntervalListTest.java b/src/test/java/htsjdk/samtools/util/IntervalListTest.java index 613afde45..983820bbe 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalListTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalListTest.java @@ -26,7 +26,9 @@ import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; +import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.SamFileHeaderMerger; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; import org.testng.annotations.BeforeTest; @@ -34,6 +36,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -76,6 +79,15 @@ public IntervalListTest() { list3.add(new Interval("3", 50, 470)); } + @Test + public void testIntervalListFrom() { + final String testPath = "src/test/resources/htsjdk/samtools/intervallist/IntervalListFromVCFTestComp.interval_list"; + final IntervalList fromFileList = IntervalList.fromFile(new File(testPath)); + final IntervalList fromPathList = IntervalList.fromPath(Paths.get(testPath)); + fromFileList.getHeader().getSequenceDictionary().assertSameDictionary(fromPathList.getHeader().getSequenceDictionary()); + Assert.assertEquals(CollectionUtil.makeCollection(fromFileList.iterator()), CollectionUtil.makeCollection(fromPathList.iterator())); + } + @DataProvider(name = "intersectData") public Object[][] intersectData() { final IntervalList intersect123 = new IntervalList(fileHeader); From 854d7365539c63e39a4ada03ec7abbb408b12993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 25 Apr 2017 12:48:05 +0200 Subject: [PATCH 102/137] Add .fai index creation support (#842) --- .../samtools/reference/FastaSequenceIndex.java | 26 ++- .../reference/FastaSequenceIndexCreator.java | 180 +++++++++++++++++++++ .../reference/IndexedFastaSequenceFile.java | 8 +- .../reference/ReferenceSequenceFileFactory.java | 9 ++ .../reference/FastaSequenceIndexCreatorTest.java | 90 +++++++++++ .../samtools/reference/FastaSequenceIndexTest.java | 32 ++++ .../Homo_sapiens_assembly18.trimmed.fasta.gz | Bin 0 -> 335008 bytes .../Homo_sapiens_assembly18.trimmed.fasta.gz.fai | 2 + .../Homo_sapiens_assembly18.trimmed.fasta.gz.gzi | Bin 0 -> 264 bytes .../resources/htsjdk/samtools/reference/crlf.fasta | 4 + .../htsjdk/samtools/reference/crlf.fasta.fai | 2 + .../reference/header_with_white_space.fasta | 4 + .../reference/header_with_white_space.fasta.fai | 2 + 13 files changed, 352 insertions(+), 7 deletions(-) create mode 100644 src/main/java/htsjdk/samtools/reference/FastaSequenceIndexCreator.java create mode 100644 src/test/java/htsjdk/samtools/reference/FastaSequenceIndexCreatorTest.java create mode 100644 src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz create mode 100644 src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.fai create mode 100644 src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.gzi create mode 100644 src/test/resources/htsjdk/samtools/reference/crlf.fasta create mode 100644 src/test/resources/htsjdk/samtools/reference/crlf.fasta.fai create mode 100644 src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta create mode 100644 src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta.fai diff --git a/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java index 9ae9f1db9..3668fe671 100644 --- a/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java +++ b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndex.java @@ -31,6 +31,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.LinkedHashMap; @@ -39,7 +42,7 @@ import java.util.regex.MatchResult; /** - * Reads a fasta index file (.fai), as generated by `samtools faidx`. + * Reads/writes a fasta index file (.fai), as generated by `samtools faidx`. */ public class FastaSequenceIndex implements Iterable { /** @@ -159,6 +162,27 @@ private void parseIndexFile(Path indexFile) { } /** + * Writes this index to the specified path. + * + * @param indexFile index file to output the index in the .fai format + * + * @throws IOException if an IO error occurs. + */ + public void write(final Path indexFile) throws IOException { + try (final PrintStream writer = new PrintStream(Files.newOutputStream(indexFile))) { + sequenceEntries.values().forEach(se -> + writer.println(String.join("\t", + se.getContig(), + String.valueOf(se.getSize()), + String.valueOf(se.getLocation()), + String.valueOf(se.getBasesPerLine()), + String.valueOf(se.getBytesPerLine())) + ) + ); + } + } + + /** * Does the given contig name have a corresponding entry? * @param contigName The contig name for which to search. * @return True if contig name is present; false otherwise. diff --git a/src/main/java/htsjdk/samtools/reference/FastaSequenceIndexCreator.java b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndexCreator.java new file mode 100644 index 000000000..ee425ffd5 --- /dev/null +++ b/src/main/java/htsjdk/samtools/reference/FastaSequenceIndexCreator.java @@ -0,0 +1,180 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Daniel Gomez-Sanchez + * + * 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 htsjdk.samtools.reference; + +import htsjdk.samtools.SAMException; +import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.util.IOUtil; +import htsjdk.tribble.readers.AsciiLineReader; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Static methods to create an {@link FastaSequenceIndex}. + * + * @author Daniel Gomez-Sanchez (magicDGS) + */ +public final class FastaSequenceIndexCreator { + + // cannot be instantiated because it is an utility class + private FastaSequenceIndexCreator() {} + + /** + * Creates a FASTA .fai index for the provided FASTA. + * + * @param fastaFile the file to build the index from. + * @param overwrite if the .fai index already exists override it if {@code true}; otherwise, throws a {@link SAMException}. + * + * @throws SAMException if the fai file already exists or the file is malformed. + * @throws IOException if an IO error occurs. + */ + public static void create(final Path fastaFile, final boolean overwrite) throws IOException { + // get the index to write the file in + final Path indexFile = ReferenceSequenceFileFactory.getFastaIndexFileName(fastaFile); + if (!overwrite && Files.exists(indexFile)) { + // throw an exception if the file already exists + throw new SAMException("Index file " + indexFile + " already exists for " + fastaFile); + } + // build the index + final FastaSequenceIndex index = buildFromFasta(fastaFile); + index.write(indexFile); + } + + /** + * Builds a FastaSequenceIndex on the fly from a FASTA file. + * + * Note: this also alows to create an index for a compressed file, but does not generate the + * .gzi index required for use it with samtools. + * + * @param fastaFile the FASTA file. + * + * @return a fai index. + * + * @throws SAMException for formatting errors. + * @throws IOException if an IO error occurs. + */ + public static FastaSequenceIndex buildFromFasta(final Path fastaFile) throws IOException { + try(final AsciiLineReader in = new AsciiLineReader(IOUtil.openFileForReading(fastaFile))) { + + // sanity check reference format: + // 1. Non-empty file + // 2. Header name starts with > + String previous = in.readLine(); + if (previous == null) { + throw new SAMException("Cannot index empty file: " + fastaFile); + } else if (previous.charAt(0) != '>') { + throw new SAMException("Wrong sequence header: " + previous); + } + + // initialize the sequence index + int sequenceIndex = -1; + // the location should be kept before iterating over the rest of the lines + long location = in.getPosition(); + + // initialize an empty index and the entry builder to null + final FastaSequenceIndex index = new FastaSequenceIndex(); + FaiEntryBuilder entry = null; + + // read the lines two by two + for (String line = in.readLine(); previous != null; line = in.readLine()) { + // in this case, the previous line contains a header and the current line the first sequence + if (previous.charAt(0) == '>') { + // first entry should be skipped; otherwise it should be added to the index + if (entry != null) index.add(entry.build()); + // creates a new entry (and update sequence index) + entry = new FaiEntryBuilder(sequenceIndex++, previous, line, in.getLineTerminatorLength(), location); + } else if (line != null && line.charAt(0) == '>') { + // update the location, next iteration the sequence will be handled + location = in.getPosition(); + } else if (line != null && !line.isEmpty()) { + // update in case it is not a blank-line + entry.updateWithSequence(line, in.getLineTerminatorLength()); + } + // set the previous to the current line + previous = line; + } + // add the last entry + index.add(entry.build()); + + // and return the index + return index; + } + } + + // utility class for building the FastaSequenceIndexEntry + private static class FaiEntryBuilder { + private final int index; + private final String contig; + private final long location; + // the bytes per line is the bases per line plus the length of the end of the line + private final int basesPerLine; + private final int endOfLineLength; + + // the size is updated for each line in the input using updateWithSequence + private long size; + // flag to check if the supposedly last line was already reached + private boolean lessBasesFound; + + private FaiEntryBuilder(final int index, final String header, final String firstSequenceLine, final int endOfLineLength, final long location) { + if (header == null || header.charAt(0) != '>') { + throw new SAMException("Wrong sequence header: " + header); + } else if (firstSequenceLine == null) { + throw new SAMException("Empty sequences could not be indexed"); + } + this.index = index; + // parse the contig name (without the starting '>' and truncating white-spaces) + this.contig = SAMSequenceRecord.truncateSequenceName(header.substring(1).trim()); + this.location = location; + this.basesPerLine = firstSequenceLine.length(); + this.endOfLineLength = endOfLineLength; + this.size = firstSequenceLine.length(); + this.lessBasesFound = false; + } + + private void updateWithSequence(final String sequence, final int endOfLineLength) { + if (this.endOfLineLength != endOfLineLength) { + throw new SAMException(String.format("Different end of line for the same sequence was found.")); + } + if (sequence.length() > basesPerLine) { + throw new SAMException(String.format("Sequence line for {} was longer than the expected length ({}): {}", + contig, basesPerLine, sequence)); + } else if (sequence.length() < basesPerLine) { + if (lessBasesFound) { + throw new SAMException(String.format("Only last line could have less than {} bases for '{}' sequence, but at least two are different. Last sequence line: {}", + basesPerLine, contig, sequence)); + } + lessBasesFound = true; + } + // update size + this.size += sequence.length(); + } + + private FastaSequenceIndexEntry build() { + return new FastaSequenceIndexEntry(contig, location, size, basesPerLine, basesPerLine + endOfLineLength, index); + } + } +} diff --git a/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java b/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java index 5a8703381..5c318782e 100644 --- a/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java +++ b/src/main/java/htsjdk/samtools/reference/IndexedFastaSequenceFile.java @@ -136,18 +136,14 @@ public static boolean canCreateIndexedFastaReader(final File fastaFile) { } private static Path findFastaIndex(Path fastaFile) { - Path indexFile = getFastaIndexFileName(fastaFile); + Path indexFile = ReferenceSequenceFileFactory.getFastaIndexFileName(fastaFile); if (!Files.exists(indexFile)) return null; return indexFile; } - private static Path getFastaIndexFileName(Path fastaFile) { - return fastaFile.resolveSibling(fastaFile.getFileName() + ".fai"); - } - private static Path findRequiredFastaIndexFile(Path fastaFile) throws FileNotFoundException { Path ret = findFastaIndex(fastaFile); - if (ret == null) throw new FileNotFoundException(getFastaIndexFileName(fastaFile) + " not found."); + if (ret == null) throw new FileNotFoundException(ReferenceSequenceFileFactory.getFastaIndexFileName(fastaFile) + " not found."); return ret; } diff --git a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java index 2b0b1e7fc..654706819 100644 --- a/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java +++ b/src/main/java/htsjdk/samtools/reference/ReferenceSequenceFileFactory.java @@ -163,4 +163,13 @@ public static String getFastaExtension(final Path path) { .orElseGet(() -> {throw new IllegalArgumentException("File is not a supported reference file type: " + path.toAbsolutePath());}); } + /** + * Returns the index name for a FASTA file. + * + * @param fastaFile the reference sequence file path. + */ + public static Path getFastaIndexFileName(Path fastaFile) { + return fastaFile.resolveSibling(fastaFile.getFileName() + ".fai"); + } + } diff --git a/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexCreatorTest.java b/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexCreatorTest.java new file mode 100644 index 000000000..193770abd --- /dev/null +++ b/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexCreatorTest.java @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Daniel Gomez-Sanchez + * + * 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 htsjdk.samtools.reference; + +import htsjdk.HtsjdkTest; +import htsjdk.samtools.util.IOUtil; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.File; +import java.nio.file.Files; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Daniel Gomez-Sanchez (magicDGS) + */ +public class FastaSequenceIndexCreatorTest extends HtsjdkTest { + private static File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/reference"); + + + @DataProvider(name = "indexedSequences") + public Object[][] getIndexedSequences() { + return new Object[][]{ + {new File(TEST_DATA_DIR, "Homo_sapiens_assembly18.trimmed.fasta")}, + {new File(TEST_DATA_DIR, "Homo_sapiens_assembly18.trimmed.fasta.gz")}, + {new File(TEST_DATA_DIR, "header_with_white_space.fasta")}, + {new File(TEST_DATA_DIR, "crlf.fasta")} + }; + } + + @Test(dataProvider = "indexedSequences") + public void testBuildFromFasta(final File indexedFile) throws Exception { + final FastaSequenceIndex original = new FastaSequenceIndex(new File(indexedFile.getAbsolutePath() + ".fai")); + final FastaSequenceIndex build = FastaSequenceIndexCreator.buildFromFasta(indexedFile.toPath()); + Assert.assertEquals(original, build); + } + + @Test(dataProvider = "indexedSequences") + public void testCreate(final File indexedFile) throws Exception { + // copy the file to index + final File tempDir = IOUtil.createTempDir("FastaSequenceIndexCreatorTest", "testCreate"); + final File copied = new File(tempDir, indexedFile.getName()); + copied.deleteOnExit(); + Files.copy(indexedFile.toPath(), copied.toPath()); + + // create the index for the copied file + FastaSequenceIndexCreator.create(copied.toPath(), false); + + // test if the expected .fai and the created one are the same + final File expectedFai = new File(indexedFile.getAbsolutePath() + ".fai"); + final File createdFai = new File(copied.getAbsolutePath() + ".fai"); + + // read all the files and compare line by line + try(final Stream expected = Files.lines(expectedFai.toPath()); + final Stream created = Files.lines(createdFai.toPath())) { + final List expectedLines = expected.filter(String::isEmpty).collect(Collectors.toList()); + final List createdLines = created.filter(String::isEmpty).collect(Collectors.toList()); + Assert.assertEquals(expectedLines, createdLines); + } + + // load the tmp index and check that both are the same + Assert.assertEquals(new FastaSequenceIndex(createdFai), new FastaSequenceIndex(expectedFai)); + } + +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexTest.java b/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexTest.java index e24e28874..c6fa1384a 100644 --- a/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexTest.java +++ b/src/test/java/htsjdk/samtools/reference/FastaSequenceIndexTest.java @@ -30,9 +30,15 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileReader; +import java.nio.file.Files; import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Test the fasta sequence index reader. @@ -254,4 +260,30 @@ public void testSpecialCharacters(FastaSequenceIndex specialCharactersIndex) { Assert.assertEquals(ent.getBasesPerLine(),70,"Contig file:gi|17981852|ref|NC_001807.4| bases per line is not correct"); Assert.assertEquals(ent.getBytesPerLine(),71,"Contig file:gi|17981852|ref|NC_001807.4| bytes per line is not correct"); } + + @Test + public void testWrite() throws Exception { + // gets the original file and index + final File originalFile = new File(TEST_DATA_DIR, "testing.fai"); + final FastaSequenceIndex originalIndex = new FastaSequenceIndex(originalFile); + + // write the index to a temp file and test if files are the same + final File fileToWrite = File.createTempFile("testing.toWrite", "fai"); + fileToWrite.deleteOnExit(); + originalIndex.write(fileToWrite.toPath()); + + // read all the files and compare line by line + try(final Stream original = Files.lines(originalFile.toPath()); + final Stream written = Files.lines(fileToWrite.toPath())) { + final List originalLines = original.filter(s -> ! s.isEmpty()).collect(Collectors.toList()); + final List actualLines = written.filter(s -> !s.isEmpty()).collect(Collectors.toList()); + Assert.assertEquals(actualLines, originalLines); + } + + // load the tmp index and check that both are the same + final FastaSequenceIndex writtenIndex = new FastaSequenceIndex(fileToWrite); + Assert.assertEquals(writtenIndex, originalIndex); + } + + } diff --git a/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz b/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz new file mode 100644 index 0000000000000000000000000000000000000000..aa8ef591b0750b572d7255bfa43ba44504b9f1a7 GIT binary patch literal 335008 zcmbrE(_$rxwnSswwr$(&*tTtUY_8ZgI<{@ww(XpKzv0fO`h;1d24OT57|{RSF9eW? zCO=c=CYXY(WP_p&!lU$Uy^MPMVMx8>7qY1Y8H^Y^BE_2+%_&DZ2<_Lfa~)fSE5(Yw%gBM^U&n=-?zWT5We=;`M2O|%BV7KL-u6(@IMhcG)$FpjBVksEaqe@#(ASy(hd+Z(>vWCTH&k z)i^~Rw|ImR>-JwSe#W_YWBAJvCEW)o(I~R-UZ*!In>ml^&!5J`+Qh~adQ%=#vwqhJ zjVMK(sL3eEgYC{ufiB+5#uND4o4BheO)B2J@lzJt`AD#W+bMI=^S-8{2Qyu zB}VG*At!)>^!bbMXpOQ%&(XGs0Sd(}B%}$Mi$$_>Ff6+zK#==s*;TN*)>A)Ro7=ug z4>3Hj;}%zSBiH2Fi!4)4({$f;auNO%vNW8DVA?qkE({uCP9*z^VVLNe@v-dfY29?uTbSM>}{jy;Ca$D{G<`v&W{f0L@F1|PX zr}Oo)vgwCy;1qzOV52r2s*JUTyFCgi%Aj-gT#L4AG)mSkBIP)xB}Y|cGoj~8Au$VH zPkxxvNl|GHs@$-pe~)Ymg71V`m)|BbS_ss65(58s*W;)u^kT3!1e_+l@Pzg7I!wcD z^kZAOz}zoqRzY^KOHDoP`26BRb5XVZW`8Yi>LlelE@8{V{LjJi8bd*w_1b9_=0S6M zMkl7sbvqGg(vsTavqo9ilDi;sp%-R8+_dPd)2OV6yVvanQ7r3kY*Ul?;F@MsoL>yL zq$o>4J)3b*ZCE=@-AS?V6aB|2uNB?XE0>ki$*-@^ z$3}j^-#6cbtJ&UH{j%!WvX}4iQUx_-r;7Onzxbe(&|~a z%bhEiq|c>UkK5^k%hSPEX0Z(DTmg$3)ZZ~vI zMKNZbX}i&Z%|>X002m*yX&*hN=gbX2X6?1P*}pfdv%&rgUE=-iXCazB?pUd6%fUJh zWFT`*&H}gYRJNh~aIqVY9Fa0tW*0kBa(br7RpMSw!{5m)h6&4KH$oD!E!I>*-$I#EQ4z;!S{Mt2XF%1*&5f(A~=MA@*&_sDcXA$4ePl^6&v zaHs@zSyuFT4ttD&{S|XQlHH%Jlu0NeiJ6GLLly~+qXA9iGR>D>gpV}hZT`B-;6V?>%tVB*nFG4-P&s~|GP(+f zAwn~i%^ZwP?`%Ni^!3^#m|6#pV*=Jh&An)W`2{=W|FS@D+TelL36GeYci*oHko~PV zi{R8%Z-vG3jp%dT1l}CH9vs0sw=5*cb;F$?cL(cEPS=&pUn=Gf^T~Tgi!1W%Byb*3 z(u-CffCcUyHIK>JT#N_8sem?~!p}wnF)(HAs8*WnwC+(~u=ftd2D5LM% zCYeFN;Tc1M*W(IrR078Oy^dOIdg`Q5Z)STK7md4xG=XMxK08&nU3Fr|7m=sGkB<&D zSK%=!%~Qgu;leA@+w>ZUb5bh%2gplXFpe>=lz@M-ZVw-I{*mV484`KkT01D5-%4Mn|X)RHW-H65#I2sdH^y%*atZXzU z26;c3HI%G6y4$Nz`Qn1)r+vV^oXmvil=x~lAtPGf38Or4 z0;duN1WX6Fi}WWyX83%HI;txSc5$`R5&v~?7_2vg`n|n$iDWm{Loyvf>NbA$F*It~ zFb+B2swuc|Km!-zxO+eEQeL#tOuvE-LdN{&tl4Gg+ zaAR4mJ=eWnj`G}d1SNCu5r_CLl=PW)IMN`T0U+zOw%00ozrW3>t@4rtVm0lTrUM|a zNy`7P&)0~75pV+tYlibJ3kkb^$Dyxq8u{fy^<9|DFX7G*-9sImo&$eLcvZXS{N46E ztz?RERq8*7Z&2%=Uv!buuZ@&W|ASP`$gJAXOr9h_z-5~BVo2Djgt28S1lr?cFI4_I z8d6zPi*qq&)<(HqU!{m;wn%P_vh-; zBpV#xeY~-eElAmW%~}olG53M@V4UU9xkC}vSl|lAHW3)9aM<&&;*tV~wsRT3-`UGa z%lUY(yqkrNflt>r?uRdvn;4sLkV5DR%?TYlIt~hUI+PjXg%!>42R zvZ6r3z7M|lZbmc)Y%d-^D)a=xhzG|8@y!xBt@<1+gz0P@ZnfQmQ+c68vFw_y%`pl+4rpHA7AA=SDJS1ta2O10I;*wzrJh`!eP6uZW{5RToERIp{#_Nd{ zz4l3pAxsn2ZUMaFKkaO#h~2e=dPbhQP0wB(lMoQNq3=R)95++D;$HP6Kj}_vP6h=Y zk-G(1wF;^DMq>b8LP38)>XX3YYbahY$(^(<Dn_@)tdlsXrCkhayC?c~vCi<*I#*p6rd9VxpvsvQ?OKI;1Zm`<30 zh^V->l6O7-;lNy`o{XdcSj@~amGyl%E{T{#j-POuf@&Kp+tAaH)%d@STt$9QDTib_EL? zfYxE)-RTn`UAs62Tvpp{D?a&!(O;^ow-rd1l8sEyHd1V2&r_8y=U+3z>ZwAS%u^l% zYJ>C?mulNpL$u_-qox8fZ=ayt?-Mp~z2NV4`;jiBW_J}9$b*Hro3=FwWXowHIf{^y za?)iSvdqJ8=@m0P7cP%TJ#Liq`*Bz1YGVg+h3XzxrsngFBavJ`0`ZaeQqYuK)(&#caUaMF>#~T?fAUSKiyH%OpLe$Nl7x6J%yYd2rRuHud^IrG#%ak zF$ocZ&Q4NElc;Tl(qCASN~_o$eh!7kOgJD+qt%t)WG%k$QNVeVvLy}S@8~Jnf$hLc z7W$ljD3$pyGxn7<=^VCl-qmC@4IW!U?XK#M-WMZOP61g3ROlHWFN-_uc!)3pUzePyNA>9PsJf4KD9># zX*hu_pY6F=)j}ShMFJwBcq;{^@qXan6@Z4DucpAH+WCO~{;`|O4K~-~4@IhLgb6Vg zYU#?!1Ug?(uJIgd0#v@{Q)UZ4!xFWGl(SlW&Bg2xCVvj(b>1Iks^r3lNGbkTc*GWN z@L0U(g4RNVq0<-~wFSoz@{3*?<7HViKfz$4L!1Z|tyLE}Mm=5w8yXv6PS2)dwSU&0 z-3g;C0X{0)XpAm=bu8aDEDORfnaJisNYbRUZyV&g(=6~r~e zNPkO=87mDbHn3|c^m#`Tk#Fk|JpGKIqO}#GNo+P2O{<1;%cPvap}iB)u2Sfk6EF+f_RqR|9x-r|Vb4un6O4@ZZwVuUAL~de#2{N1H=Ra~MO;?GWmxAY z7cXg0i`~`il^ZF8Gb&p?cLtr;O(whPHj-_{NaJtou(%$n)P~fNKw6M?nTevnULE5#YC| z;YP>nFG^NuCZ{$;zPaW!sODNaeUGfLer=)Tu!(ccS@eyjR(PLak)$BjA-q_I(|RuK zjA=pAu|u;v23SJDETB2cp)oj~O#m2)fL4{Rzuzz1d`}78@|$}v`4*hjSZXSZ0GsYr z%S-3a{nZHFtlAw{#^sU}+Fu~{l6cw*yS}CbhDloEoM#IBB`)bNkN{@3Kh_HK9YX+8 z*7GGO+3ylI;~4T=?ad;*Pte_S^TsQBd9x1SV=d+Ja@RvtII=%V88j+grAub$Q6(Mr z)N36*?BA-_9?|_tWxER+LJ&Saho;otQTiAido#(VwmUZ-QrMiMCenPZ8Ecu6cSqhuZ#NYIR_ z@L8rUDA}It9waPLL#0_O19R<9HQ4h(=$7n7Qju$L5@lM|@Q~svCsl%Z@!>67&l(G+J!gZ%0Y zso_WyQ}TjcyxvqqQ>Lv^ZG1lb69cIYfc5#TMyH8a#S4G9WD3SE@^TAWr)y3npR|lO z_*C-|=A~z{jDqxhzYn#TcsEftNm|Kx;YzFLc_eR|_bU0{0FDp#F*Q)o$qXJ;0Ftck zh38l5RX8J~T~T?apn9)+5$@QuCFBqokwlTr2<+nTFcbnVg}9c&-w31zBGo$z!Sy6j zW?O_EamnXXuq1Nw49n8=cLK&P7@#glCcO+CoIC6`!L(y|9VRu>3z-HUy(!>$l(;YD&blnddjk#hp zAMJXIaux8kTWB>?e(?{xE5Ba`zpuY)Kcm0l27fVs^}amrcZdI9;Wr+05r;!N$$oQZ z1M`SuwS;~w!ys$ax_jF1e6Z7m>VC)z_tpPKjpJI& z&fNKC?RphwThjq_-?IqcgXjLxvgXZ8J@TiI62ew?_!Iud?-u)7QC&`U>-lcG&CjVt z-FF}5bdXrwW$kn0(e(7-vv{+xEt?lHh9eid2EPbXR!*^rO+P05Ja1l!gMpFu6aAm( zsT-Eq7WEDXPMfVaA+Oc_{jA5N09#PIV0*XK?Ve}oyEpY{Nh>`0wXuluOUEUihJwb6 z{-g+VVft>TZ4zu@3i-eHcIk|){yN_*iOGpEqcBm+14z~nj>X*f$0g^4TuVJv7UZ-Y zgl>o^RqJm_eNxfu2iF~-aXf)$Gf=gIf(9Dfra=49ThD-u85eysC2 zc-OyUl8rTSjgqft4V;TZI789HOL@-(QI@01!wYNjTp?uU{>N_f9Be#Djizz7C1_@t zm%Nc?qdg{sayTfX3p^M29>rPq4ChnapGczh3k*qN>8?CNX$9+fhZTp`fe@osT31t^MmLm zqM)l^IGGeXUJpjYd98f&zkJJ!>bMdnuC3;Lb@{L&M55rEBrh+%BIk8LsIg7!G37g( zchn&`=%_i>vqz0*&KO>m9Emtja<0YV!XY2rPX-n>Gp)DYXVB_w5SR`8x4b3_s$!H+ z@CR|#l|`bc^HVgf%XEA0j|+!>EG987BaN~%&w4U7EBB-K>wcb&?n#0! zyvKQNae9q8MslLA=YFm6BTo_A&{?N++mwXMVstjPoZ5>;G?cMtoQ*?zD(AiwS2k|U z?ql4Uj)mSiH-0M<@wJ{gC?&&Fh%Wlb;+VlafACJ+I!-Lb zUr95MP4WgoxVlsWX?*lU5U&RHWopoNsRcLOO0nujEoyDb{r>K?h+CnpB)uaR#4@H| z6v2QY9@pu9&N4+EczA$<3d+$sdxhfnBghBcHAKP66)M(M+5#e%`MIC9tBQCutN)bi zyF=rj2@@B*fnr_nRv4NBs#Y5|MSzEA{HEbuc)m~95h^r#>amm=soz0HrsaTGm@I^uS0_8v0Wq`o_ItlcJj16|9~d(vHLfHaI*ha6 zHjt%Or5+i=0LxhMxx$($gE}k~0&5cK$>0$0Ew3$=-sWuN#sCtZ4jSd)0!mxL(8xfG z8{v`+joUfWsn63Y8B9f__4e$=FC%XIT=RgMEHxl{VR8UurEGJEI{ft~a3fZtGDO9g zzzH+6`v=v!@NJ@aEeGP4OrOxFEma^*XBRhdX`y@VnRx=Afpj&O%H=EJ~+cLt@=d;7kL9V ze4t*5#7z_r?Yy)MWBr-evQI6i#32KVJc-x95s&D0Ivyp}QKE<@RN-T#o}q&J5QTVU zg!a5gs;WU0_5O@}llr6zcb~HXhAb@g%upuvCo%?J+c7_m1BsTp2eGXQ#4Fq}S`4(% z*B>b*FUPw#GZ(DwWQRmmbIZHLqLuxvfF2eBB0jrJ`%_PafeK8XEZ*AxZ*N0)g-#*M z6p27dDyf1*?xVUDM)oK%`oy$>S$4G1xNUPv3HjFx!j(F}ee>SYlLVgH7I`jAP4Cu6 zNmwS0`-u6&ONaR?dV_wVqw9V{Oo_|V$oQ)4AX~HFr&VfJ_$2R&&6<)f@d&+02kkkR zzITp4xwUG0uteam;O|#6qfWgT(JU(l9Y_ynpY12QVI<1nwcu(z%;j|fy-!I!|HSwK z;TYtSI8ksh>{ni+5rzd=#V1ozkoF(S4n#(wPazc8zfSX6DU|oRy3@_p~%c-%8)Sru=ii+kt4hR+cnfl>= zORWnLUOiE#1DbtEWTPd7`OQFY>&wcHoGCj@3&9i___OkL z_@q*Gt19EAr-Ru_TZD|p9|pTdpA3+L#No9jLEl(?Wu9D1#ncu3RV3K!sBe82K0$Y9 zOOA6crV-Tj)W2v=%9F|#&FG6$T3|1w=39f6@0#Mc@K1ziijXIk?Kx&t`85L=(D4j} zsZZkNP|2tXmhx&y;TiquQ>3kqdZY{jxt}DV;|#;tJUVnUmTysZ^aJd=+iy$A@f9Q{ z$<9n^3MruNgf#iq**d@*z;omy@je&)-fL4LHY1TXD&64-V4vgSH}BnG)-1Ygd?h3` zxSTWRB3hM`W&Y`SkS=GoW#nmq6TN$A&4ko9=696-q52PH9;-o3TYFU6uDritp27o%CRa6^ z4_IMM(~2~bg3KK2GFky6D#>J!Z_-4fzHoCofR4%KbjmeJ4+a6hnakXG4252~9vm4` zU+M4kB)FIzvV#UaRB>w&Dgv!M{3EWxj0rjsL`Aq+BoXNXzXR0FkoponBqHtwWZmIhz`-SiQ56Gy!@tQ2sW`Pz&uQ;@hNmbW!n-PF9H}GAYI4y zxfpD;LJgD0>IM^e+MJMb7x z1YcUXNUohw?3IuGXe40%E(1oNP7N=MYPHXKSVdGWm6M;g`B{t{?A#F)gi$#FsxOw< zjW|Mwyw#V@nzS6j?B-NQI;w4LoQz;xDX+yOClS2NI>&VNHTVeZ6N{~MXHVitc$UETQA}Zfx6AjMn~+@Vb8wQx^ct@2@oZmxBhQs{GY7GThiZ zQyn-sF35PJi}kSB5yEVnOi4B`8L|4sct08ElQ4dPN`AF7#5+x-OmQ_FnHXDr2_J^c zLpQDB8)12P$>CgvdENdS`!^l>%@QW(4y=VeBTsuC`tc%2@o3y3b^J z97mqJIgpwK4CFk~DBwE#OGO{B;xy%~MnYMwbo}ohBSd%{JTUcH{euSmspRcrnda~V zsZltFz*>~-cQ&5j+x17v{}!lGTYs;HT*v|MnfFWE?S_63c=*a1JeXW7jwi#gU?yA# zqa^6_TRIPYzBtcSX&VD+*D~KM&AMZ*m{Yfq_V2$1(+%CGvlbe0JqHOjrN4ormc99B zQqiKU>EJCctIXG~LJGwHUMQ;BuBe+Wcx~f%L=YNU4dgi(DB^Z32N{&J9m-Tnd>7%1 zJ92c~>G5qJiEUi|YD(}}xh9>nt0@UIZUTk)Q*C$at*g;fQ_l4GCu>fQ@LqlA=Hy+S ziW%U788BP7W2+f^2BVfET*j( zKlR%V4omqgT2d&KT&f+267qK(&NZ8Q4)NM)svHZ1PGeM#iu_B!8Crh5k?7MhdzXH+h2{zIwkmX%Mogl3;<0x~V$ z#@bf%Vm`|Wl$*>Oca1o8b?|b5c@5X<9N;GV%JeMDEZI1xZ>r<@k{dgfPW9(&p3c=@G`>dkJLNz6X6@GD^%M)l+fDA6?9I$L?(0lmd+(tspcKvGNom&t8 zo_?u-L|!L-A$}wc?%7)f3vQ-0K7%cW4i?{UtU#!vi)!8)(So3fS~24_4ardCYu!~Uo^WqY3+ow=FNabzz!$88vn%VYv+t|uFN%_)8Je^ zIrkZ{G0k~XgkL#QgrqB;N-5-iQ&E)xrbJONMI-%Bjl!@9Rwp2$prSaMft+Mt&Z|YM z)nOF4DNPCQxvNmGL6)$3Sv1t`hPl3Yqd+gX8rQojDQoTp2*%qZDS8oGe)T{(P7p4$ z^=+D>_8@)7IDt^+DO+e8rpw&wvRa5rSPxr%PB`hV^oGc2%-!)P#%u?dF2lx?v>zF7tYxc~NV&H zmY-Lxj3No;1pxW@Jm&SFbeK=U;yCu8tK*_6ILFXzRE%*0c7$0Xgp)5cz&|dda#W7! z5iWRS^WEZItz-oVjJdCxiTQDRf-Ag7L%RXVxa z_<`dgRKpx1gCBGn<4W9A>2GA);{rpxm(d-^<$NN<-QJ6tB@{0Td_qwl6R#ZVPx?(t z{h;&I6LYh;h!n-f*ojQxFC_&bx07J8H44}6GPKv+G;@#Ct3feumiiN2J}hAN%4)+{5t7=#v4fY-|7gvkRB~Y(kEs-D*1y}G z*=-5i$jlvwMHTE{cfL!JSO{s)m6BS;E?*1<8J1t>fbcPRw>+gw@(gKE=&fth28>V3 zAQDI_9W!b(G;anLS7o~t-MrxQGB2zM8pG*fnMi`w!(LjIffp=(yGw|2I2}@M-0N5Y z(Qo*g9&^DPb!O;&#>mZnbD7thW*8NF^|z>$)eLjjXK{g8YNvC>P5HKAFb+Ddz`&qy3Kl0{%@kayA3!R(K~CSn0oOBp~}wq z+;{vtW3&;YtOpZ6r)Q44L7APOu(O0~?>XZcrSS}>g&%zp5i<+?Lv98kF4x@g_IW|( zCrZUj#+PxrcPXfBv63#JC7O(xwD6OBqnKPP^2*Dl{VJ54PYc4;!IEp43$Kl+8^pOw z%QA5TS;>?L^QuX?s&P!SNBrsBRD0vgTzoVk3MLWR!Q~IQF$oTBIn%S%_s!m7n~mj9 z6`7I1uKFSqv5dG>d!z0d)`|e*;aQe6`!Bi7v1IBAXBv!_c19oApB(NoYMJA&JeLa1 z?But{c)rR`asv`{b9X=TYg$=qnNvb9)zGD|oXw@Wk#TskV8HwY++SqbB98s6SzdAQ zpn7m5_u4d14!y|({TC;RvRO9NU(+BLkmhcKC#EIEF#Xc`Zq247;!=D~jJ;C2JL^T)8a2#}X0giI^`Y=_%k6d)iMUxmZ_H6!zftsU^7|Wx(Mi z-oZSpSEWVpMecOVFxagWA=U)r5!)ochPE} zzS(f?50XGOIyL{LSpr7+7JX93y?>dSruKCo%^oUCjZ}7ru7hShor|nQkVqdBls52F zl>m3xQN<4Zql^2B3tjis)B7teEos^1#dUk*Puv%2%vD$yQ&9+YzEIG4EMM1f z)@3((RIrgQ!?Fzeum`Mt(VvG&^DLcLolcthn)ujZXqTc46u}b4Tr)97X_3(vQ-XbN zvNZX}3?nWvm`P_$UwS-bi9ULCcK&<@u1*lGWG)&1n{@sL zl;QL^QE%@BDy0WY?2F(`a{l+V*ydH-}IL3q1oy;UQB^(#XC)BH%ApSjzzg98TVt9q#^oXZBrdMR`7bp1BDTPVLh&{+ zhGoas6o{7;ETciBE|N;i4;s@c)9}=C_F}X_xfHoJ)TeH$7*{!XicySEdmy74VNx|H zH;LT8$vOG~_)jUJgl=_nMVKJm;vm}7#k%j*?YE&4?y1(FY>AJ5h{q`q*Si)n{sVlN z51`4g06RF+Rk$H|6FMh-@(q>p%7JR`)(k=xK7`Up16n0gWqp8L!-;VhtK5a`I}6Rh z-fg_hvnn2l5QRl^vbp4vWNyzBJnslLDQU3J4N&e!gU5)c7a2i{G2cJ&H*%47JpY4S zE0ZH`D(#`x3$-yk*+5&|A+eZg#`!r<1`NJ+!3Dh$qSl}gxOl=T_J3!9%+q}Kab}Vv zF);%7k@= zvnjaB{U5u>g~s2jO2N(IR_lK3VxaUr2Ck-y0WqDV_}e(VfOfiBl|8P2O_O%RAf4Cb zt!i3e%I)JH%y?x0V3P-Ce@mqITeVoYD1uvGTzg*>KpqWtgN=^yTX^)62FO~VIvyLJ zuOV))dsG3ucqSJsxi?UeyV$?}Ej~J#S|j9eC%=m~Ld$+9Bsg9!N({|Oabh;3Y{wHt zwlI?l2#{Fu$tHWD>v;G0jpvmB6;uTUv(@4~Og{ibs8pL_&%ED6dW}_ zogqESnvn~)wnv7nP7K#nW8B_Vrc2LtBXz;-lRsjI4T+G-*+$5qwIM7Zre1$#LgcD7 zOu|D}Ga7M9cdqg8sFCcxz25;oI%7XxA=-`Oqa`9?np#V05m8samiaUi2_wM^xDc7K z3R0K8e8p@vFGWpwvF_UM6fR^sW_NfvD(wEQt)weo7Pq-i(A9qdStq@lwUnW%w61rU z7D7ZJ41As7GLd+k=-+$rn+km7Ld^@=Y|iv>%)vjYfW~{16AL)pdy%EtR%f%5J=fi| z6z<)S7kKfU6xtp<`^B#c-3-JRM!iJGU>OKu4++D0p46->vaNV-GXv)Kg5j+P02^;K z*MjhR28+{=Uf%%EZQ7p6c-n)=Cfrg)t||;dXH7Zh_e&Z|?&wlMj@(kBYr*g=oCnk3 zBS4Dk3XaYZHi=sKOa!Ub3u1JP=_^8?@IPDOkupQEai_Zwae5{O7Eh)k>&gz$Ex0su z#`Gef*X6vuHcZQ%f36rAu6W9k6KLgv+PvyjJemOk#}XjYbmFEs3FVy(+pj$QB&Y9^ zfj@APK3<0UQff8s=Ud;6gr|Puj>kXSz1!ApaQ3xDH|(484&s2}UEEvktWAkc93!i% z3V2na7P!fE#T3zNNO9@rOZ*fusv6@rLN6-~O$P$!jap`I4x@&;l%Lv8CSt9A0Taro zfdaXW*SH|~ubNk=H*`L)=2}P#6c4WgS0tUnpJD8)dJ!UF8P3Kq4=m~VXX9Nm0}6Zk zT21*yrty3OGA+mt2mF)lY;tSYMhl=27qihAa267hwy#a!8YCp?n)l(3D`fm+ z5QVj@pYqqQ_zS=3T@YAHelCMeNS~y9$^*^!%pa?NB{&M5(EM`wq^BI5q zMR>rbFN<`3u+=`JwKa80uN4UUdIi0LPA= zgCo`W3xsnW8!K$5nYx&mraeUxT8QTza`cl_eJDF-&Bz>e}8)zY|(@V(ZJKgGPV*n!`&$2Z&sS#r( zBU}4*4wK~AdR?-`)N)Rny8wTl3XOUtz>5 zEYD>M>|CF_oe2I;ZV(VXSjehpOlJ<6%j+aMk?NxB)Z2l7df$+kKL+kWz)|KoREJz! z_jDLONld63Fv7t0@MS=Qj_E&8p5QVrdCw1Ir$xb0EC;hH{|z{T6eLJNU+29OVssE2 zIHK!jcWrsQxsXi4znQ{F^)huWHiIjb_9C_SOJCp38mmq$?t?yBmo)c{9+LWTz(y_~ zF_4Od14%54&6ML`Rs9Wz59Mmnemkz>dzqhcDm*u>Q!}f0FZWTJ?e__?g<6{PD0{tf zf8+tg!tmO^lM5G686sO*C|iieR;o&VzWzOu&BGWm8%eYs-?FwNAkbpU<3vXZMt0VRe7YULPAdyO1Uzjf7hqHKxRP%bdiS9{rr=Sv}EslVQ4%2n{;0(6mQZ1_!hQg+$*sKebE5RyBd%gN1+UnV`VT7Wu z%voX0|9>p&;=9YplAY~ zv2yGc;^VCYlOx6d$Xftr5JgvDGvg?Y&I$Zf-^~DQWSMoLF)b|&VlD+8Wjk3@K;jTN z@77O-hlt|qxP4EPLAV@13GjD7oTfdxHFiVEJ%cx8de7x+{Q;UxGrQD|Aat9oJvh1} z+hLGlG<_kibmlT~jlvz=qUBliPUA#rhlTJ4p*cY8lug`scB=>&u^~T<-5~V@|7}{f zR33Vr-i(k`rV#mbh-z7GCpCoOvI+s431E%b^dX}a6pAvptliHtJ0ulmWQx;T+@Hpf z%K#TSYI;bF<)Bj3y6<^Foa&6qBhuZ{{AJ=(A#b~Y9OK+LH25@Tw`m=l{)h2hZT zd=n`vBRZRsSEm@z%;8^T_{V+fy9n#3AH&>)WO~+JDqeOSfFNZC=5HFZ&O%A;=Dz90 zZB~c2OYd^1#?1yDq`IwQgZEfY9N_J-wz`#Z%dK|Q7j1_m&JidreLrbkYjxU?kuba_ z8P`|Gx?yHc|DWV-s&n((3H(RhEwZ*nRp|z=Evt0-FS=YOh|bGV{b%GkOQ`Ff`2=K)^zQx&9YYg!M;>M{1NJOFR9c_P#2&RqM678 zH9OgFqRX-Q?+TOF8NNArM7b1qAKZuS8@Msrw@iJJYB(C*Z%{xI!lCnQG52uy{LxH{ z?yyp4*@1viRu*d zNbY_3orLFuhSxf9{o11UDDD^SXn`WTb%B_S7ybDMqTL;$9nIF6P0{Q_wjQZ+XP0VK z<{CI)iEc-bh*=}->OcrNR#{>PVD}Vy674MPK51`I5st^HGcexAC7;(&{F`@sv@;PP z#ABk;g32LV7C-esZg*=gik(8AFA9ju=tFJSp)nAwreuz zNUtmiVy2XmSD_a41jxeU7+dgtz@`)pPem&3jGR2o5;I7%&W{&ZpeKKVloe)eOoWjB zI%Wut3LW3$tJZDGdyFsW_6nD_pOK#Vb_==fx732bPln-W#TInEj74u{E^ZPp-Xbzl zP@XV~A6c7j*-b6QGQ;!K*Q>r%(nvhYSWk3dAcq3dEW@aYwYD7vat{gRxUb-mh^jQV zJY^j$6@ITH_bE)J(6iz{agcW?^QJe%SvoO4arRG(y|^nA=j>!~y0kAiv+bV; zfK25tB$R8{my3*w!ZKG2%MPm-iXIWVA_l65TbHDR~!Z;=xkKN%yd%PBPS40u6w=+%@?Z9@bmBz(T5*5kv zz@R3js$50|bqti;0r+h}zI!Vu5W~};-Xuw=mFEZF`q*e?CgDy(T%;Q#Z}^uY3K&3$ zSZ|d2Po{!AJ-M7B3L7ZzSy}S4Afb$=Y#R&8p@iSV^k`phGlY?c0W6h{{tkX zDysrT(<&sh-@Omxyp6(#d>x$oDof{=HQyL|u};ptb#Bq#*&aI;`+5Oo;ky)W1^^rl zDEMhep&U$g?qCz%*ln3~pcgce{gUWZV@E5>>=rhUp}xH)nI76;z%g?UI(<-i8j@DPxJ1bxzR3zOpFDXPkdhE0)o--n{JVN{oak@{3THj(A@Z{8fO+$eDl@kzP}$YmB&l6#4-Kkkk3(bV)au`z zc+m+Wu*e~dg^tx!X_WT`=BS_^dC(#hRp@{GsPR_EaBBm-AcPenTcb6H6SGbGBb_st z_4hlR;667F0;9-ceo9Bi0y8dpvrQm}EoV)jUHV%e(s2Wsf+BFzzs_>)Gq9=lI41M` zGJCB_3vxcEJth?`R=ej}qCtm!w@V;T1(Q@dbLgRVmYGYg#_TLE#%$A8*u!#eh^!r6-N9KK>^&Bj3;n)4&_f4y(bltJRKKiRu1wbi%4|F9e^LUKm@=9# z(C);iUoF|g?OS7RU3F6R8+GcjCz>D_6TysGtxo?!5ouM9$O`PATR-ZXqn8 zr;9h{(79nlsg!ZIEi;40jr<<~FF?@0;%O2G?cYi1U#Eqd`7*bWb&clrB+jTJvO{{N z0nu>a?XRr#E^JQIx`!pb7Sy)PO-sSb^dW0Z*%;JIl8uC#%9Ne@QtL@>WIx&>zR-e( z8LQTRlf-Q$vunUbuc>&5d3imkDJ7-gy4sL?nAKZXrmbWNjbNrY!YoeQJ=N9i%$cb9?JkajR43)k?jk0+X`)GQB z1~lQ_Lp4ZJG}S57g2+50+-XFV<|{J^|M^N47nto#(`#Np>Ja-svdrX@XViqKKW0~_ zdXr(hHLe)@DF2((Uw%L3-%PRt1RMA~Sp*TdPtM?EXu_cO)U!VfQV_?U`4nm6)>3CB`3b*kshPIQW@v(Dm0|E?_0UMOa0ZWyfd|UPcC^^sIgZ_)j^i#Q~ z;L~%c)BzxH9znIv|K?ZH`o9V3=Ov_1g_ZraP6{rZRB*j0M5Q`oi}d&^L}Z zrTd~eruf_2zn%R!iv4n6?I%EdsE`La-O>cN&&hnPnNzW4v9>O}fFFc1B!g4-I$qG%lzsh83s&v~A>?6B>&#W5Bqc;< zAJb=#C}6v_mO9ahe$pfAq>#tr(oQ+*b7m%%BptlJLZ8#UfEI=MQa`&^Z*7ubGfp>C z?uid`;$d#0RKvs*A)HvAon#+ITHfFMZGNCmn0X^PNmTwZk9*6NMat&Ph>sv(Wt{uC z)6XL$PX-XZbzuUYp4u4(Gl@bO$)~44Ayh@9GEH9o&MS=2)^d`3q>5N3hZKk6Dy0c` zFQwTF5*{Uzlu=MxFUSOfLl=>}p%%{83M!`WumiZlrJZD|+*PTRps+-jiMbh6*{M~h zlWWcpv*r{{6GRTck)5(Uz>hpM!!J|A0xu8Dow+-vufI|HbHAIL418em&5R(C_hcN^ zpm~UWfikvH3wkX>ZS0}5EWnwW*s?Tev;2k=w~3kZY1=O{d}z?6g$&5NOm-rN*iwLM6d}*;~!FvT`4Ei0U=Uh5FA)@Qspd zx;%Nb9=-Ll8mZBP>N@7d`aBsAn$+1)tI`L*(YLZ&MBZL^ujpKMX@zP(dT4=0l~?oB zb4adiAeU8FaU}c559D!X@$dB(WIcC5=xWp~DC#w{$^_V&f2vSUuF+0YzV#@h24#_G z!1A%hNW5?j7u$Ff%#8?9Y_?UVMF}%=Ni*cT(%(HhpR5VVO}t7n5PLu3fHxuHC|=jg z4BZ;y9U1avaU=r0@g%OY@k1Uy_{gQ;Qq-###Kml#scOeQEFJiC(Fq!@mkWg=rI-Wg zV?c6a%`kB*Js03l&g?X2tN?CbV&^oANI<5}NT5KbU8?O}5>*lXd<9#CYhA+Wut|0MM+38-3(%^H8bO237)mMSZ~5=kvBw8^JSd|58@Zm_gDD4xGh;;{7m<~Pze6LsnG zpVtj&$$Y+k24Ee89}EQHm`_oPjV0r{)7@!{_VRJ*{0R1zo)gGqrYNn*K;$y_70Pi! z?PeSl-Nmk+7US%1J%;kH`0Fn1#MaCv0PZD(>^P+Y*&^O8y_6MUS&p#*HZ1*8c<2W> zlTVqM=)Thne48Q#PU8}&^S2&D)x2_v^6@z4g zEuxPlgN~X1J z&7P~Z(A|RAcgDgvbCTiE6E~-8a7wMQTF(WJ;9^fm&iIH2j$tsjy=wcGA>Bed0-*Bv z_AnmVS(sh{&GVsZBJ%iMJgd6D*6=qJ*NjtUc+#kBHXWvEi;d|`OQ3so%kwwBPwFFA zi{@ActZy5JHnKTF9usABJPK2k9FZ8Wn%cW&a5nzZ~dAnFhQAK0-&Y5@NL z03VA81ONa4009360763o0HIQyy<3v)xUOXT&tA#&p$@16G=8uH?7!6{#&9Y!_de&| zs+E~5Bj!U75(FF$C-l6}^}g?Q*Z3ZfEqp!7kUt|AWf2}pg zn!lg+U#rdEZ~Nu_`mB%7`hCnb>$`P?HGbdevwr%0v)BJeTZ>o5sQ;Z)t$l{RkNP{} ztTS-XXMckEy1zRKTUvyaxfFm7vqbo6~$W7Yra z#op`l9F@cFiy8g*<@x*aoV|{+4_+5F_L1hOUuzJ!97b%_QC4qtd%f;%?W?ata?-_d zYv}fC`$Kcq4c1{++XFM}?R}E{p_+-oXZLUW%FmE<_!_tU<$Y~6Rh`4txPF&W->#33 z)=(}dW{-AVWgU(S*hd>)SWVF$vQe+sl-7OK z%MIgNH6#1ztH!-Xu;yT2#q3^0&ybf1VkBjo=k=OI?!NoM81u38eagD*%o~S&ev9PU z3+MVRnBOYlt{5o?s|H}-P#bk&gYhXd`i&$r!-5$#VRij=2rp~@-1Z0Oo{Mczi`(wp zdfz6YQFiZ0L&l6+UCNw}4@+VHFj8cyshaOjZ_sISn&(8z>{L;@IZ7=kequOuP8`tlz z`gOmvofkhp?efoaU5{Vc$J*~+7ccz2_}oqu^Xb?%?Qk4lF={YeWvl*)5jM#e5gx!%dYl{ ztJCb`zHPs(@!WjVC0U=?P~&ETF}$lF2RC!K3nTbl(>~w(VpwGt+X%fLk8pFp+{gVJ zu$$NFt`&0OL-rBh8q2z?GWdGYzaOsXt_9Pc z7kei{ye~eh?v`WmzF*m6S)*IG*y;mMR^>j_!z$gETeJVvkYBFLJ;g7k!D#>EmGi>B z?EOm3dRfZvluxE>ztkMBhi~mS>f7~8#y-}zulQm}f1h}+b}?WNgY(9r)N%Oat?iX9 z)eSr~U_bMGYT(MExWr4J!`Jrk)vPh<`y9`6^X0uTa8y@()wY$ZVC|E4uKMVn!Ft)* zy?+bi!5Nlu@Aq|cHRjAbW}f-Fw3%l+zkjv*{fy+n z!?GmbjOFFrvNjwi{NO(4wu1NG>$$AgMg2NH^=>Vd=i=0O-=DQ>kBGfv< zScC-vcFljQ^?B`CuGLg;l#xBIbE8!Z=gjBt*TzKZKF%U8E2%lbDtWJ>n3J<7qVWbU zpq9~dq7qKm7|GiY(UOnuKzFl#4Wc@mSO4-{pd2Q!uD{l6t)&+8yDt;9r`$zNy{<~v zSvFdSdtD~2%!&)u)#4@%1vLyeH{fbN9`EN2h#ck1YZx95jccjf{M`izHaK;V5B(5e z56}VEB=f0I3s9LrscjtqgEj&-q1p}JJcoR&v8@uCs`Q}BSd+<*8?Q-f||0KbyL=<qRDOPjf}Id zZ*$eVQ^6>gIE)PQ-ReIy6&$=voyu!$P7 zsRJ^W?aO9>$%(tT{XqATi&!40Zf?K4m*i?SdUHCCBiC&f&M}gf*Ko{{M69!bf*4_Wu+TEu zF3q=7BQhN)YaP}ETf>lR?N3+58JKOJBxoA@FDa{e88t5xPd7r4vA8BI%YLbqqo^x{ zLh3Q>%nsMX=Lsrv!tJeC7@a8hWQRQ|_mv;Ru70PSxB?bvjYX5N8afScevUAWJ#z@thfO(| zjWs`KILGdyW0-opOxO(8<%Du<^&5tRvmmt3P!tCHvh@zpdfZ229Eu9VSJlamQ>mZo zdX`K1IMZ5^vRR*f>RFbS*0bVWuMXFo`mjudlgi)>LP!sJe7Hn^;+|!3T$UKH_@*3E zmn#c7v?r|wsU0uxO~-o(p^2% z70kAD$+|g6eki;Sbr~P$oUs%aPQ%NY-ucLA6+JqNeeCi327HDgsRq^@e)wA`J3;wU zk!`sXqpH(NH_F9L9k_0`hPK69uvUgA`>0VP&JuS_0>G#{X*NyKuF~?B^Zsle6WteC+_*(_`1; z8>y}t_p?5?d3M;_x{Gd*%Icp7{H!cYF#+qo^^t@msqX0K1UP#G%JC-y?>)zyXT}_IVisSn8rM9tisK;YfTy^jlrm1JsxNmV zm4RbbU2w_`ET+dOO*Ap}+XGgpRf1to(8oT&b@V6Fk4mXkd&<86=m{xr{l^Wz_JFtSshj6U9bk zD}Mc8kn#>AA+R$ml-S4yT~(>K_>pzYq(I-Wc6NY=*m;*zyGRt5KGsZO(h`G~A*=VxuYXB}0bBbQmSKs_;vN)czY z3}bRe390g`!}uTCp~vqBF;e}Tsd&y+{L9^t=@lHR_B=g&+431p@_5peSyxp>=isq z4WhzTS^im|0K;RlW_s) z4bB88KpsGfiXnwPK?&7l-fhdEQul3$&&Ym&<#dRtjIiP6ZiwOc8z2o$! z<-x9vvgCmU3ogk~!G)3*lmoVup|RxZ(S(6r2`j(P8KVKZ|>avgmG+MZT|388hl zH%JmQnQ64|2dAA#K-zBrJ|FnlA=FGgYnBmQxv*N#VXl%oBx@iM>p`vm@n<809)Bak zhK%P@5^=wa#s>?e+vp!Rfg~d^c;SZu8Ap)>CV-MDOrnSig#?$>orwA}YA?9(!WMMNKw?5IXaP#j)wvmzGxj{+O5sFUis7d^c!p0(&%#46Oj#^|| z8#)bNJ9uI!Q2KdKLFsUsMl_eh0^K|$){2n@&^5|2W+!rN(?|}^QEwizzQIdx!d+p4eVF0-uPPb%6alX zmZ)h+toU{F~}?W0r>`Hh+6_{7bNYl-9YB3O$*`ISL<(H=v-JWuKJZ(9EA+X zr}w+$qh->Qij)v{Bm#)$iZPrOZo)B@|46a|-Wg#|$8<4FE)2)oI`?lL@P|#~HP-V) zjZi2P996T{iU~N$A~bY)c&bZy4HNA0D%^H~3=|HK1L~l|l0Z*^e<3z&?544NV^3wh zHK@agAN(2Q!`PO~m?vr57G}NVqs@DWR(aC>e>nW(Nn5 z28>cH8yFSuHy4Ddl8{!QO&)?H2>9wDm$0xd6)wle>qBTWfX;)-a%6&_TD@y-Vg4{! z*n!xLayNF*4p%(MpSelN_@zm%WodZA@lEeJ;IT8>d^z=!wGK_O9K0^7g0@aNyoxC1 zyYu)gmPvl_JghCyF#7-fb6a{QO(do6VflN_WOOyvCRMz+TKLjKoKDWb>fzG8k?^10 zk4}KA<9}l7)Ll!u0=l_i*M6bZ76=Sx$gG%zO^Mz<6bXnYg-*>;5Y!tlL?UI&Gvv-l z*ca1}O?AZPU_-oPMGe}iRCEe8(mtEh|AxwefnLH(SVm^NajuoFF20OU8Y8L-LJmnV z1k6WVYF_agjht-As97!dunO(PyRn)*)xwzKY6UJ@m7u2{BnIRV-k2m&$`T6 zj-x8yXY5@8E)UEXk$c zJWb&U4C3xocTc|>8 zZx8HDFq|%iWg1|PBj;9}0aG7y)Hy5mVfF^zPwe1w?0`5O-H5l=im-8lfcKP}!ao(6 z=DWvBsqabwcBo>}s}L2jss@2hKE$HU3L~)sBqs_{E{{bXo9I4g+BlbJKQOH}uZLY?*O`9(GHFwfsBu9+RCQ;u+mc51m5TI^bkQ zZ?GJ>ijpgJk{+Kq95`Gaj@j&M4b7@*H^Jq9BF1vx&~(k@ZJ=gKpmiJHP-SW3J-biC3D34rHabFNi9>7_lHcNTeMU(?f^Ws|5EUkznCP zM6)a!Ni@eg<5o5YSSyfe@8UmKm;k$Q7rq0EY6e|b;fG~su*7>cU|yt!s`^Lb*HJ> zc5FKf&Toeu>V{&WL$Wvt`&u(CY|+Zv>ckINp#i>Q<&8nfSazxk_A61rPfwS&PRCz# zelr-a(gdB(mxazV-HQUeitbp`%hGCWxI8Las6}(wq*pw)B(98#)SyW!V5A zob1AjhO{Vkf>XYnFM@kQADQ_yAk~?dk}aH(qcf=^*Jch*Go%sM=g7{y$xV)AJ%I;v zv|-FIM@za89L2(#eSNET$QUsE&Gb&<%LP@Vsna;6-5&qS=vO&)dc#?hEXRR~j9B6^ zSAeiAbB(G1bm};hKo=fwcZS#6au#iCi(nb1_c)isn~@9 zliX*8cJG0nC z#uC$VK*t9YtFWMcMq&UpJH!3&@oX!KWn34| zvF@RW>2^h;gp=dW&v(g`>A z>4q4^#>iRRnXW*AzV+K>5lsQNK&J}1Gex0g>8e{CWsI!V5^GbLXNAwUqWJOfl=b2P zMzH5sc#;m-*3y#st{z~rMA#VoQpA!2>-*|sg#fADk;c?8 zG#T|%kX$8*71@yvnaaZtk3YlGR1KT~Rq=oe0>HRTb!i#>E!o8BXcC4Nh)zLa|3jk* ztEBQ)LmI&lz*CFqSFUGRRweino{25Zz`KGqki6h(O*KvkSpaKO{o(+ViGyHtUZIxt zV&kB(FLBKlDbdpW@CKV_4Z_cmSXjwv6txg~n&xn9bHHeZ~ zmI|)J6UuObNyZq_f0}F==%C@@ikT9L6O%1-D?)x%ILk2FJd%pC1BUk0#k8Ax$K*Pp zRMM0Jt5iVUsD(4Z&p0zZoK#~$JF^N#ReYCa$Qm8y#kiJod8PVnG-R<47@NqEi;AA3 zEEzI!sWz_SNx&;6d=%wETLWLZtQU1~r?remSP`|s%EET(sk_pA#>Xkt#O{UhubG`v zhD=%VDM-B~VwUPSymp7}gTsnY(#UXdfkfms<_uA^@KG6j%*gl72>TgZ)O*gfFrNBe3Pj%#iy zmNMJD_qf;9Lk>`8kS<@+SsmBg9Voc36Np?DlQ2U*08~SWLBxRWNiG-)e)L?=DGP)+ z?cv=UH8o>_Y%#GVyF||o^-w?NpE7ZhwZI*tOE-l!ahGFA#Hn#;>%gG@`0u~}>v3u$ zG0-WvJeAwva_i?+l0C3IOnL32oBtH^{}Y#YA(*mW!7wnQ856Q>K3u-mKY%&08(e-Q zn|}oTE5)t=b1%jfSu6WP<`wAvLi0GkIQ-$IJGupLdKiq_%&|-uyB;oS?h1n$`|S95 z^Wv&Y8ojO1FsT;lE2PTcfWt{Bsv|8oA=-h zqty#6J7{T^dCqkx#W>`Y`BrE)*;0@s;GQ#o<;E=QQ~sT%4px2(TUb3}z?Mv>N$@l$ zkO4Fb^~9hvBKxhz;yP|B?86ne z6Cq>@mwdD-=Rfdw2w-W&lIHeP|BtG+rwD8<@el4^sbbcbyV=L`W-MV2DL*|uH@~uQ z0TNAsXG|q)_T0oTZP%ObVfV1@5JP~*o&u2?fFKYFS7W|Zbd#c*$88PQ@k*c`=04_w z=yyDG4;R1S86|<8&mmbwvxB=6IjnA;cHf4;47e=^>&{>z{0`*pft(CVn!l&<=9 z(IBpNMm=dUL=(5{neroSc-l>DXa-tMoSyporcu*%(*depd~AF3(Bg}Rl!m!8e$>G-wZo}{a37c`v@~@-GP)PT%)X9Ab z``!k@ww+)mnvUOL1r{tXNI0gshDyXb^E`49)NusFX|!BceS2PX?LxfAO`Ep)cp*!U2;pIDHI0TR*x*1o8*n9eB?ZvViDAIC_ z{-CeD)1I^J=v|gpX4h-C(7MiMFRF)DgEZxOXbDH3G#aLG)#PP0Q!jZQ+#qi{MP8Fc{<(RcDTE$dDfc-o($c~ zuFYH5@irRbJvbw$<>Kbu(CJQ2TF%*$k$Y&Dck<_-$7Z0=0M$Q`&UJMIwC8;7jYq%5 z@^Vk{T6EW>!J4fX9nxwZcE2{*yW6X`td@+nH!DkdFvO=CoZ7y#8W?_EOB+4+Zu$Ve z_h~$NapN~8^4jqdFMZqAywTyZI^Vq+AKRZBBwi=^Iqh<9`YJx{zHL5QN3CnUZ~Na? zi?jRvdShOWKY?U7*1M~{C9MZ(gIhh&SSd6wPESBOVV==%)yM6!5$O|S@){l0o8ERe z4gKumx6gdt9Otg#ByPRwFG@$v8oGzR?~g4gYZbJYcB%a9tljt5QI+PW21ri$Xtuk{ z=EH5+=8j(zbtrA=~uP|KlDqHR$hm)wn$ylhIkucrOkDgF9~HmCM(%>>m2-Dd%U zNv>m$X)akq_6ABku4a$>kN=uIP;0!8$GUAA`J8!Sq0>?Jx^*@!O$h(QidPdX1EA9| zMulr(zV<3>Hr#w@+Igo%x1RI0vve9)+Ps(0Q#}yVArVLQ=nWrHCwtB>yJ)^?;keJ! zjw`QI|2`#Uza%L|=xe4ZT1cnhdL<~ZzW@pl5qQ9yXDO>Q@sb~(i_KXc9;Sv09vEtf z(|bztq<(;!RogtRQ}uII5a`ezD;fmTv?(uGA8vv49IQ+5$Sw6`c`!9V2-0^H4$wAp zd1ev-_{%8TK;*yAVMv+!r`dx!6MKKZ+Uc8Vv6tw3+eY%GYwmM@VK_ad=_CClsPirV zrkQb>TX~W!3BG;XowL>_fhOVZuFpQ2zx@o#$+~o0nbj5-C+cFikt`QeZoIiB?BbmM z%V{Y3+Txdvz^DJ`P42Ln_4^TDUA7@5!O#_4;+;ouVien-tR-0<5BJHjb$T^3j}6!_ z8ZCp-n5{%Y5{48yau|gMEyE0RTg!@bqk~Fynz%doz20lD=(%p>`~Q>f>p7AznM|Da)jL*Q$q;~?=LY!M#}GVz zJ1xoUu#MClnaTN)x9L93%p~ zvvz4We*B47o8-@>cadMxSjLEf+YRgCwPMG39dk?54*++a86S~sPNNV(;T~{FBugYO zj6R_iqs88< zV!=MhQ1AEn^|D7Y)be4V{y&}WLsgu#%CTpeGqJRRZw}!Fey6|@X%J=p8N)p4jqJ&2 zVsSZm75`MwR{)9m0}GH0v6hYW4of50j-?ZusZQ??NnO}hVo|$IS@llf)dvWfCuF#Y zw51|iDz>&enB}ooeg)Mh%m0ygC-6HeiqB&F(j;)Ta47+H&0a{K|Sm8 zJSPb5_)P$QdCJyZMDhJ5B@4%KD4Lr^^XGv|iQ0rgGJe8AxNW+CDG_`f$!?t-x;;j9OK6~3j-;B6}M=t1S` zeQFBNn|Lw~t+J2D$5P%9$*<4)sQUwdJtQFy51zo6(4S-`%47?jwWbq-FutQphL~|5 zo#vYYs>`Au;UQ%&msS}!Vlw*y63ve~Dl8HUEZR-BUm@J60u-0MtjJQFp3#!G3&8gBa2)u~b{TK3Jn%rS!sv4; z!%YrI_yXlTKJUibMluV`@wxw`tQF0DFxHa<1!DBoyswWPIn}sdb8&J?5<}c1FmJfEBUWV8_m=Rx&*rqUp8Q z@=LW01ob^EE!hbc7ASn@Ax@W>s|0|oJh&~e;_|UulsEvL<&i5=Fp+xm+Y$w(q*@!5 z3N=-=R5G8-2QJ`>WZ)%fkvRkAknEYmnTR19}~M|grXdz_7;b^!BnOw$8T-))&{jtw-;CIkR(pGwYWe1_>Wm()7tzZ+~&3z&kaWm#^vjN}qgys722 zC&*9o>~>Y(sbN2%`9E?YoO2T|31dJIq%fD1)C(@$|5*LHgn-~MY0(D_&~u<9p@9s{ z(xp-Ey*k_f7zHB%RK0z6{A>~6xc5WAjKp~NVNc9Etj zumrcPrF;>(g46bZ9~vAZRGaQ{$8-TvLpR-z*dK|Jdum#0Vjv{8$H1d!+lC7F+O@|I z*?803r!*pdslYSUx~fM;j;NBoTN4}X?H67pftaQdlmxnWPU+2rNzF|gU17F+irBuA zJys|{*^G|b=;*Q*y)IKQ?65x5k^oy_>wIh*@lK~e3***gwh~&v)M@zJ~fHJf3YKCHh4Z}Y>C@F!q;c{8*3|iVxK_9oqIZh~cl~OJC z4vtWBDTk6}r)+fgJg=znDHO9IZMx;cVLIaa#eNp|26lg3Yo#ze=fG3LLbo%?Vmdv$a%``w`4x5 z|0qklKW6lQ{zHbJkERU+8~4usCOgu&n~&K(lH927R!^tksU{S#s*zFO)_Q~Le-&*7}s+KFsIaS9q@b$LH>_H2r% z?LulG)IO6Wpm5LC%?w*l@3zGy)bpzbHQ{ruvnNRzM+^RK>4@(g?8u$&{_Fiva>svA zFPLY4RfMG-Nyu~qNKABP?*xuM?@6n5+fZ=XYK50(x16Q*ccTiH%^i$!ESW$z&RZvU zi@^aWgKXVCGrgG%bZA9Z-FKu;XUxFfCnVd4OZQE?<>v2@Lft3->Y5KQAt!97* znGHeE$cP~*F8Q(hKhoDEXpa?GcEIy)W;FKa&p*^ntdo(bZ)It*I~A`|7^pf7k*@#Q zS*9Xl{Au{Wo-#IZxkqbo<;%o^S`S+$znx`a%4(bE-zUTLbFF75Svov`(cJG$puea|WLin5!1sqSFOBopY_W&`oz1HUkw2@Xurcl>ev>-;zLX5BIyMaYF= zy65-z%Ja$IU6&XycD4ZPre`=z*H_oa37xtirf{i7U-NAJb0sGk2+NW$rqtlM$xk{) z*mQ?p_WBn4oVLH}BG+!unc>dbjSQxBlK4YRWQk9dAq^WRw6o<> z3Tzx7JJ?A4h4vFn6$7#K5^P1$Yh*wC@R4$w>`@ja*FJH5ATzSBjRZq&d0Z!^clW4r z8S$ppm03+SA%3C_d!K+B|7#4-Rn0#ky#BQB$3LU>VMtQf`VST|8EmhN4p3&R8DMF4 zN{ZNoY-b-IUti1iIm5hW92EpMPKsFLt!#_9Cem#LO`PH|MIGs}ICC2~hV``+sr6*f zG>Ov@=RQ&}=p$Zwb{9{V_&_S)%E^_k0Lr=gzyBDu8OG7T>?3kkycP>@x}k)eWA@;O z)Ag$zE?~NYsH1M92%@XYR33PRpJ)O~lcotvyk_#U55l-Ha>)Wgrg@2i9HS8n8&CsW z`EobG!D2(&1cs@Lf0YATC3xQyAS`mgeHCxm?(2`{k{d|D7S|mgI)gBelV>CGNcK+Y zMW|~Fo;mf|(-lq{R|>?sLhp!V&h&2{0%BP0ISH)ey=XJMx^#l9dDm#Cir{R!>KZuz z(IyG;h2ebISU~qY-9RMO4_QHp^JRMhjG1NmQ-W0b$L^qFan}^?)#cM=bNq>FhdHpw zX7SHhv1&Yr!4 z-IZyC9~PxUy>xMG4+LaCU2|iWgXs;7kOyldHz0TFukx>=E8C3~UlHA0+3S&iuE_Fs z759bm^`8dkuKf8_-yWy$w#N3llOvMk+Wt_2I$ohPeY@=|RbRGKsrFUxd!7{6!m6s6 z!9EmQJls>1jS$dqq;;JwvhJ!js6o5V7NPfzmY7oIb>G<_wzbSp>iSW8YOQWgtMYSI zH;&|3E#NNjeQzk?se_gBujXxr3tfLL0yo9JSk?XND`wVSmq?#oLK2GyTntE4HlMrp z$DpiU%PeEa_&7MPBZ!vyp{)HXI#t%72>lU*iKzpu%Znb=Tklb}e7;?&$nf0KqGS}! z>K>?u)CSE_RsP+Gac9dPs)Y!C)&En@X$S|e$FgYlSsmdMQokDw#eHj>XnC5#egzQ^ z(*~P=A!4TFK|K|9N5svIDj;&i=i9bSVT@9lTMbZZu>VHoIK?fk&){)IH}~FW(O7D} zquaeojvO7vlgqrT+mjnAFH65x?(a9nxV^U8<$kau!C+OSN}Y{7N;Nf;Cv^ld?Cp|RsxwmwAKgL0uOwPTtwr9oQbePN;@8B6 zz*pxgyblp*>0_q8=D^Dw``9hQYw(NiE_h@HQD+8w${}!ot9uTBNKeYvQ(uVpnIS6B zds+~g>u+fL_dlqDRg+&Q`ZZAA_vVe6^;07w4GSs77GuctRBLm=` zsG{YmaUBBRgkl8MU^T$~aa)k3g1Bq3XZP%FS^9Q!&vUEGAtGgK4&iA3-rQH9W}e-d zM&vhJ;0*q8YmZLrR^nAVGSTop;s(q5>hIM(iXhk|izW`C59(*6D&1P!xeVf*QPuOT@#y>w~!j=c}Sa zjo#&6T!j_9?zJ2(WkiIJb#?09M_TVbXd=&}?FVNTh-qw>c+n3B4qWb93I5BN`OivI zs{HWpe`)wSZfg#3S08gElZ5VU7Ko^&D_~*3qnDNehb{NqZDATx>`}%{^F`@fx^Lt#F zSC*@y_{REhaeU&>7eOE@!R$)NB7fbc(e5;V0508ZjC+qL*qvhLIwERLK$^fO*kgyD z>K4%jl-}Q7vXa^W=Uedc&os2_d2l!iY-^*LAS;Ii5_nTtb!WfV3(Ll@vz_WfT}D{IExVEDAx04rYDeyCYlb^H!oxRTRX9392Tqj_ z19UYERWIY7qsQd(ebbr%-Hus0@}CkNeFDQENOqg`(TH5BT~L4+H~ zVpEov%(Zr>vO6@yWwQ1As8fwby3z;%pNqY~429Nk=qCl+X3fAJ7?kAyEg0*4$Q@6+ zZwPN2z)?*;k`@$B^$dGeMn#R5sHph;ezXl!esS2{vs0}t>XKrb-Okb`j`~ST!C@oK z@YHnf28zNC%O_wH(>8iqtr_0_6ui9b?ESI2&?z+MRm!$S4ll~yl$%Qz;e(V@7#fQ3 z`YFp+EoC6xIlb>l&nD08GQppc*z-sLExub4&rbV(D&PHM?$=S6%{0yER!~U0&q?ck zrn%(oNyts)(GGy=MhXGSZm8Q@f1V})Ye1C0=k7Gw#cD<>N-r*Rc1O?})LLvXRUE3^ zsY8)_pHrFHfL-K4$k^bp?HPAw6X|{`2Ucym<$H(C+oq6f@iZEg$xgQ!Zlh9f1e266 zXhu6FO_>0mw3xQUi_!@OpOayjW!L+34M7>jH}2W0u~jhAdXamdE7jFfM+yP%28^GP8eA;Zmow zsDgwUMU&Oc`P~Gn9g;Jcu`eb-&)niE+NzALdMy~Y?Er`B0M*C?iyFS{ zgomQ~HhwKfyQhAG7UyV=Y7ve8KRtA-R2`J37o~RONke(Th^B+wa^R9WB~hMt(`BBf z*=NegTKGaB5W(7~G8=70lv>4qiOyy1Ygu%FSM6u>wc)36$f@9nRZrEOY9b!$lYGkD z))n!LTxD7+K3Rj9Ud0cyC`}F z#pRoF;7}Lz(S*vBJ5Tc*N3h2bZaI5U**-!ut@^0y zny(+jh6p`Ng!K`6Y5qoW8<0(?qx0Rpw9`aaDUAg0wT<@97T9V7dcO0({(;H z;$3v|-D=Js#nOkXd#a=uCFpk?prval8H^7QPHGcUALqhxt@<kQ;X*AH_J`j#v*%Bh%tCL zrJMZ`@kBvn2$?GuDsThnmDPhG0Dt*+yWXJnR2UMso}AIYvaHQkG{ z)fgQVGgm52lQ3D&`A)>2#~{mLpeua6ME(lvT6aq#JH@n~B6L!x|l?*J0`UvU2?tPjXw2qSADSsXp00x%hM0 zh-IhUg8AA~k@Pbe2x@!uQBe9dra0#zdYtu`o*D<%5VXHOUcF8y+OL&#ccpYt#cPh8 z#31{Y)26~w=}_`Sf?0^Vc6McSRQ@uc!_S!$_mv$aGcJ$K9OHb}x_X}AaI_z0)XBJ( z3bkf(VH%Lrtkgg=JpDr@OFp7E^?y4}xYMhj7JWVgM4yJ)7cN_mxBddXFOq%ZHk2fd z<_4h=fk9Mg^FvbsUf^m~2D6gysHAru#Iy+?83Eyi7|YP72{sQrzC49bg5VG=;MASC z7%choyLq&AYWckWJI;NbqC^=SVih=dWyGe0c)}|YB(5EV2z2z@^W!!0n zc3Cd4FePj4C*H^qp7f%AQ}fK^zN)RL9iF7a6 z_9~h|;MM042ZKI-PAxxS*qY%lf>peq$dOTcKL{6pZjw#e9+w0uWlt105&?QpFvm|`EGH%4TrC55^7jv`M zWt#-cVy-DlSxUm66i#t~7A!3h(Xo&Ii&R|R^nE%K0|t*SA*p1dFwCiIT7*9#ZUwC< zz?-Q@KRnhlUslME7~}L47;fh&z+LS&NO=`?4;`uS7hK=7GZsfEDrrQ|~C{Dkh%|Mc8OOkemdFbwZp#U*ujhBxy83!M7yD-P54z6=^CvN^Ds(yL|e7*pLcS zbsGDBAaQ55fxy&QQcVH2lJYZneI2UJohcQqNWKR#>hM^WHe^%sk3`7~q3aHTFeibn zU>;5dIR>Z6X{J%|^aDKlZ<*wxl&I*fVE%FNl&Gy)v|#Sox-vk*Oit&y+&zTUf| zzF9FD9^s?`%c%ikcaR4N4glGCXyH+Yt{BToYFPk=ZEk^-+KjZXDz24-e?uQ13nMxDn8 zZ`&T(#_6&qGaRDZ7`nM9`6PaRv=nbwNDQ|%tuXXUcWvcuovN=-Jal2UYV`Q7AhSyYxFmc_zt4UJ09m{!vIOigT!lNcr@I}2v- z^^9Dn1*URj@sgA*E2U=MsY3XF+mepdx2C(fe)mNe2ApgPO%zuQ)#W{Vlnd~jJ=DhG zg&a}t;yC`&>ANBG@QW4@SE_($J`Fx@afj~8L(bxvEBDVM!w9B?ARRu4L>FKE!(?VH zdM@ejF-~a!3Ja#C+zJk(W$DaR@hs&LmQ#C$(JqgIo2{XZTHZ-cTc(twt)$M627tC@ zM{=Vi6b5)o_8y|b`$kJb1!{y@pkUu6tkni6%b?qW+Zbn${D87R1tOk=HfWdG z5>a~((&;QlJQ#+F-pnXWZC_a?!voIYtj2X=Yirv$jz3TNBYQS{I@%$-t5hh>wK@|NZ@%o2U9jO0Jp}#^G^?aO2+ZBc8REbA@Gf(?H#CX;~k4G~R6Ib#o7e zAD^an1*W9YQbpVqej0q-MhaA5F=Dhl{#X{tamlzt^epvOONqe30gK|8l2xB>&?UM8 z4d*U)AutpS9L{!7c(Ew^JJFrldxOY|-a+x|7P!+-_I5+V>n5m>Vm9orX>HkL`?@XH z@n+7Y@uY2@H@uA!_J%0_z4Baz?8}l*WYbkR%v*)HcG6Vrq7Q%Xbb+P?gLW^4qdNb& zWSGjfG1e>cS}02__0hN=89W&+#KF}a`EslCd;6TlTek34zkk100KUQiyV>5;sGN;; zVF#6He+1;4O-XNv-O@5YUT3yog*$!WW%ogdFG=FFIgqESuRnhqZ-B+h2|@U&R32y9 zrhQBJpT#{PoQ=67?(8 zI3DfaA7nj0*Pl-&Rr!1J@tu5rCvSg3S-0_q1P=6`{#Seg%|bb8pt|&*o|Mv(B4s@s zi31Jx4|~(3Jo;I%HL2ui>#L_GpR=0Jg|uMQ^@kCdB@!x?5S=+x_YVAWe^jLM>A!r~7CRCz;@ZTM^RPR_p zw{y_B5U!e$`uiV6PKK%lOKsK%mK7lH*Mkb-36$ zR9&9zS~6#eHP<}MRF4xuxq#0S;7un zK=lM1G7kgB8%cm>0Ch_EO9c*sgruW0qMT{}Fl}c2McHS;{Kt35^!8WUzrWLS zUFOUJaV4i~g_MMqq9Ah$%(1;P}P*qTC1&TRy^6obn?j&W3-Bc<<=Wc{PYD|oaN zB9QgW`LUZ|MjefWPuI20wT>8twE4v8FD|h^XDXcdMMpnDr?pldB6X=~@F>Kin;r^M z!qHOyfdicKZ~B{VH%8W}+(CpcC&gSaRjsEK!x#4T2ijIB_6`>$Oh{y6>)8tC(SD$gEZm za{}vzGY4~wC4^3ON^iWvxvh{=(mFw0Q#p#HsTC_j z-dRIn#xcU=hJT!L^)#{B-&W#>X$_q(|306*I2pV6`DyN8-w$``0 ziRcJJ?Yxof;?5{}0a-$8Fy#$Bm%Ng~(jjh69n_++mRC!x=C_5`hS}{r(yN2U7{skfZk97sB%@tR&B-0iO)@hXEQW9_vJi@iCIL2aJYYD=ulWo?zgX;!? z9skkec!!?kNC3+BFuYl(kt+tZF8OJ}`V^m&AWw!krL9b004vtx8sCsv>@HwmS+oaI z3@Z}7A4f{9ho>+v(#3dX$b7~(2<2ua zM(g8q*M}NFyeq$z>Qdac&gUdtxxlU|a!zKxd|a(6n>aJ2WQuN~nWhEGOh9!#tVNlp zuMS(>4%ZVa(~x}@>&TdO3Ic;eF?nmA{J^U9!XHrYuM^@DMmGf>K^}bCN*m61tW#!7 z^?Z8qd!DmhVGzkq{n-jx#tM8gNgKKOw#-{BOJg7=>xo|@g}h|mD>$qW??9r+4NgQ~!BE!1SSHy_y@xh}C+89+Na_P~_O({ki15YU zL5x|RV^Y~#2@4E$jK;Bs1S;MDSHt~1Mj@PSZBw8rrYk4r;G+ww1WBysRKCHCrqA29%$v0B=t}%Qrqu5cpV1w(l`g zfo0{af8lTpNe&a#C9cvVjOCMRA@Kq+`?tKTp9oagOgMmpv+h|(kHtbqdknIeV)0dl~jD!fRrn(MiLMT|n(lA~9r#G}DHmKT* z;^Ii_W~jV2Y&v)~BDf)|GFdE$VTKOfd}OfYvGPe>bGT0L5YHnRG2J2TS#w`!;}=mL zeiYm9a@oqsJijwU<=c63>0*x3wBvB4E66uY2!ReOA<};3UUgtNsQxjV{nUb+ly<_J zL1vKzMPRs13|Yeo>(Q`$2#z>)Do3Ed_o9r zbekuUxUdu0PL0_x`5)8_gHhHOY9sMVULc3FR`&j|s^fQzyEUb|+7EBh9WRJo1-TQ) zjXmo_yONtq#M4$q!!!W{MNk|U`uCm`p*sx4G9Nm4jwDlHZLDxe>j1Oo0TR8Mz|!yq zmdBaq65Z)6=Ilvhlkd;~V#*tK9dx2H(*?+5oY-x7P);Kn5g%8Kgyf0XFutU6iTO)V@QfQ!DzXm5^$J(E3de?o;f^Y zRSwY-`nChN=ff{+wSW0#k{v~J$A9CO|8UFWP|NMVa?9mQ zi+_MyelZK3_i-vbUM(8HE_7g?l3uFcLkU#;vuq|06mdn1>qY4-50tBGW`m*|0&5am zlAvWNue*MT<>+sMUj@+`8+yAQY^DP*R2GBZGiZSx)IYqhwb zcD~x4oq~9(s$(2^km!7b`el)Nc@3CD)(=xx;ar&+ayQ4%@?%d~r8>wS#~fUc4G5nP zkufHp>tTBIBql9P1c^U@Oz_@U9H}H$eXBf)v3#X?37RNieDFr1B~;;xGwAXX&?ugg z6r=pCfC}d-h+s0BQOGa^gyd&oz!KoB1UebT&z=S!qI6;&uqq1#REi zQbAm$Mu(=wgadieq3Crb-4gY-ZSwk_EZYiBT&1^_MNEKUV5&2U_A*`PmCHL`B#tY( zyyJ9X7TO~1+~yznHzi=J6nJwgC{ zS(%dchyJD0WpV9LCNnkgwX|o7U2}Y9wcO_HT7WdwWwVepam;D7Jg~{j<$AH9LmbF5 zC45Yo>~-D3ik|ooV9Y`SIbfSsv_`@Kj1SxMS#XTHe%0;q^s~N>Uh|>~s7bUbkx(~e zrB^jk6&jCQvNp}`?*P63Pqwq@7*{rXaUBF~%2&i(Avw$C@Z)~lT!(5I6I z-4WjG!lr%uizuD`xNd;od;3fNTk>vt$EVNll_$sKnG+9pRbu!0(=H!pMW5!=tUZki z-bl?aB*w41gYP_CzebH)!H|;fTr~Uh6FV(-DhYoE(-rV8Nyng#v9-sSjrTMyTqxr-`$D?LT z^~YxYoaC{+0E2kt=|ESW+)(G;tRN` zRQA{vyQy-gb^dkv@G#o3+pRur4xXE~16LZ3v~(?Sbm)9wlY>XOI#-?s8c&|*ayOTn z?LfGubgsgntEv5+2Y&t8wRz>iZr2%x>uaQK6Ps$&;IS?>_vHcFb*3>LQZ82#5`T}% zNajf{8cI6(`*E|!n@-=1*=2_v6!L5paRbZ4-NczN&{uT(dEQhck1F@yE7g0RI30ABzYC z000000RIL6LPG)oN>h!UNs{9_(`@J16^#RKfE$2efDK^(_JlacAywV_etXqsrX)6K zqH*^KKJWdu`?;_8dalo}_j#`8eV?`Z=zWgS=j{9UT650z`N8^pxz^{!&zEbgw)*FN z_2$nPpP$q#^Yn#7jXK4ux72{`E1g_k=Uy3*`Z$;J zIRF>&xu|`TxhG%`SdGO$|Jf&6HP)XaF*avt_Q_fa{`s;lAdYDsdCy?7Y5ee71Vv9FB_t$F4B z%#XwF0&|UWh&@#`%ftVMH#J!Q`-K|d&O)e#Q-AlMwe$1t*bd%5x#G!0)OulaI#YWg zw!MORQ-+NzT&&2NRW{w;&`eS-!m-yj3!RO_+qkoQaJuzb+-{0hCvV+d&R$?ay9{b` zPbS~%smaJVel`Yk#`MK1FkgdZ+-omLz(ljlt5~4sFCf;N)g&KV#a)ndHpN?}k)ja3<9mVDEe)r8<}I@@uvcU(mH zsSJLtvd^xngVYU8%*5W=?98>eCjRBl&fmpIpm%g;VD{i3->%O46^x3jz z>$~p^>w%5|uiMh5anp=8n`Qj?K_`P1mo=psTIaTFzZDR@T#r#U+$ctR2V5XHk_+=iJk6xz}XdZ(nO@WC=*tUq8VdEFy)vOTK8j zALAs==&VE51=S!_lrnnug_f@Dc4tY~z4k1$ik@Z}@})CaaZ7Pttxh>zW93Q*;e?xi zWYdj|zOHVTubLI3)ehAJxQ=6#d)#2{^FFc>KWAQEUqy;oQ6oU^c8tV~>$nW(u(_uO z5fi_L8@If}I>886+fR^JmLw6!V%mDH7EfJUyt{^Dy79HGCaZ!#x zvg5EM746oJu3ITz#Z}ZUs*QB!8-vi-*O4U1ATH~5+1YkA+qJW>S)6&YK&K|~n77Yc zT8-UwRZjwgb;7)gJg45g4n9pkMg^{J1kLfR(7X;YevMJqX_jN{4V*i>Y_h11r+1aQ zbLzUYi#Tj!*ApnmP_pdtd^It3Az44Q=A9~UpAnh$;W|EEZaLp8aovoc{=C+V+9r{C zu1paU*{bC+77_W%#+b(E01fqdHqI(n#m*ZYV}N~{(?d*r zY55BQ3zOKg?ko@uV2*EQ)!CD?VqWY|`TJS+)QE*O-G)(loZmFG{^n(T$e{j=;y&D7 zVefmrpI`Uq|6ncG^*s0f_VeTWy6?}6pMO8!eqQ~&^{V&2ZGUY4?B2hwI>P<=`2N}F z*XKw3Q1?FKUEh9=cGX8d2Wju;wP$~IU!He=d+jq_Z=GxZY#;G+oQsS4T+9CIZgrG8 z(DUq<_6O=v`@Qw%XMgq9OZzfEx3hn;|7-gi_ow$|?L)rYNB!iw>M}n6)b-!>#XjO5 zkEibW^3(fX-@sGv!@v9PIq|;sJv6Upxi+Nr`D%~!T?eoG9)f$1+2_~i ztF|w&u(*AlLSrC+UXpw7$_~zdx+A-gU8!E4ymLF_o9MT5s{Qta!Kad+&Suyx!`%&Tz163xKWrxa)9r z05A1Dt@&si9|n%O=&% zt~>vH_^r`;FdYud>!E5FKHOD(_PrSDw|3b6aBZC0Ij*q1XKF1lfVV5EM)GCNmxW-F z)Lby5wV>~^ocjVUEYqVIWB?zw%x96*#bJE66}#_C3w&v6?>k$z#`tkPWU)|M> zb*jt8~f*|?F}PR8(%+I zi--N=UM%y&LcpT21+G@B=D~23y~g0zdFvguzAw&No~i6q4OwG{U~}s_pSst&;<5#p z>KEs$Y2)V~M|ZO}%LK5MHICR~KU|9%+xoDxpY3At)V1WVI7F(|zzEH=~ZUR@PV0rP(oyOdeNL*0(g z!}RXu`Nj(xBYaMLB^Y8J%a$87VEx~+KJChFJhg0YcLtVoFUqHuXx-JDFsM%V*%(A& zPN?As6Mnla>R>OU7z9a8|R)cOA3(saU)WbZZYdVNK#`0x>*Qnfn=p~$F0cWYyu6nqb+8V4LLg1H$RYQX>B&JFDu!FeU z+r1O8vJmTH%eT1Mah(s22!~Q;i0JJ)X6D^2>5U_;34JSO)~zJI+(F!h{UZ8urTPAf zY!Vjo6=!}it~mFKo3dn`;m;K<5;6b%{WVEhnh?4K@5Cge@x=;C{QD;=tEWveBBYw)E~`N8wUuUB1YrgmJjdLd^>Dt*M%8 zhy-oCVuFl#mo?`@Y_J&Z#b`sPGDF2iL3|Vl1+>2cJOe6`;ZD~`p^uOhGaOwZ6}3_d za~LeGbtWGkdw40tR2V`U+fNZ&*go(jw4v{RWzY!X- zxq^MTR?)T)W+Ruo9OnU>yJ2}y1>OwW2LoI%hO|!(0S+$m^VyP|RPcSW27nd@+dC^%}L!$}slRj`+c+iVlUBj9f4 zL6pY}sL7j|!`hMi3Yw$``^>9idl8%lJf8g9WmfA-TGSXK;p>p#3x$U_C9@2(UjTh9 ziIxX^9Tq4u3&yBUx#n4@gVoVyX!sSN5*!>#FckKfBEB*eSX9`4z&9Z_xQiKDWk}=9 z*$;j?p;jBqX?8!U#B;nTc1nuhsd_5@j6^&c?_A~#Z@m7LS|nB1b{oFGPBqL=aJWmRMqT$MN-L$SS5|*a$Uim#@4?sL(z|+3l_i67-ozox;j?;G12ho z;mMUQ@@iR48dsZS3n`7&oL5jO;{1OKU02K8 zTrWOm5V##rfP|pyDT@S|Ox)_BQrWcVekQsqHi+ZFJ#GbJtocw&RHh6Ik<}P)HI0L( z(ez$nVI#|=V|SO%7y>4dE?LXcwCt(UY-e^bMT9`}+%K+b!Hts*>`%0zWoBHN!mgYJ z35Y&BK?!Px+8k{pob8MhIsk$xr4wL(WCD?W)U7t9Ac!6DQ7kKC8GN~v{^yHA6vPD@ zR7Jrx@)2bA0KoO%4USnEHQ;N32OBiK3WE|^yVFjSn8)WB^2YLXiueUuC83Ice zWI<){VRGt2f*1fpR(yD*1G%m)6dCis$;12cGG1b%k> zQb|VC7ZsFgF$s@}Pg5qSLtCo53JPFXlaCRS;s{R4+ByoO5t39LU;m0ZybyF}9vYj_ zHsbtxEr0!cNtD7Myl`Yf`n7l(C>BE{Y$d%}aHtraP_7&&l*(0$kR zK%-osc+yobBozO#p>96RK6v1pt^jWrm?MzqdxhW)lKimL8Wb!SjYWWU0bIL8;oQnH zLKie-QBIe@s)(2a%(ejKek55T@&GbF?`_iEOhIVV!k|Hhv1TCk2PkJ(e+KN5riI&L z1=MB{j|2I<#r$t5V$RUQgB2rtpvpVgDcGWbcjAmevIPBtus3pdoTj2Bd>!e~a$gB; zZ2!CxYDCXVc!>x%IGG3}|AILJ%kPVx>;LDs`OliywJ-)N89{xn$OZ!fEY7?zKSbbz*%ShRkw$XTb{qwxX~Bu^!NN?c1$)HEE4;|VL!9j$|X zF102{vqXy33`U1J4f63$a)S*Mw_4cGaq@`DpJxO}qG}5NK*bnefQ8vt!a#D6mH>B= zMF7SOqi(Asm;^?IDI?`=iwJOOYsriih++3v-p`2-?pM0rWx$LMCWRF0fwbNwx&=>j zd@F59q$$b^wBiX69Yyan84$3WN)VE#aP{4J z7q9Fkc#lrg(;T5C^DxMR*NKeX43)*!I4n0cK+;sZR!c{`)3IBI=Hy3B>mwE=d_NS| zuE61L@a#DTeKt~pA?&&iv<7^)k}737Sj9~IHobYNz==hdo1lWJ!p5lk?*6QD4AlJxCt#IUCNhq|zJJxS z(fv}8wuT`fScxT9s2HR_POd4ARET3clG&s0Q&VT$x0AerJK|A5=PMj-8Ua87u19lf zmK8dF^=y1$Y+5q_dD8wrGZ5Rd>xz~;|r8JQdT1@0Pz#~ zteR7=V*MeW#YqqIiSm}MHbc+x+_F7%8go5i7Jg7~plmd*U;M0g@33ZbfPF;JdzNLL zZJXYq`@%+yp9`5ING~vi7oyCjV7zY3QOOXLO)zU9(2)!YVC!7x9O$=!#JUdbXWG`! z-@ga2!=TJ1Di0v_hyJ)HMC#!x4!K&F4Tds>L`Bq)kNaY(4QhxP4oJ-(;qf@W9fQ*Y zgofwvU}pt3F)#;Y4w*bx`iO0a?pXQ&s-}e~!X$o$$M~Mk1*{O1;Qd+lr@ksHxWX7A z{to`M=Ap9%uOVv&fh#s({z#AA6`Ce*4R;|avLP)NIdKxfVHYmPm{_*(EnS&FNP8Sw zKqCK>ESzl51KRduQ)YO-ZNo4!E8rLK)3o##4miW zD9~ZK(E9Tatgs`_7g_dr7!9PV>ABGzM*K@iZe+=#Bo z$Jal8HTEcc9BA|U!pk~SV|cKVdc%xnJ?&dp`*!~v4U9f;96V-quw+tVb+(N{kIxmNS6*%3^{-s?K1sju}xQANV+~KFkL4N zG2v%ryFvz+dSn|TB(UYY&TH~1vf!V!*%cETxjGU_?C_6+P(L7)mz4 z4tj7P5!)H+a3~?ev9)k{T$=l$>$|3B(^1u@&3K2Z4iQDM&iX_x=&AGdF6zj=?tsNQ z+xim4U2v9QO_2q`paykm=oV~tTcP+5P$ObTWM8mi5BQRQF(+L165R~AC7d%u#sd&1 z94?(?4PA?QJZQmCS+E=-FqScrV2MB#5=W#UPR1qcCD=#_U(KkO$wpmG8^`q<6^w(= z_mgv#$l{{Zk7K~s^d!dXMCd1Whj8fa`^sO5b122X5@#WgvSkhb`)Atx$1kAk zU_Ll%H&a#Q*4fptc2mz`iZc?-XiK|tnfau9S?MkgR%DCF|;96}c*K?w*1EK|9eX5f#x?Ew!It$>yF18fS?c;MAUQ3=p> zC&yR`>x}@*gqiU=Xr-`_m>Ne06kV|XoKBRyi_tb*Sk5qSY@EggZ-YPtH#c zjjy6c7Lxf+NhXltFbl$_Bo`C{h7j;;8G}xAEU(zPiJ^xwqy@uml7~)#n0N<1>r!kp z3pyc7$MT<6G68^BTzfy+@`U)nW-d)|uZ&I#Nt{bkft2CCtTuubr=UqOO_e;}k%&OC zhb)Jgw^-<_dVmV1w(;3j$dnMJSU07dH3f=#D5XSBL~Zk(8Wdy`?`s{}8|{kKn_k-c zSVsI>6a7%Bi9m&lCe;opz`Vbj4$H8tWUHHRx2XXsK9K%vQUA3Rf2v>sA&9hkrD?ypKhUf{Io8bmTbPJa_k{vA_wqaM7w}I{y zeUgiAf&R5KCh+(<|S581$rglc>&q@ zMHu5aE23UALn(x{5@Cpj9N**_)DY#}sz`6S(G!PN*ie_-1lWQW46=Hlur3_PatG}s6(=a6fg+bWw8_GlBild-gFkRtn{*MRkTrE_Wsdl3FT_Q-gw z15TBuT1K>I6bP3rvZ;uA?ZIIS1cXmmPUKAQ8&iHAWl^&TwK@_!YSS!d<{*rm(xj4} zMIpF*bv%j)OAdKi%YNRdCqTvdP`ORP?}Gvn36;g2_nRttii7VIbW>kSJu{69sbszCt4dt%X)W;oFnoSlS8e4c)^r^g+ir^}qe*DDi1!RWl^N?N? zDIk3jfuWXbx258)zAH7Ex5u-i!coLM$OP+fEpv|i?54<+;k;A-N$K%@TRnbTg?`~; zEk9@d=>lK%eVqI9X4HeybD&*z2zmWWI}eTL{v#r2%^dL;VWfL*RiO})JQH- z0~(ZspPG1PtD*%&q_-8t59{_uHfisgm;M`Uk;E;JamsD@(-85VU2n1OYfbs7cUF=6 z#5UBFd_Jrq0Q^W9QLg)kkDcpJK32}1JO1*qDgtp^`p1JLj|8BrT2J6Ygn9qP$Ksly zzYi3>e+XzAClKaBm5CVV;@Xvwq4K15GNi=NxoP$@5Wzp~ubgko%Ik#SiY zK>Lca+lt}o8x$+y6~Cz&&{5Ikqc+ivC3$F`LSq7sbK6VhO}C124X5=0MFu7E=Z@@( zf&Sf3**vKd61vmSXvF?mE#6yae7-9A8{#a&)Dq@q9o z+yADg#cpo$Y6@dUF~7zOT&yyEydCXk>Uho3y2}UI%A*l*wlQ}+{E5P_Ti#e#!VFPC}+0$fK74Kk+&MFnm%<#N0hm~7QXjK zKWnCjJfWROBwENJ|i^(=k*x6|G-_t@vW&VvzB~aK? zRrhG~FnfvI)j;cc?h}sDbtDZr2v2K~q$|*E^QkxU%Y-6tr{1Gd>$=U39& z!_TS&(Tyb@U&Y-7lA1{_790%3#T2$_iTO#1 zgm(a5z%P&fZ<=I^45Eos+pq+@rfy$ECm?>;NhxnD4+1+FrNtDyk2$hhfQYP^%7ZIPhY^#^wa43fY|oshOb}dP-bm&a8ak6GCRm)a5mq?oWX(1v6dbrW*)3WvHk-(p~`?D;Q*{ zH-Hf>c~rJam$O~6D^o-OXHimt_2uvSf#kgFLy%>$XyMp1RGWLpNu(rVvQdM;&FvBk zcfrF}mD3$i=EbL7cN&yYY?%l%Ne=1N=MzoOOJ;PXDdR;*nMP|@a0I*ck*_|%Np9O; z?TCo1jE-sdbKBQS03msCf#b=UO~MO%d7hNr(W5Mg$(u^L2e00^Mrs4?8bt##%h<$0 zPNPc$zKV<*(F9T2`zF@5=N-6Ix;gFfNTF*5$;}Rx*PSRcRcG8Q;d^yOz24-a&*}SU zceFe8r4v~gv zN~}o|Kmje84OPp{;^X^D|3BN@5rE&}E>aKcv_Uvj_;>DZ6cBXh*bKn~!_vCSysr+}Czp;Cu8B+w1u{VBckxa&bHL z8dT0>U2A%AJ`cEOq@|9CJ?4mqPmKHEG1lR?$e$5W$q8V6M*Oo8K~E4l!V2#JY`B?n zaf>i*jCjojE2zpVDo>I4WqAP?vEcNLy{3|Y^IbmB2j7l!n0~Da zSLk6WweJ8dXcw$o)6^TtEQ36i7=^8u!eJ%MBM#`iXxOX(N% zNPjv^QTohX!AO7V>nReZ!|zUkH3p|sU*Lm4$CUJ+R7+r}VvH75v^oU=_-KeDv0M@= zs%4Qstu~a9)l{M_8xbcd2?N19dO}oi8_B6A?W;#|nD9h-8}dihJaS?(|8vqOPQ{K& zDsyyh@U1>4680)@D_A=N%RT{ zzd$^eGBilAhp+;G=&?QLl@XFe;(xnI6hsYl z0gi-ydMKE$5m2Fplo_CQboH}UFR9ju3{pXxa*{QoZN?EVRT%`%7MSYIgZr-|d`d#n zmEKau(iIiAah#btLS-^hT2s8ND3h+buYJ)sYxLTP!Day1^EUv}eT6f$UFm7ByZe(= zyl0yany=d&`ZYixviZZ)Xt}|4q-|!n!Vu9hY5=+*RRKbc2+nw-jyHl_&ZE*8 z@>i&Fl3N2^8Xk>tLe6R7b!56CH2{qZgHy%ROTBYfoXI{Lme@p7p*ju7ReY*3Qr@Zz z6V^_UtU#tAMk1(G$ZE<93c`a#8zNC(-cMcb`09cD&!^5nZV8U4I-lZWj-nPNTC6fC zRgks~H0Vks^1`Qtg6n_$D(xeo>QQ``xa7|m*4oFCw$hKdwgJUi!#cQ;LXWAP`ZI=& zjxaH7OAs3+SB)1jY_3-ct=QVBIjJ5Fv>Z;6lC=}l&E#SsmMxK)bpkcoBAS1W(GsP_ zf?JZ~<{HY!%1fdI%YqVACbMI`hL!Ovkfw_H+m9d(U~1)Tl?pS}&Q*CHxAa3Kv&E1>evtVbV*e^vmPlYOf*Ax2<9&5Q&n$| zZhn0c=;rD>I}b19tNbZz5=-g@4)wxFNfgs*47t)(G2akJV8Aw37N_VjTllwfI-Ki_tvw0 zN8~Cam!R~qAG28Qjd3y37oh60d#8$N=6-PIJ#s_~)CySAx;lV3dgCI>4RCA>#!Q|= zrqQTsP_WVuLA8vm*3XdxW*sF@Tl(H*VX2!tf%@mCp9T8s8%M%oI|Yx!FaF0p(%XGn zOfrrQn5JX8O{7G=+8a{fB`i#}O+hb53s6pIU_bsbp2ubU)>Eu@< zg)VJ;No#l--Z}c2@Mz&VdsxwZpmxd;fPo~0IUj(-{^_?Jt8i5TJIxl8$fdz+)8_I# z4XA>j_lwsVJ``qf3l2Vt_+AMxAJ+4z7L~`c5N5yH6rDr;r1dSZf?A)~l*>U@3=PmA)2QUd*3 zANG%!Y5f^9{e(;%1fg3;_x}+yMXV9Uhapq@XUx?9ikbdL$khJ7Vy1*l2KCw7&CMIc zQW8SS8}VI$bM@Jm#U9qB&vnaLV z#tPO`h7h^HzJW)||9w>ip5ijQt5!OwF#eKn_UV7&((-6hrIru;HMIE6gP^3HA0AKm zm}Femsjpl3iPLEO-qB>$P&oEQriSxux(7yM}+rx@v z@Qe8lHKIse5Wl|i(WjO{JWs^uH}-C$%xq+;noTM}wp{LO28pN*lYJr0)+v(1Wl(Jv zQPWDXKd$i(wPfPk+}MgE%GT7*OflXMXmH4U4ot!LNqH?bHN3FqN&EuMBVo0GWp&vE z>A)I{ynLK;as1^LC-n_=#TBnNbyPpU=l|F5eeW$`E*9#|0)LUCq>@sz_XZZhlZ1mr;Bt5ejPO?2Dl#Bml$2iz(pHKm2WeO^8pTfa#nq%Tp+%lFpT7?kYt!uOjY|po4`#sTRFduJ zJeJKuy*@(q%6u?3NbQlG^K-V*m#Vjp=Mt{DQfmcU5GsPfXPrlA4^4n(`i`_65}20J z%|Hc5{=DBj>aV}w}q|pt6!XSnOzB! zww}DI|KwGF!=xawg3puGsOp~&N*d&v)%1gt_OBM;%lYpMX}OY!U$dR}ZN>}nOAh=s zM!DbWc8*oL<3xW59RR~OyqPpAgN>Hvy&5IgBBPo`-72wZpqxbtq)6U2# zQs9Z;@9}}JyCZ$YDRNU8A>B_MInTW2qEk+;{w25m9`DCpLqy}lo89#<;ZRAu$IYFe zOAOwI*H%(C5yJ-aV^BodRpw|;XK2u0D;8%5}Q z&a}?xepNOceOTqI&XBw z1YqX5X06{mTI6%h-a?bTsw0?AqTtimK$Xd(V7nH$%#>POFnR@^b-|TBWjAekF@=?u zxkPE!d!Sua(<6Y8Lj3{nxI*v1%T(OegxZE^0)&#_ra=vvuR`?b@b{40m-(=mX76AV z8qUN-QX(tE0>0g4d3&6%I3|$51gAik@r7Te(U3ZfCG2Smo>a@uiKOdZlOn<{e!?^x zUyLK;TK-(uCPbrZuO$`{+!^0dJYn{$1>H&)c8347a`8yB6oINp$hqGjnS`{Fd zw$w8q#7;{>eogbV16u%4jno-5weB@IL8Y>oPv8nWJOrvyOmv5X&t|@-n3mt3F8!cE~`!=V*&dLOV-Jj&dIkU}%1Vv!TJu1;# zA5Q6O{On&pI)D4mg|wfa{vNxZi~IBYKmNA=^+HVD7;zR6Az>Pr=WZf{kh{+VN`R=v zyGiKtZdudjCer4brn<6tqsy83jb^B_nwBW5hKVgjz;)~vphi4E40YaIn_ZSKSy@V>a+~vNJA4fBJv_GteNR4w{k*ogYE0>`=NED*&T6w%$%t^ zu<1~zYH`HAYX5bJ?8xx*a^{*i^4)e^k!mf&%XlkKuKnm;%+n2ui|p*e8!ZSo2t=&dB!AYhl6rp-~*X8j|MSn_?`S zjs=FyJl!OcA#i7~D|BBJ6U$;w@mV*PS~<#Cpe81L?X@YuDbZGENnQCB0Ooz8uijaj zE~2f(D1qeS#Ti`;L=U|uWCaL2(Ly$ zf)%c-=c%njOC`a{Ekq)eJc|i6c&=M&3*F>8QP|3SmDywuLi`1Q)7(^tcdb;cM9nh@ zQeRm?A=bwD#!_MX<4KQGaJ`8-jaPOuGr`y<| ze7KKeLD%^*x`-Z-%T{nK&w1G4b?#M#ms!*b=_yi9x^`65YaQ_#sk}Bdc1KB^+h&^K zHeFlLh)8mJol6^bzw?kx5Oc|%?9%w6p7+K|@8Fu}~5f?~Gq zS_Y{+=RpLD?Kk)%xn@Q7{94ZKjOc$EE;77LQom<_P%&Z=dY9E^XAWZy;Mz?HIfT1s@!HgBpYe1j9!!rE^xA&?(~{QvWC0b4Uubgl?mbSFuS#KH$w|> zX0)c?JzJ!_kye$6N_Vr|R{RHfZKGtEO*-u(@o-l6a z^L_6zMA+V7MTtj|@nQ@*v9NaDsi+Xeh$f+0CLv3$Ddli{maojE{-ehn!zd*??LjNt zu1nMl5Gq{b?~wHbKHSfuJ-omd&A z6Ml=*ty4CY4e8yautF`sW7TRg4Km5}4=geyBnUTo<%!uA8QoU~s00J(i74Ex5-E!l z4@-T8i*e*tj1=Vco7$;6y%A+++hIy?37o?UGe)RS(pgDT zhIP893K|@3(`7YQ6iQFH9@OSf3Jkc>2AM^-0}DKNI|a6f#Gh@U=%L22)Lvu2Oqq4N zWy`eXst9}6hhu+n#G3>$*wSE)b#$d2*4-|FcE+6tb)e3DtYrApMAE-KiVoz#2pf+- zD)jN!E0}28J|Qg1TER%$M78_babR4fG9T+mtcd{^Yr*8;QPG{;BRQJVDO0YAtPKX!RA{PY9zV~pGDut!)7@HGW<5BDFx}6(_z2LC z0E+kb$c?AZv25Wlw^?Dg^?yG~53r24@`6LVuq%PhV`ydjl$);hH1AWkkg}m~uYz<#MpIgg zpGE}jq1&%AI|OoehgxV1SITNE% zT}LUqYeq5p@90bMuGX);Z)&ie#9PH~BlxhHg|h2gz3O%9wr!YjMTU(Q)c)HTyW2W2 z2OrU;!jluS$Qf)i-`GlcoN`;kqOWxA!kaFNKT>8;6k*AoR;+eEg{frHeuKYVkyI{A zawpH=i|~qF7u<~BYl@aE)E`8LXc3!=K&2`vW;0TyO97o7m=3k&x1n%>y~=i-VFEPTNZA?X zC1<(L?U91eU*z<^xURJHqQ^g(8KAOIr(M2Im^n3sJ`zIYm zy*T?Q9iL!A-K}+6X4qL0F1uOUaChdB+F?ilI zD!^iDXD+*F-ANBJG;gxSW`=u3N@mCPh$Ri|A9JVYw99$u6il{ZR`Rvk>L$~A>`Ld6 zlX}v(!oCc(PTuVI$2MzM$N(j)gd?xb5T4hfiN`8{6xW_s#+N-du4vd$!|u!0py?8R zy2w1{Xk>zh{dGq4c#q5T$w!h*KQxIsitpFOt08lPlp2rZEr}-T(D+oRhV{mn-S~ie z>$k~Vl2fCmR^VE9Y!|xwB!^~R2WI|rNRvxUOTFZ9 z+Uo0P+VII2DX#%RgT# z|A;t)0Ax%r9ORjc*RDy{CS1Jj%~j*B#eANxv>1d2{cr8Omd?#Ct2@fj>>%$4+eHzGbda zEt&S2ImdIyar&v-knzr@HbY^@6Thh6w&A4F8TeW-n-m)v3tnd+s^_>%^AcjYZ6btb z_Jmo|nE|m1NNAVkIc#WNXj+Mk9OcG{4Op2@*=t z+bA`;s?+!T1}%Bg^TtT>RO&yz_)_t68z>&=k~@B$qjyO={p8`^-YKOCXIm zKQ6np3w>Deh7LF2I8;FoJUt_UFzPCvW9BQ8o66P(9qJ$*|5c8>VCI&3R|WIx<=eNf z2*}KTtC8{Eo#zXG_3|BA8;nO-2QT%%a(U&C(aUe)q+HW~^zsskX}U-QKxG;KTP>f< z0OdF0U8gq9>W*UJlsnBM-|zD5L?sdn)P*QK{8kn>K%=2jFIu0-4eW4Hbw%%Kb&L-|IYD=uJ%VK0*h2r^c4Tgk46yRt0>U z;nhC6Mf9XvR;OuXUlISHADMye7!4RsdW%H6TI1gg8Rwx8p>5zf1&N@h6}W{zT6qxE zKB&oNXojO|7tbWf)9xC$x1ibqO!bNvg?=}A+-GnO=_E~Dq$cBDUxRMV9&V5cagn*& zT#BG9XlO!}!v_K#x>6C?sAeoWNv*J3vZCrs2HMj_kaDWrAg<>@`T9_3HSGuyMoCF) zH&*}hqw1nVb2hX^OKH^<>>ZH`<+5e|tg(}9Ympg-P&?e(Ne(0gC;Zs7wch^KLUc3u zQNXNV!rYv|FrQysS!o}aNh_~-1LVLG%`&_SzP$Q9r#uSO-CWpZ)~lf3UI~JJ>vX{w-9Gx7WpKL|@yp04IB|3EJYVZ0RIz~l#X+=x!hVeM zq$$Msf&Io8^&5j;T5HaK!+xm6tom0|E6t8232FR){KZ=yMK$9vkn%0H^oO#YvmfE6 zy1lTc<4%DEwZea)tv_bMxXjnMM8>hru zvB&KZ4LFsI*EbN8z!E#ZcHpt z`~@I4T%N4&}%h>0YfS4$kHVg+G?#i%K@*(-GCpOSlqW<-} z)Pc+|ck{id?h2qTHdfp~aM~E?e{eTL@40BJa$IkB1$|Zm4K)wdbU3e`;oE=O-`)@2 z>qi)NIIp-rxU>qTJa(5vBa9!3Q}FWZ%ZL8>y?SuWUj_e8-CussA^WAfs=VMobk~~e zpMG!61Yo$3(r=F=>OOz-ujH5Q6htuZtmj5c44H}G5r2Psp%JMZSxk)-SPv5OBdp8r zzHfyFZ{!Q?RB_i`mmh;X6P#!I^{=6(AOUcx{c|rw)mrQSvRBo zy$h1xSoPnt=*-bCZv^2eS zsSBC#6e7~7pTe3X_al#ELOU#D^_NvbOg6qbJf-STzXOJ3`GbFX#Q#r<($(OY-JAU(@Rh5l3Vs z_aR&BJnfZtdz5WSd=t5I)yOXiWJtE?=TX7>6VdQ1Jp!=>@_CHQ=6!dGZ(! zH3VMWE3+UaoJ_Y8l^gOpN#CWQ2u%)5$DY$7gHHR*GI7w+lhxSw zWh{=tYRWD~7JFflOXIf?djNG?0tBXAh=4sD)UXR@hnnby%;4f|cPhn5HyXyq5)e91 z^e74^I!fWD2?#k7*<*SmW2dg^oX=j1^%D<4G`$j6qfG0%1)whk^h3OUiDB^p>kqIh z&00qTzb-;=1jG&1%d=aWPsCNGXPaIhP=`Y|<{7@U za=T2~(so=K6D~lYYPYLY40S)DO%;1zQJLEx@I9VDjatMgvG!JgsE@kQ?>~{oJa0%O zKo_31_@Hv<%9N?&M(t@t(@#*GM`7H)hQ=}yITTeC96IuJo3000eBzPX#bJw*M+0=9 zf`<1AK^B3_{Z%+zG-D zohD1^D+S4XwvoBIfF;KxIhIYQ$6EVp2p-#CmDQ2-e~#!f5~RmtmzN}>P={1*2z(H( zkUW}FA43B)O=&iKljR1$G z2@eW1tH$XCDaZ=H6_CDkAmm|ocXmQ#6K~{k2|*M@>@yJxS8CNT9^)k0hcF(5+_;}X z-SsKT#c?!?^~9ONAe@@69tKRTh%^fFeK$iuQLhI2^E>r1NNsA)gKZ*;Qu_AP3Paze zD(a2U)X?xo>~Tjfl8tfSKJZJ&br}IiuSowkvwSWms&^ zI}>k1$@ZQ{(YgF?mfr0~dc*L(HW99;Q9?G*1M!Q+{f81Go0UfW0}`Z+YdRcy|H!`PL*^$$sL5tCPA*V`?YibX8yx`GzR!zW)3?Sh*ufO{JoC$|IdAZU-us~*QUAN|6YzO zDFYmr8ioF6g0j2jXWSrhFa4M0sSuC$f-k_qs)OfE*(+enfmbfM*Gu(tLMv?88&Rvf z{6(lJDnlS`BT@*M_XO|7pxE;JN?%~3qThy`+z00*t}{)e{h2 zfd;;r_Xl&HM4wuV+v4GdrDwn%pAd!C{Z8RwbbD#B!GCh_f|$&Xy$L&-~NdE|L?H>{X6UrM<;5BZ}?dnhb@#({3Gmd z#{Jyj6P)Fh;@5aGs^k-w4;$J{Sy#w8SNf9q#1zDQ<4Y6?+RH#~n}lu%y-v4=wcG9k z@Ji8{TDF51?BC5KAA1)O8!}Jd(pRzuDKVT_N%<63+Mxq{0uBPl>kTd_6>|a#I*9;z zQ6;Cy8_!)d?T|nsKau|&s|9Mu$Nezf$~%WLy`F|;78kEYWBTxTJ+S=eb?mQde+#-E zg1B1__{Ea2b8qB2xA*}8Cwc2_EM)G%guk?he@*fcU0jr;2ypuxIPoZ|-PIFSh5c*0 z^0NnTzL9((nmmUkX|7>pY8&M{_r8o#D!_Q>GwxUJslpg1bh zih4`LQXAy7@f=@}7)dW>%2S0m43_Qf45f$LbrGE!81Z44!R)G$Xh_=u-rb|99w(Ms zBf5XsXpBO2Iua_M*S8Gt#eSPfunBZK;?5U=PkRqDb19m8*brChI$r8~Ov->~n4Ct+ zeyIbzf_NDU0n?Ex%78eH`yDNq96`@>$R$G?W0hBCg_4i==t-DmgU+#zr6VRCDL@Y% zkMfL6y+Qr;r~<1*A;ZE3*nx2DR?k^d?iS7V}6#V7d=SetI0P39Mf42PiJ9IgVBs zj#{w}%qn>ZX+}a-TQ~M}U#+D#<()^EQ1EBCi*+)kWz-vSi*>tZIx4WIz9(R`(y#5S z>{OBAIGQWf>$?3++>A(nq9nQ(n8zu?x(36B5pIs^JJJlXT<)c7OVW(mKs)RkV8Xd zBWmI5S2psnT4ak9>_l1O{3wf1tdEf}q^k~!Cynl=xEuVlzSmAHQs5AKJwXQZYkXrx zZmrn)Ju|nwoY*xJA#{UY1nZ;Nk{C-o} z(NnZk6VayHf}T|N-#We{gFKksUfYq!{m*1Jn4S9du8yz5NBFelvVWzseHq=2o@~}M z_V2zGe`71Sn7@U5r*FkpX^pRC$glbH7cuu#En)V)REsxN>F?~ls$s0Uit7XQcx_|^ZE5R6!^9|=L8B89M&T1wQ7e@O`DUz*!)UFF=vl^7>Vu)P6&27js>2qBgbAmYRpTqFq%_S;8JHCW)J>P z0#b#<$b=N#n5kJo3P{4L!c~m#YAtq9@}uWHgqAb@X6V>)&4iJ)G!Q})Z*=1}Kpo+k z+TugCIbto3R^D_q_3GUi#{Ia4ap8&=WDg_d;_d@!x!u|7)3G#ing?@%d`&b&PG7jXGCc|W-Um`Fm>7K;2DUil8Q>pK`tAz3A13D1m{H}>L3;_Fye8r zGztQ7THaZ=FuMr8u85gK5Gc};;DDwv!qfIwKr^+r4GhiCW6cyLcb2z`0Qyi1CpVS{ z(2E+a3qt08j{+-X$E7So%)6a{0MeE@jTUV&Bi^zLm%HhhgF8;Tyc)%2wIp*aPEi>Mh&8+P;4l)X(i3f^-}3GiC!RQ#wFA#1@z640@gw4On1MC#`NSX%!NT^M@Y z0RI30ABzYC000000RIL6LPG)oBU6>VNpd7T&vZNIuEIFr2Dkwl2iySnZ#Ri^z(}d| zy!ZR7QI%4e(Xc_|?m+CZ_MCIXKVyyf+G_8gzxDb2s!!{!)mook<9B>hK0iC(eO~?i zJYMbR&Ex)Be}0X><@0s@KmGk2|7o97eXgc_F70!&^Q)eVh?hScd_~{kbGxt3+{~sPR{tP~~HfNolb51Aga}%S)vN-n=A2{b8Gwve3 zpXbl^s{G{OHa>?f=RKeEeZF6xUvX#WH1oWdJ3iO5<7X}#=VZ*d=Me+2*PRJ&_;bk5 z)tzJHb;VfD!Tf#B%mJ65j1gk_IXq{4KL4AWwKX*&zDo!8k&?fj|d=;!BWp~ilCIAd++o8Ak@KSz%h&NBbJ>HPe>SkIOl zE)s5S$5ejqx5NPC+@2rLd;`miWwT>Dx7f#LFVwik5}T&pyNc7s1;>Wz?xW8OpP`Ed z{u#j)%j&a8=2`e>J%%Yct2`#{%>2Cbe>TQu1Dt=)6rOci&UTvTlRLIiEXmI~&a8ZX z#g>YRVyU~tVsU>y{CVR(%Wbhk=44%S8GGb*WM?nFiDmS;!efTwtnr5Ux?_u-OP(&i z6&E#Q>xONNPmJ^7V=h;`ZN}}hoLA@(y#H| zKZds~w)yw?&a;KQF-KfGwC7@gx$tsFc;6A;j<8mTU3_*1w;BT+V|>p1bE0E}SNu9N zwB8X83->+1W#d0lG^Z#0^f^)-)LCpLKE+#(iRv7=vqzeKJboVb3Ri!2R*N%#_F3nu zz0^2vI!0_@3>+}3t?#}b>`(AY|Yp^IIzScK=Yr{4OU z@0gIV0CBh-mkb(08ahXFh{=F2fl+mIxt zG~%Nb@fEW+;*R1*3*Wf?VyCUiy~XEULNh~2_hu}NUBW`df=j2oCeci6e~bXjaIy$v z-D&_oBR?3~IBwj#mDMo=tnHh2v9%Eldxh& zL@}0;)52HAT3?eGEv-14CN8DK4!}^xC;5hT6gSDLAOD055yUN4+l?1|%YT|*z3_Il z$4~Pc@wMJ;X1MUD`7P;If5Z#l-t1pzMz}CeH1s=Oh$CTmSh)K;T*v{7zvUghW$=ti zi9@r~VqdN>8VvKE7@ZQQ!nh3<6E_qaA%;_qI-=AQX+3~@N?m=fbtY~qNe=ED5EFG8YE)cZQ_lcUwl;~tz4?tutOqaH@SN19U@ z!a3fAE|RmAGe5kLIQjcc{HLrt0|Q6)UWiUA;U;akG-eLPLUA)Tu_+41?`$%7+B zKEZytrAU+cK#hg6FdfC<9Ph%Sh`p+%+528Sc?)k~uM|B9zt)`EalS79PtLSWZ?!v- z4*H7us*#CH3|kaQFGn#iWEq!u@2?YJ1DSAQam+67Te#jGAv3vNtVA|HhC>fY(8zaT z8VS8))s{>hG>Bw2?2O4_OnmW~kwHxNOyNLt5&^`{h;6+&AUSnz35;_1p!1G`*O@rr zI5p!nw!3lia^&0@(V6cS64Ts=LpWe@;_akq$zu^a>gV!`kt+9$`;9Hc#!eK0|GNEl z9<*>`b~pBFtd(1OdC51!c6FFL78cj%bg^<-<|nR;WjzHO{^NJ~@$JB=k%Ujy99Hex z*T^1Xo-7w4g|}WP&?ifHth4(&lUfV1nTen^kc&U{=(A73cV5k z_#ZBZ?NI(JMa5Ga^#6y;Dc_mgzY#xagSK3ctIILcpMRvNi=od5?TiD%0_9jGoC9_k7%NvH{-_8w5rGgl4P#$cFcHnpXe8DE zIpRU3;+XM%7Wgy{%aE5*9wTuv0E=_3LbzhUyYqBuMV4?Hp;j0y{AWb5XN5gmFk}5O znTDo!F4xywmhi4W;)ne+ejo(?cP_Wf^QLbZKdhhW@R|k(_zI?Eo#Y!Ux`roX%j<%i zoCM48K%DNp9BF?7bz*fy+0F-1^I{#E$;7RHZlh@8y>ISdje3kJt1oOv7!~uL+~nLyM1BS1 zsxfs^wpfE7m_{Bleh77L>=N^z&BsDuvI0Nc&#fjw@aT1`!iqV#i;v!;` z<0UhZVu&0b(%;Q8i_I~Qm%K4OVOf?0KxT7&yCPj8B-Pd3mo9%fZLMPx;GT z73?J$TgGGj4{k z!Ntmq>Db_8WCL@{nh?=OW~#Xee4m5PMPxAeW@YfWbIpk56Ji+5#)=YTS;P#T^!O z><)5Q9;w3`IA3HzCpk(}YMeyu3U@Z#W`TRc1|;raD098ncDrqtAu9|wQ-(;x1jv#C zT1kx&pM?GS+FE1*;U$GvDNbzzo8|2@!zbZJ55L+mb3YgP&eY z+;njmO{$4`hOr>f)}GkRaY4jwR#SwPaQ>`b@~L6KJ?>Be?+bClCIIDttGK2&9%+In zZ=*xQM8`Ll=XJq(+4yDS4#slkrl6aFOgW zQEXyxToUv!t2s~*F{(2@Whc_a?!#3R>#_lu-7SVhY)!CneAN;I+L?Haoajy7yOTlY zs9Ygf?@ph4#%s**LgIf%3Wh|vK#BS1D;~Fww2adV)VP5ggP#7bFx*Z$iTy027a%vAT)z0)MHTA?=P;YWx4>a=g zx5W7xIAE-eWXgP6^5aDt-^(rzQ3FsDKWqSD4n_N@_u|5F+&TfPlzs<=mV(67;)KRt?~YYyvCr%{npBa zp|&`K5&;M+?sM5O7}YzE)9_8bq~y8NiqgV4DvV+y^40VF!Zj3{Fs)d3$MWWSxsUR+B$>P>4 zH>ilWSbWm30(3*CP|_rQz4m|jTf_4f?9m=Gl}T8->GwhHHm(RBIaWDSR$@Zcp;X>b zvCL;FouwN^q7 zz_+7pU^Bs40IoR_Kp5jD!1%yzMr}+YB7eQoG8{}Fu}!@R@;qWVugRJ9b_vN4HIr!p z^}+%P@C;*(Nc$%~WRJ56nxZ~;PUkhY#E~k6g$io@7{D5urr~XF7_d*c0bH9O0ZgxSHzbi)%>+CzegD_*isB zyB9XB;@hiAuIisw#k*U4e@+@sOVfz9@TS~ojBH%*g8^rR!x*&Yl-`NT+hte9k>gV9lbjYe zU-06OyEKhdOViU;TSAS+N5Xr@%cZ7`3@66Cg;&K0Yq#SVxrQS6I(HD4t@Fc!rJjl@ z<9=e~apD}3K`pqphRtZoM#5o)dF|+0W2NvZtYIwoQZq_v`1q!-uWFscM|H=GH+4+- zS)PscqO&7nsZ2TQmzb4$X;B+_aRuP=NFh`+#?z~WwJ8`sWu8@| zjFZST`|7%y_(S;l1mH_*Y73P7cG2|Xx*#7@8i%S`$#UV}eR zIC%+DfpSRLr;QA_RzgM06*wXjQx8L2i0u`y4*H`2xTyw3gkUTN7n5uaw8~3kN64I_ z8*$U5!Mu8jFh)UU=h6T(K+L}*NA9G|W!+}05Pa^dvttgu(4H?$aFe>t=ZaPwp z3>v$S_^q*@n}(m1u$lwL>zdYrH0NQP5If=5%)Jcu%qL0k*ajz*QkxwSO2mF_qt-wG zw5zEljV9Jn`~S8=`~Jir_R?%%6hudM}ZUj6HDKX4o&k=&7!p}sY^0* z)%utK6O}3B#~hvOOyhe!hSFRExtclRBho?o$lQw*G(3h{9z%=PL1Q=4l`_$wN0_7ocjL9ObI2M~19|Pm(nX||*MB86+iz=)+kN%5dZXYM#SKS8lFQuG(xAVoCtI^a=Dz>u;Zj|&J} z_D=Cu8qrK}J<|{^j?9oFh)V}Ac?F*cIVD_zbO0>WGIA^OXvg{#_=#(t_bc|4Sb#{} z0JPi-sp_DlLXgs*PLGXp3zCB~Dv-W<#R3vdHPKQ!cJ(Mqw&1enzW3(e^8L+^zdJK^ zh=K|Iy0GUE#YGAe%0Pzj7lkQ5h#Vulor0@^cK9$EzTu0)OtvFj#ncW@sFri;a@7}t zlVbbRf~k20H4-oCZ5F!T%VI9X?3c$(Sq|$xCAE1mDvG}c^Lh>W0hMHaet|kbx*d4O zO*So2kSB*WSfO8pTt$5_e+bOWQslAW4p)Xn>% zJ#y-V{)mQKtb}fM$sgf#8UE~hofVq{$Gn&QhXG$wun>4EnyiReP+oOkkOZ*?P{oD$_(TFAj>w_ zB&rs);jIWdRKzbun*@3)Ocg=RWXoO=)ROK0N6{u*MoCjjp#J%_sFzI2bz*3tm@r)m zG?&DZw5B&%#$EISkpThjl^%`#T4sJabEkX;tks=VL()$9x1w(D2`xVf>5?vnqr-cW zb~K3xDs0S-nng8FxZLoXv4D&ImZC&FdQ*O>e(KJ!3YIs1FH{w%gk7l>&N75C4Y(Z` zRcA^A3zM-@qk|w|={kWbu|M-#WGeOM;j7#|WDX#u;G}ZkL)C-Zac9835n3&%skze8 z%}WN(RLd$u+{($IKFgJM&Ljb$>6e;E#a-y_hGH>Qyk#ru;)=H3g|L5Q$S|ZNIisZM zjtaiGr={Vgz$HV4EzfVQ1lht+m@;BlrD;_vOIigwye&70Tb(IJRXtYbcmoiopX7^U zhPGkm3SeDJwuTVeD>R2m*$Jr|o^K#Sh|5u)OVm(wQ+SM-(G<#|v6RECgwVARB47mr zp#%|^Fy&TJ48qcLn>XOXFcum8q2b-4`m3z&QQw-L?0eQ`BA2v2v0_?LbM-4p(;bDT;b%B`#*lq zKVCz&u~LXtGIGc;hFDOA*UOg1ZMOZcUQ6S8*@tm6IYi5X479F&(OOND;IKo&$M?H*cK2 zSy>tpq5MT4;0L<23Ll3I$6A2_Qd)wON&kP{|4?cVM%y#zmzu5l;&t8NAtEs+bAJGw zd9i{bOO96v@=BY5j#8yHG5F8F1@IZrJ$R5NECWcMKxFC*S`%mv84M`;9jHq`aUp=@ zpoV@{D(g%{5&ge(k{ph-e?>h(6NT>l5*Wdh36=-G{Un9NrF7up2a!gH0<`r=`=5e#_J4+e!GgTY4&jX0^Bv}vZ;1xeF-y@^eo0pB|qh*T04A*`^ljb*y zRdUA5?Gs^jbIo9}TLcF)X)PKAkK+_)?qGFvs7Ow$C;eJYhYiBMf0dgk)7}BRli2LH z(id-FntTqH!Ft%uxsrfZDU=T8hthZ7G`alDlPCUhisJw@tUprZjSzZE*e@klTeMZ>pzci-KJG z4^VvUQlQY7ZYa?1>_gGEB2X)znL4P4Jt6J&B;N|@$H9&bgI#~5Pui>gNh~xQ=OA`@ zthx_-QDVvFcdot8IK(BJACABB)PkPkN1|#u0NL_a>6NNZ6TN|#{9SvsscyTn&XK(< z$Q7nddUn*gE7H7h+_J_HO{#hEe#PA?lc_=;NyUy=rFpZu@4uh3+yQCf=m6-+h!&Ir zdH==zEuc^}NtP8(wA>g+RwRK&GX#r7j62ww#HuLAsmC5_lJizcho+k{>v&?gE}B}F z`;=AOl1XN5VV}W?A6Y#teyqGAy9I@H))Q>2)Rt`80OscR(09|c>w*Voz*=YF&zrg%;t8XJOn#-83YS9U zkK>II+^K&BaC<2@wh15`>Hn#6N-%VQqg^UbWMY|Mx|Lbf=~#-X_SD!6jS5uXM)5Y& z5lw$E0N@HK=^pMR!*#W=Yg{*Zfo$(&5;Bv`XWl{535c~+9JShd3V9&GgAs{Psvlxr zB8%-+`}?~G^^9ytb;tu@1`Dhdsiq7P4%?$5P^nOEHT9Sw1v@NoM0%C-(}OQZ#WutA z+EHVeZ6ov3>kKOnwh5W~=miuSDKKgFn3vm00Bxrndn&oZ)yNh^B8pATY7f$MqY<6e z-VY%uTG5k2#1<&27#~~EN|OdK#?FXrG#8NBW*GfAFxXnURgq8#<18Rqz&ILyP}TyZ zz*ew;Lt+S27{qUU3F?R1g@@Sl{%PpcY7(76_mZYWbgH~ldEZfKRkgz*{y<97YfAFW zIl79HOk~IsBQ-+$h4kc6qE%7%pa*FP1v{E>lz?&BEk$cKi%LMi@a2DXHY-G%cyK|o zAK?p{gUo>UkLaZ^ruz+Fe)cL^@HBB9dZTf#qpYuHCF>W={#7`>%=X7ylQI^C#3LD< z7iAycDEn8m^M$ixx5s5%KPw7PVL@>~^bX%ZykfxO@{vB47jo z#XeMF!5Z?inrbBij7R+dLc5Am=)C{4!Vs@U@}`oDq^$Oy+&!sTOF~>>;IW=zh6Wun zuYVta$D)=NP7049b6FwGfLC6LdNWfZLChW4TYgKxumDX3hy!2+E7MHvJj#ToU3;c4 zL}D|d$I=vAdLea)HZ5I#{{=h8QFobHT*W!Iz*B;LvCuNOWminON`R%E%MRLDBsitV z39%ab#`uJZIRi#mMK}aEnlbCPK2E%I+JM2zu|=mNjt_)2%nFlTH1r0qH`iO2$Ji;W zHZTF*TUqo7Svrl3K&tIB5M4q5XL0E-L%ty{m*uKi4KdzI)p^o{`DdoRz6t5|Hz8dT zB+l5B0iTkrWEPfX-I`hv!NZ7tXWC}iX4-fpgRvip_7>AO)7C#`8in*6+wwBhi+FN zM2nb-49d*t)&eNV3;)5zv%6tQ8L?nQlh+;!EOrvYGq8o$6(y5fH%0b9KM39*Rl|x` zBQZk)U5WOT%wKzomZ?-F&|x?!C`p~{=_s0e6D(S8Cv4d|C66SK2+9QE$x>Wtln~5Z zhcd%)Z)F=UuUi+f%o1`(++UbF@uxG;MN2V^;23A?10SF))@}vn3%sW(ssvBEZzSTVvfn)})Dosneq}q^B4> zYEUT4varh%qbG&4&MJ2%2DLSH1v^cgPoTAz$mW1nof0+|in-`dney4Potv0%N_)OY z#ncxt{W$5elbXU!PJtnLImpYMW2V88OFID#Fk@9nXOT#dEP^tYm%S`PRQ?>?vsv4L zF-Zwzvux7Zy{1+^Spq8-%55f2?LoFm=7bHOn<gDzj(@PUuLy2d~!zlY#;w75`n;`H!a8v<>ndZQ*WttT}Z{9?r zka6u4%mmsAP*9}Z( zLJ3pAW)Af?H(|T~t{we`hV;f1_%mgj^A|Om8*?l#fu@o3%FD=2{*xV*-7^EYawpSW zWmjp&w(=Jg%AQ-)Hu?>%new&NRtXe{ISQ;f(7^~u^pq_CjD=XP7bg^AOzLGPRzc_$ zQhP69?6g~)l~qjWYZ#XRvWGurXgjP1cd7Atm3gjQf?nq>)?&=FiOv){5#z`&*H6R8 zbAcljOTZ!u!g5#-E}@`VW|X&!SS6TXX+oY~M`{{<5; z1ui-=$Rf!22VSwivLQ&4@msT=%l~FWKZXs9lt2qG`owCTU>ym1Xs;jxHrR}-TSjT* z?sD-Glt(}P+*Om&Tp3s2yqRUOzVNOv3}w5xL9EChu1f^8SXwUV3CYFCs}OVPI_E&r z(Xv_@$&ShO&g2*y9$4=tF}Ih&yfhlw%6!&OuzBx`rXru_vqzEr5k}u%f1!exZF7CZ<)=Rgx*tU1$ z5JTTEJ45jdo4@~-zhdn-*nSU_0Q&pCHLMT-oL}SD$KLm?A6KyXH`RFG$b7nRrd!f? zm`G1Wyk{vFHw{@TaAnZB2|Q}`v8^l>jM}Dj=8Uyi6j~+dzX)bhFhl6CS|HK)a{DJV z+qYEEDUyMV^?9sAzuguHhYEyZRW6liSQf0A&9?03ko9(Pb&n;FN(=2%K~72hc_Xn* z*(eN-lH&5%d*+d7@!20KK9I@p$s+VsMsuXMlV2WD=9~donjAz`h?XDVKoXT3pD;JyXn1yyGPKB>~6FCqa5R6a4uG@uX#L^1dF8u@&i4)gwFR8-zL^L^R) zC<37$$onvQGk7Z(VO)Y_7sH7kV5iF8c#ugq?W9C#+h=n&OKk%fON%82dW+ zj54oFz4)T&Vo|&B!=di|>oRT>w^#bpUFi}+2M?5q05&jjJsUA){wUfEh!UL?vfwi$ z0L_f9t1FOu>uOfJ-7}=@Vq-k@N?*H848*m2%LXsGIRY25t4yiV%vrfgre0hA^La(M z?ZqwAHkL4SA+59- zd)@pd3l&CE8H>!eNNDoT{JXLVtHFNcirMbN8?N=SV@6B3xMuU3Cw)p~CLUL36FfZM z7glk?NY!pEN_o0qk?yLaXJDV4jgea|d)p*;1CWNCyh?tnM7C(G_3~l`i7vM6-C}=@ zRM@$C+g*fD$)Aa)B~&0@rdTuyr&K<2>4&efjYr&e?dddIVu3lds-HVrfVk6Atz>I} zS*h`E3P4!Z7!Ks;?Ya0QjeGh9{41At)zFVs{`mn;8dKY!l5P5G0xLZMSv$cr1^CZf zHBa$Gs@&`OZ5<1gp!4rC1vlmC4Y(5U&!6ZZnZoMm~>%bg-B9~ zcy}6XKi(fY{%5I2Y-+`BJm3N;IWF<;I## z8~wa^JUDaBW<(N20TiobYpJqx5Moj=9S^gtv`v^fT0?nor7Xm%j$xx&SWW(ft!jUt zCrmGTx+>0%33^g#{{BI3+q0w4(}F2e*;l2HBgp2MkCLsLJ#wlzIroV%fKcvCzFl_S z3+*oyk8D`+@M-ixqd3Iq$3j9~zK z&nk#Feh}zyd47@aq|ReGoB?xC648Cy-Tpd;_@Ep__N;nv#O1y@+iuWGrQ7K(*|Tm{ zO`|?IP8DRAl!d*l!n4Fx2ceY>N2;|2?lmKWPPAu%=9{gGi(jQMsqR~&Hn(+jWQ3M` zBel57NgxmsL)AK4FtbRUJ)#s(w%n50dt_}CXRvhq$WtyY)a(GM39tPv^`g5{(mL_+ z_(730#LlMWs0OG`VPrlmQ&x=i5=FwTWly!w4-_2{{Ruio{*qk0qXgiPO!^Kc4F|On z(b&oFeglBeBmtV5Oh}y=k&{1>A*~=LCC|`+v!%?qWiGe6CRF~O} z=do4c*<9_~EQB*%Aqu|9PI+NT*g_KQEzNt+*fY7m`dpJ37NnO;CPL8b{q@jpNF7Se z&_G%^%Kus}F7juBfNn3EYKfU1Ay)YsEE!RUKGfnKTJm6LzVjE1{IQ-SdxoXTvz2aByhr232n%z&g-<;nj7OfW zs%mFzTDGc@s|3Agszhsa4D!ic80H~L&T_iTwPH!b#bzK!)P@rDraRZoV<_6|uJzAp z2K3YCcU(=IFSf@-{J{IlUoY}^{`}DV_aNoR2j}k({e8yw%)iet{`Zvt>Ha=5a(lWt z#Ujt}fPL26l~Z>EpjMDvP+~!EO)v88y3w3*qWkL%#h1Et7M1@BPJ0dcr7Km1DXd{i~$@CQkY3N}Ad1F8>R$*WsYA}&C$04s;VcELjG}YK^A|zClF*v;#<+h@u$mS)WvUO_2OS z0r~{U@{nz21pm-qqi41)?Lhv~Yj$#Ifa7WWG`xWoS4cat#3nIUZAG2&P?k=f=C-zPE5nPq>~9 zAuwNeFD&x*0a#z|9&z9twx5fZ1X}^uP`q)6iG-isPNIfdDG3XnoCTHfIgW4}h%r!S zu8ky`18^cgDo@RV-sh@NlZp}Zv%N${ZI%o(cI4yn1e zVxz2h0kBAs)Ktteuf>__G7r=NqG625PL7u(I6XC$A(#hB1mamvRx?S_4C_eN5WP~> zGuGvVCLvvA!s zV}2lM&$45Se!aI579?$_j^4N}3$=)_#GuBMd6zcQB z{`$3&a!06pFeTe0!hN>ZF}y-8EKzq&hO6xw;Wf z+U5zF#v&yO9(^E; z)aCsie6W13=&|Y;IYvGdA0`AuKftyG*XljN>SVh?zq*V?iFeBPtKfK;#nyaMlK5nb z)(C6!%(Z20S4%!4l>|n=+(8Ng8gF`nqq|zQhG ziVRD(3WP`4IqWI}C5v{m_*`zW7y9$BqR#rJxxOg0ZHU6E>6%>gZ+I-|D|&in!mOBm8n z@1!cWO9ng^@3pK|tCy~6sW||$D_vzl&E$rhJEf}XXB1=`fF`yph)%Ejd8FS8JPHbh4Q2y4J>?^_JTyYJ5u@3j>l_3$75-pRv$hUsXsu z+hx4!)foljU$M~bA8`vpaOnwD{8X=&l&yw=GzR(^jQxs%xPaKO1rhSf7>IhhS}nDM zMPfD?N(wcJtBYm4kLm$CrQNFCE1YDZr|gZdC3MmK#;n8#P0*BSQjBJ#Y#n+>P$x~= zJxi`E1L-G;Z0DUNg0#MPd1GnD1ORfS}jAeQ$=%7ls8> z8NblP+&a36eg&xG<^bp3ufH8{&6YxyiTHOWI}*>Zzp1-`kr%k9T;^rptRleJ#3Dhx zwB)K1_FsDF0N^$t+=2?+<}RD(KU*2PuZVwTwTyuo1jkO6l(b@F!^meM+p#wV2v44h zwX=uP3}CZ!@N&y=mNKgQ9_P#qCjjsx0B^ERqX~2q3I{8E5 zX;TK}7THYs+Iq|)laGcCYJi%yxrEP*mEj%{3o)BS8L`wcyc zo0MF$j1oM6p@gt!D9MLFq zGD82!%{!ZvHPb{G)O&InXlP|046hD)+|UG(112Gm1m;UAdRfP_klW zo0VPSV5aF+!|>;?9ILPw=|?hjDa+!YNl26>!C8*WP8q}9#&+96sRauLNd<2Vkr)@3 zhas}^kjc7jo<`yVaubcbteNfPg-*y>ZnVYINZh0&f%LrQA7B6d6&+-UbmUCpJ7#Q& zNV}v10rcC zHb3Z7<_BYIycj+nSV#}cWNv&*F=b=fn}kyf28+Q$CwvftKD#>%B_!prr7>2G(%+Cq55dd;$d$jY8q?5qallt?K< z4kcf+EU=}pUYvyy4`7fqGdR+s1f{1Y*|JQM7$5NXU_h}$^l~7_FebsCFlKnhX$+G= z=*zr}tv99!F9Lyy^lpyL%Pr8Y4lQCqe>D&kp ztbQ5+nhHm+tOUo&%oJqAS^Sk|ksOjIq)Ae3qMWtwhEVr?rBw@aNf5bRo=ahfto+x+ zPH6g(EGVI?>*6Hi(ht11wU23wcB} z6J&yxMlj6 z@app3k#%fCa`LJr4#FI)OHPp*%q5Q%Qy?sbYW9ipKOa}!-m;pOxLZwsfyVom@_?@$ zb*<8LYBbORH!IDbCuO~fq4joqHzEG+S>ysF-}Uu=4?{wMuxG5$s&w(XBiw5i{5@T@ zeY}(c`GiB_>Y}C$F;7*&47Ap)YHQUQ^D1Apo{P>B$Q*ZT_otJ8h z$CJlX`;z%lZvKPLyuVN%O6^94{ewvO0bNbO=Oet^k)Y>-4F{B^ZKe5`nuoGbi-Vpl zo$CsnKt)5%PKU&xUuw2NWBxokeV03J<VX;UlPI(IzCg0oE{h-kXxdJUm^0;%?+Z!wxU+kh~b`II|d$m;<;K zQ8|K0VsFEUdGm|RD&P5cnDLj|lyO_n^ zIWgw$e2^iHV11_|$mC~CLvbjxjX2o+V3YIc7H1iWZzP7fG75`0*5yK8w=%tkj0{om zF!D|LX#!`!eI{cl%VMslNMM|Kia{*dC_^YSa#sjLeV0!hpTLzM$>Tzee&zBG_}n?G ziCq-Xr`U1R0)~hYS=6bz701g|2n2=jaT3;nyon`(-wGPC1R(-}aKa`OgZm}C#vGz= z#cUAzWgxr1@WJR3V$=d46uW?H9N9@=XHDHOx@CLQ1zW|!`P`F*o&$P937>B zm3voC@VsqpLL^aBd~0(N!9_SB3zWl4XyExwvCs`>j{x;8po@u`r zdDAsh^5NBPul5JhR)m;?3uPTW+I4ApO9{{h4!1vMp|JTjEqCyL>_UWqCa8E%?D!s8 zKT#5QTT8z%Q;~k*$SIINDX_=_7<*A`$d|DJh8k4f1l-SF=$4TvDn^JT>>VR8u~qda zl!mv&55=t(&H7HLQYd>QKwv~8M7``nPIg~1QIjndScs=-FR2gNCW+WV5)uF+)2X<=u@+9smU*r@Pcp)IFviRx$=sbMj1 z-z7sCmc@SmI*j5)F{4JL0`ocAel*1)ie#V;((}szZN#^6xdjwT#syjeV|v1Sc28D7 zEmYDMqOo=me?5h=(xQMW0BFK%T<6U-6$^k7Qo;%#iHR%eVkBgD2618Pw=m`wf0I)HIn%V%p)qyPBPUb9&9h0g<8LHn4! zNve39rJgqI$%aAAE+@u<&>5ut*#m@#sUsg!HfaDP%tzgaEiFoRQBb_9h!=J1lYJ0W zdJ5Iph#~ruRIwCFt+U^vp_82&o0{KdcYqj~cv5Y@r3#EPct%sqYQqRC$rY2)U#B$3 zHA2jQS88gJIV!qz#Iv;VuU#Emxg9_%#aGmrN4&PEk=){}3kcI7TU6{Ru}EQ@AS zh~5;`pj}hqlU;dy$eDUPRl^>gd+zGN^3Yf1Dqe|HhtX{6^Z`bS zy8%HgqFhQtDr!GANp6p<2|7~|9%Gxc6BY!?kNYA<(RBh>EUd1WKs+QoLx5osQ&_Dv z((jQK>41TKadl-HK8RMDT^sXED$Fp1fE){cH8Q{=IU5Q1GC_yG1Du!_1Gw^z@E+$3 zmh+mkZzGYZ%rn4;^oy@FBKeS#dJ*^wfoRYJaO_RmzPvy)%!pa8f4))Qkc1?umQIaS zcq)0oP)3a?z_|Rz)GbWoP_Pxk&V55X0RW$2k0fRcF?|(HQ&B;3>0)9d!_-4wKT64w z$+ZMTM!spJr-B#2LB#MCWe`O6AS6Kl0Tehf8CNQCwceGdg$Zk!u$OWs zSc&5$$?w;?;z7d2}JlWEW4$&Bb;^7BPGsj(5 z47)q9tZpTTa61;WC8POZ*+_tb-hD-jOFZ1j5M}F-yHUCJk>BA839k-fo+~hjo9fBPbcX#^VT9$t^>oUKz?mxsV z<7b{vX1(rno2J!lGuix`T4Np8i+o=C=4~o5Cy$idKcBz&>+jd-bqv0ZmV&!M-+aJ% zqebz;3>dfKC(PHn61btxAcNOs=0e8pvYM%SFcc>)p8yW-N@r{i13UwU9-LHrEOzad zKAs;GJfk&AG{o%o&3r6HtNG@nX>2BSf;f1ZkVABPb%Pc0q) zWHYFEIlD&mcoGt7?Y!>t@T^>zDA&InPKEBGk5;P534P#K3jg%pv$ivJ?to9a_0+i| z*pPm+;-^;WEdQx3!;Hk)s!_?=iXdW#uYyyGi$|0X)n&0?^_iOV^Ln7u?tK+bRBdEq ze-k_N1Ov&(32g~;;DTU?4f_ylkIb&5m&PmtG8^AWQQN*=m)?&*Zw| zitBSnm$mx510Ms162e{!R>IZyJXO}W`u}=S<}+YtyDS&S`)B0L%($Xxfybul)mEDA zAnP?N^O7?*D}-02GMAj_Y3>OHC56VHPmJW_NU(K?LZEK#)A?A~SDDk&WXrQTai>J? zVc4F`KWmnPR%qS0%;Gy_2^id(>zw2k9A6ut$RLuC@?^c@Lt8+tJ(tj;m&JzKktLS^%P#1Z=hb0@KWjfBq-reANpT?8%H zv=-Eb!4K>$*s@>Yv0*7$5K8Kqy=TFthNio^xDHe$#T&SFO({k2be44~AmR4e1xLx! zNW-%$AHk*z`cZn2rL3_Pt*$)BEgFI>J+1gAHU^{3pgh!(<>HMoD$QPh56L(-jNIR{ z6{S+TG{_}1PSM9?4saoITQ*6eKWmzsU9`b*O#f#(rh@jvMvFL7qP-OTXQ*)(D^ zFt=iiw%+JBXcK$`39yOm$}kdj=s_PB#H!&7=}VJ~vnrVY2@?Q0$&#QWG-Fb4Z9cI? zw~~!*+%2l>*>ofp2_TSlSeDB@P7<>sRz=>VDkoxz0^$=(@DV=34u(wu$7R(t6Bq1O zh13i=s?;t7$eT$@n-DzDnV1Ml0Ksv{o|SbRGI)dXx72d4l`TOMdM_7_j})6!PF+Nb zeX}qwY5Y{w%`&t5h)U{I%@{XlPcu@@FOaiV^D5>c!7wWiwNIJY-txzqIj|2#r)b&q z)QZMyh;naXqhrfHT6EfyhSA|tF`^yaF+mj+TcMU&d(oB@{TM8=N;97TYGtVgR%B2VHo9OFd6`mF zYgDfwx@B_QYi1XB=t=;>HI4OyBUhDn8|^+>o=)gfacDKu1a=_&dJ4TVp?EY-)n^y> z-0VeCUf}Vu+|iWe8qkCsHOoGAr8$B zMwuOQ0=f#QQOspe>JplX3KHYPNErh(2#?@6q5!VGv9gCe@Y_ft!9S$NFq?zc%EQw5 zk^IP2jLf%c)@t}HMLdapSwj`%b1Ot$8qYIl>V}4`uC^IJ+-(s*T?7(6ovdM|{6o*? zDK(qp#+oD=YAN~cYA~&nCMvppk!x}x1liS4%GkA%dn@--n?Y7)YfGQ-I1lw2)ux^|W35M<8ZI{J52Uvotb0nM(9EsaL$Xz8v{eMx z#QfmynrDMr>6w(a3=_kblw4R3fP#l|d3TL%N`JkxZl9roFYE@8t)>3>(Upjg)MS3Z zE{J-H(x&hxfcAT4O$6Y0c26Gm>q^lXPxHk4qtM$HJX*J=(4%K6ktj0l63Sy&0gO67 z?%q^=>=hLwa;3qSFGhi=KoP$9pO!}{iTTy?C<;vBi(yd$C$)0qYW6LN89!;-Qz4+7 zVUDb_&w{fr!WjE?TOL_6N{#6HhcLe7{`;?Te+YzAkoMzBmV+T-9B)oSn2jn`V9`!8 zPzpNHFv#Alq`#0@ZR?p+oH6pUDUbDoQ#F*aU=m4zg#rLWvhWxHgt76V2*an)JAx|k zX#rJHd`cb%EYI?Ctvz{UC@Pz%r_9e38XtYIUJVs?w%*N109A9nOS*+#2|?o_1Ynn7 z`v;i?!V#AYk`QNip&3Jw9TG1EoXg@I;_d8H3zY||oUGwev{e$(p0I~VSSa_QP+Bhw zX_Z>jpnF14=d|u@FSA{Z95<<}m5$relC^wxDvQiQL3TK$Wg%qY75SAlT~<-&J~NnC zBjhHWSKNEf4i>;{^aOti)~m7Vt3nhyc?{FjeYToOXBgvxZ>v0=9A`jEtAQo_>TmY& z_Cu;Av!rw~>B^s15zrGsc#_W&{4HV#Y4iyv^59rym~W3-IZzy<@bc5r##dFNPii+n z#4@_9de?~g3h8G}ko8H>#kaXXx=4YEgYj!I6iNo~X)0$)Gk{~B_C^EE0Q&q$7!M@@ zY6@H;)V)~sG z;Uh^frQOSoJLST{T$4ZJMJPxc%IXy)D71sooy6Q4Pr7VkYvNkRm$$@{EhuP6W{iX) zzf)7DtKbr+*LK~8E^=>8)S#X>d`>}cuveD%--}1&u8)AP9yz9zzJ*GQ7CBVIVr3aS z#hzENiw~J8DmeE%QI`heAA{L#gJ2zuldY6pf(}-^q=q2{rmAfRS_)zW9erN=&_ze* z@2tjD2D};WZQBTITC8E%mYKwAEhzv*0UPQLx-f(ZT^6ycIs?83y*aZli5wC?v zbS`{u8vU3V8cGI6hvJHN8>Sc3-ea66jvI~g%{^aZSK_BxKrE$DUM^F zfsCfbMSHr8EPYfaGR#D0ZuIC|!H1qd_;CZdEBXFSkC*Rua94eP`R63aFF&3MZ}8*g zKM8W`F}|ed|Mr3_^T%_V|0KxuoowyzW)J26K#<#pc}?4B-d_<`^c=OZOzo%f+*S?l z87L%IB@=>oC!YnXWCg9|TK36Ed<>f5F)5D@vUmU-6Hk^)^qU3-yrKHw8XR9Zth=E{6 z>I2Y^Msa6_vC2`e?z|F>sLY^+|ZfS1HBoDdv;;d&b=(Gq>1zrI7jLEn43gQYqRS zU_Dg6tHBU5ie~&&X{gR(uslIfv%`nfI9dYFu`$%axX4H{a;dW=r4jIcUBwyAR*`i~b zC4*g3VCoabw3%9fY@ zy9Si{qUc?FO%j5WV4D$jr_R4gn)K9}WVu>_BQUKtEdfhYgHF;>lLTuZfRAF73_0S! zU1Iyzjy>S&fPA1mx5Ge5P+s)9r9|jad3dbK{{EFFYeh5u6wtEWej`J|LH=LCWfW-) z+aw|C5X)Akd2JK+V`pN0D+%m^O=Ov76=U^rKEe$9?_ZH7(5?OdAK04I)qMk7<&Q#$ zX(tI=SmclLg#`7ZHi5$ucik$5e>#3o>lwb$uafupjelK4!Ol%n^iZ>~l!F@r&DV@TwfXbdKWNe)IAPMnT6-nC7y9DP(3&j$cS&@OaB}-@=vz z{Faq@r{53`<6`;IA5+^W>}MgO-J!p+pOCHS%#Pl7!FFbm4dHZ!}V8ALl7b;AsK? zJ!O-CO>yIwR()i)CX>XL+(xV1{gsRkwBOmB$%M>NukA4Bpg#=4Tb&4cvlgh#}C|>2)K%64+N!9G?ZRc zYnLN7Yf4HYh^^TLD+IE}ZrJL~uSB-zk5G22wjh)(Lr~c8aozB7WnPoEdX5`t8gMbs zU$N|)y95!zQv+6(A?_EhU2ET#yWjPS02TlZb7V-p{pss(%QnRLGq0M4V?^Nh@`ir4 z+%gBh@D(+O9_-5Jkq|H!9nL-GfGU}88xhH)YC@Cb8p#aD>Y|Ttg^R*+Sz;I@Wc*%+ zvXStpttZXavSzD3NnDzowTnoalI4G+#}d_1o~HxGX9&R6SN-O8u9xIeVJivgq`{rE zR~m+Z)=FYt2!Y+tgo{}+Nx2<(TsEr87@rK6*fS#3GSQ72?vfnfDqv~W&UTC@ZrFyf z3MG+6$!uzV)9Yq02e-mlGoiEC3uO_C=n*msA-Nv8MU>f_16;3c!T6d&Fhl%lo64OM z4#4kTQ!*GLiXH`NPq)8PO-Yj!k=vYFGdkO)Bt)@Tpv)~xxa240UQnPUqzqGzz61RJ zv{p_~DOJh+Ng9=KPz`0vUIx7?P>BcZrdATc8(Ok3T9!ZdMC#k^Zw1X+szNCX2xW{? z(sDNzT#dL6NHPJ$1a^7qj9F`f=J))D1i25a(HI%61USCU z9!Z4R&|82zn-Y9Syklj}S|QzLGetC^BQulV0LWI$ZO3lGpcMm>kwcYCVN6GMEz9Kc z^DeR^kf5?Lh0Gky5-240E(tSN77`0|08s$%$a(B#;fY<7Xvu@Iy)59Db1D|AB+Bag z40}4Ov{6G(6R|?e(%th!C4_WxOgI9|M`@b*P}G$2w%2>~P%Nonv(X=4Y`53 zYrR$b|0%4_I;AevP?Dm6q(}`HVydQ|*cO#qClvqZ2dJoH3VTZptHLG4cn=SwYlJLT z;1LbgyM(jqjQBqCip?KS!gE1`x{*#lNCg%Dn`a1?AzFxJpfF>hmgp(! z%iNKmP8om&9_ahxv+>w!1$fVuZe%cKS$+&X{Jb$9{`~p* zH9v>g|9$@cykd^es6NN|{D{vv%iQnU$DI4g&&QZE4mbLJwX5FO_4!?|yYKPsi~2mj zU-~&r*{9>h`|O`n_s<3Fix>{`bNUf~j2^FuLGH(AFMFu*^3R+15yw9Ho`bPx<#Uq# zg1LP1^D9QWpZc6RP8JumCo^WN$8lrcn2vqI&P2uqJLBVX=9qSupvQ>&inqoZbXH}t zbbGvUo$+vC9S5h2lkGX#hu?!`(I?l)6zr?usz;fV$3L?YLyF0nJ+6BH!{BEu1eY=5 z==;^B_gF<{A&$3?)#Fn4pjqoUeCK>~DMhpF!|&OSfyO0|n2;?QeLCVE7qC~N@0pvt zzn47$ity)m$jR7KAEMBi+E|JeZ<=SW+t}hcxYD2B3(?pM9kb3NDb*qGpKA`WiqqWJ z-L@#!yM08m-k<+#i{GEe+O>t0|2`7CUwM~!pta9qwYB}Gk1&6J*QGxKF(=drw4Xj`s^fK=?WM<;On2=d-XsMhHzuH21>CUI;-!_>dKZ zz%j&9@x)%}J=ok(F`yA!@(d`1Z!|=8tD%r4hBsq%SDcNt?Qz6Cd7asd-zde;aSQ*( zL-83e>WVKrIOOknE6Q`vN!dqVsL;?*zA*;6#S3j1nz84`oFg{|e|NOlp3{)LrM}+F z9-_bXUOctXuAzEISJy7KWM>D(QV-Nhj14(La(d@gC9WtGvBE|xK^2D8vv(Z!;@cHDSI@=<`_Lt%+hz`G+k z0zMV+m>Oj}-orZ2uydnhoreZ0)I5h#2cpALfY%l$jnc#{g{ban>akO>7^!3WXTnwt z(>cvq32hxS*R_ZX(Fo%)+#3a5v9B;Birt2rG2DNHlg7Bi0>#JYuYyc6)%$=m9>!|L zf+3Ef9*4Vj#C&|NId*9*(bNw(gVXuxeoVAy42ykRFsWEoCTVO%{!!A%hg*6rg+QSV zJy%dy!-tXdnD5T^!cz9G#t0t-rHCJfIqT~4iJ+v2w{2y%>DXrF^Jm?5CX7a{aDoj*fab$46r#Lz$cjqVur_Ltl^d{6=OPjj0r|Rl2vD>?_!-kEiG4H)nAXpIv~o1LkHXZBbV_wx*W;4D9l*5IxPJo)>}MC1 z_ck(u!J8*))D8Wbsl5(+8D}{g>1#jtAJe85Jky5jw8eABsLwX~NMGH1Cbm$72A@Z+ z+)3g1Z;Ac5{jIP8W7kE5(Rlq<;B70idPR&u7*Tgbyw!S*{rQpYx^2_vuv_kTSh0hP zZ5j3x+fsbOlz3*J@``X;lW-S)#j7K(*=N7Q(6zV#c2SE#?*lJQ=l&}1fBbSgjaMuc z0sl`K*|YuV+A%YN`-hDDP_^aA$jggUr={-{3Ov2Iw~j4SFD`T}U<8In9gD+%gada< zQ&t|)Sig4EaEJbiQMQWxe3o{pbH?NTE*xep($8sHYVGg<=eO3Gn%s=I(GVHW4PlVj z_$7iDqC0lJdNjW9GS608*jzj$wie_f}s z^b{*XVBEEEm{x|9g#A51nD7w;EGXTTo9VoUxnQA(=0pEtA|guJR=ostdU4%Q8{lH- z(DE=qS&Y}g?+F9R>daw`coOPl1Q832A<+>eK;C{>Pcg(7pBl-IXF(WX%r{pW%8}TUU zl;Ku`wF;KR6QOU~$E^redBuA|<#6~=XQ3vyo4ftq?SyZPBvjoMLn-J>euRQ;@r@r) zXD+x4v~piYxKxIa7K?&cnSpSimg&TZ1*QYHSGuG_zi?;>(h0>zMi=a;32HNku-8-p zM*Nf>6)s^!&o7Tk_X@uwcG=hpu&X*I(Zt)M8u}BWog>GuM^)yzh?$r}eB4-tjakJU zh83w;t~(a-x=X-ZSf|gT)LY|L@4X!NUV^4Yi6yG8`{%87yN~hv^8_1U;yflUxZE}< zb7y|)#FXhiVN2&_iB-m~BDC#&=G-8@b4Tu?(QLMu#B@jFFlf}+P;R3(a=&RMm(T&Cr*c4~hLd|)!ddMN?jHor`aC|L&QE$9LdfefGne9sp(+wLy zim+d0oG4efWe~K@9|P#`0VsQcG9!ErApRE<|22RJS4Yl&;c#gFGl2I8dttuzC5Y|_ zB+h`Exbrn3-OKN37JJzFd9>?xTyrms2&Kl+e&5l>QZ@$11K(cHAlEZYUcLxjrul)_ ztPum1*ShsYX4M}(^~YSyo{@D7Eux!)Kz$E$BtkY5T00;i-}`_CC)J>6Df3H2Ce)3v zde~JEg-+zZp-a55PAD>rwXw5fnUZ)E%yV9d1uVH!i!tJ>ljv(*~W$11qc?$Aqo-OsgE#9MjO?TRCRpp|GcpnG6)` zqXI&!h32c<->w(lc0l@(z}mX6#1q?O)=;1Z1lqlD;)xByU&MJ2JGhNtN&B{yXhQ*S zOoZTE9K)qjuK6cl~gaGkxKg2aM;e_T%68OqWGQ(AhgK=W_dmFqYSb zA8V$8-Iz#B@%-BF`t-o|bCm~9cr0=FRBO54j_m`Vd~KwnxIG(Q&#Mnmz4=y2GZhm0 zv8W*3HLt1auc;dIhkiu*N}8Xuk%z(a$Ze1l^{pTG(aTa9_Vh&tj>(r^{KH4s!z`(B z=6Wf{@Y-i;oQaPx*H490Ua-qbl`~JS@I71U{H3^lq8o!F?zT96jSKdN5e?|=E zW>e;X)O*^W4LAT997nA`*6_asZiWZ=n8OtPR0HF!23Ake>PPa+5B~`N_DkRn@J>Gb z1-_OHSZBf$-x}ca>~?^iHE^l)`>z7vbqCrMa&Z2vBq{dFxxlf87Z2`LE$vB`S92Oz zxx1-y6pwq0-VJbeqd3aN@~2>bo?ALYWa!waTDHt>s;-LxdpwDM7&{2ll=w5TxlnA+ z^zK%@u6ds($EFmC&FEWPb=}idrfy+Bg}0vzv1!Fa%85pXe|AF!h}P1F;OL zrFp>mAz~{Eqpg}OJXk64*}|I!o61qeU=fs&*+Tp8M@|P-jBiAf0yt5zk0^YKMf9Ve1}Jo#~AA?JemUa z0|@+Acof?oi&GL0>gHVDUa`9~yxiQx0tvBOOB%>2D=0qVGM7XYBw+NOU>^Hw8}|XL zf#ghp0dt8GGwI7YJ_upLcCo8WyNnDCgeyDgx7g|iZFzb~OEdD%3s0Ayh;9$tFSSW{ zn1t_m!kfL$96sPkKtq$)vC^QYLx*`Jv4fmO31y75v6a_>#Y3(m1JeZ}(K2zo2>aFS z0DWJhk~uQQbMBrfJqUc!HBR(Vr8ytG`e4D%G0;ZHVFusf+p~{<=gAf(bee)eB)*Y# zeEf%xl;j9NlztFAr)D!*9-Kdine4xBj)uu@#-VnA)@Ba%m91L>1c~pfTMxb;hb3QS z@(LT8351@@JKwmEiY>0LjFUk(03X_mg`6+BK(Cn@pw3G&O6H^1N*nbf_mP+s(8Kgv zit;e!b)LCNNB9NjXYM7U@r6(Fq!ar)Kc~{k^%v{>t6b%?L=- zlsRc}5z_E~fKC%EjO5)_YI>~t*@CMHmz(&j_$~M~k(k@h6ahx^aN$k|$^gG3&RGx0 z9&S*rEr(j_j;nYivVQp4m&e{u;Xs8CeBU+v_{zHq7=6I?k!-}Fs<}d@)qZ}Xe@e*5 z@f?w{aS~ery~-t#6}a408dW{ zGzw7@i5FN3`BF9_x@pi(5e#y{mHaROQV4C^n0|{`K7&C{6JNyYWM2UEGbg)^aEw!#rNH0{V)Yr zm85(ho{R!f5^EFgljJzvZ5^NtuFFDfJz?P)`rmWbPW7`0l2@ z!_cMrUvH`ZNiRxoa{C4>{8n6LukSh}zJ4q++;_}sx->&m0H<~HJ;_>XSU3%iZe}&{ zYA`WzxNfYmvqp1Pl;kDmddtmVcSSaFE8iN}!!iq*w3M3Vt%>4c#0`vQi9pCue62sbmKBD zH4XtC40e6&>^;6Kh>jsMBEjJ9k*77$vqLN?9 zCx?z^gGCOxfFNHlG_#qJX0Je{x^Q{``LLM`8xguNQ+KVo1-E#!wQGD(brg#bl&SsZ zU?X5tq(p@xkUz!BvJZtmQ44!ioLM8uG0+!j@7XBKUp#jo2Vg4$);$dJ1G6(qMvSJ2 z4b9%YrA(AqeI)9xIsEHBD&e|#By4u3r}lsQ&GDYlAnvLww|uPqb%ZL!=3;mGUqKbb zMv1)wM<5viI0Er3bCo49A@?PwAl~XR)d!VHuJxQy89&OdM#BEShR{sJyM6|?!(*hl z0{fq$C?tN?^WrQzi2pwyy88f~X!|xiNt~=eUzmA_eE0skj`D0(1H^!heQ6;`N)n6g$-O-zwT#2k1MvDtVKJ(-IdIt=e$5w5D-j1Hs%`1Cp6(P#)Q>@KBRkE?z~KlYic_eZj2nw zAV%~-hwZwvPTjECqhqF+2gs6PEW;p$vwmHgK*obMp@Ivp%gqAwf=lxghur>ln5E0* zxiAjQIV<4A;e#K6*(*T`xdyn7KH{47JP0B&>9`o!3-WFyLJit`gn-@SXu4r5h_SveM38?d3CEgbcdX+2Rr7EPKk zzLJsLv;i@2x8!G?*)R7kw4q?*5$Vn0jvEZ#J{ndMXyj^--{HMgaV-K}-GmaM=q0zZi{I;56n$GEM%+5{>F>IICrTAXNxgN8S>J0B5 z>VPg3J79kl0ggNO>TTJqmvv`kkU63Moe*sw7LqhQkODXT3Ryz z9Cgrz98%MZ>s_n<9Y?t%sTm%xA`)i^VGj2`7yQmxgXpetEiJ-?R@0PPeJsk7?j*-V zR?{U9lV7UHkHKThOAkqGSE`?CNU0Nut68p;{kZI~V>jp~8na@GG*Y=GSkH21x}H6l z++c*H;#=2Rk8mO@5kFVOt5M~4>(aYx;h0=nN8$ma9b*i}-7=5~`C)nTSMhtOMJL7wS^PQq?D- zX5z#br4$R4Q~fL``%ilsmZaWb`uu47HFvv@HCCek65D*n_kWO4GiS|k%%{ViZ1w=koE0y(dr3dc1C0o3la9 zC8-uUi#5;nk!K+K6oyfe2nd!pHn;_bR47F!FbrbYWEou|(LRxoLgj~e?t!2)m z;I3WANqXlVoFN3dndxv8`?=CY%+gM#d^6}vjY5*5A*i>SLr)(@KN~X~O$0ro7~(0A z(u(?u<7sZ9P0f8GDG}6L{ZA!Jmh^!b@1vHM;PNGsgvHgM>!YNWxZdvIPQO?)cxR0a z;{{Jc5gOXy?sGD(s^GvlwKz%jP>=z~@xWd%H%2gEysG(y!Phc+!KY}7NFFrGgX#eg zo%-uaClVJV^hFz)zLV3l%=?Sb2;-PYOeS1X(N6{7GLN10a*ZzDi=uI5qnabuzyx(`hCjW0Ee6{a55qGp>FLXWeMPz6F69 z43NF6t>^WRjw0^Cskw^U-h?)3HS6^%UefU?O@JH>^L`RPsrQ6C1FFZg=3(Gvz9!Ywk zmmnKbsKPr@KTGln%%YtIUj39>bSaq1%CJ5k5?(q;=c*|%{78J|LAP>uA1Gg%t#BC)fbV?(8c&_G{^IFp_3i@JFD!6d#xYqxn zDy7Wy{=RFD#;=;Asnhk4tds8aC(C+$u`Jimnz+@=KZ`B88S|v|1N6N~mb>3jrsk8@ z|6*DHU|IeoS(i84l7uz}Pu1=d(g&A>*Wjr^E`xR}v24f(TNYl!zOsN4=M^&6;LKe< z8>AfSsInKuur3BGD&=L+hb2m+B-81U9&S3XvwO_6;S!uZy>n+gti; zlyfh&V``FDvTI?WQoBix8lUHxOw_YA!MR4mcy?l)C zh$8tldrm@z?#Q54S+(51)bbujQE0k+!||)yYFcCPgZ!>C=vf||O3+<3deZd!*X+e) zhQt&rX?68;w~s!P2}yQKCSr1%6#y>$=)03~+_^_A<} zCHPnEML!(M@Oa0 zu*Ba=msvZrIAo}PHCM0}mnWUN7gO4~wO%?x=$Z7K;Cdq|plZ0J?7C=&)h%V8Q!~=D zRhuh|yD`#SPL*fW8etTnl=oDG__YwxyZroaw2-Zc%S^HIHIOFt?!hVwlaQb_w7Acl$NV4G zdO^EbQSoA}j}ty7gFYW$J^8!EozRoO+mp4ETK*%C9{ z>i!}=N4t%GGM~p6$9~QJvukUa;Me06n=AqBoU-uSUj;$dOzk-=E0xXhrkpkj*Y?qQ zRRpDf8)e(yyytgY;xla@g*;bkmQ#v73aWWzeToGS>;YWnflr~o+k50|0+cPyRIDU` zD$Zh-&V)Nv(bSaGYJvSln(azzE_hDnxSOdhlDE|~Em`6xDJzM19j6CAr-N@ z4h5o?)%GP}h(TnKowl&xi@Zhb8Q1RpqxkolZ@C-T_`|aQC3ed8lGXku+z*R5P_l^K zn#Wx_Vpl9f%h2@QgHS4Z;=ZTPoG8;)y@Ulv@}4r2IsOv7B;4=w6)RyfI2fV=|0dke zr`(DXW?eWU^kKND7v3#>0~G!oB9kwbLM2O!Y;8_{{?7blW&@M}roT6O6 z057Xq_wDYH?!RqNV){5h!qQr1wZS~;aeEzvxha4W;2X&UMpq6zdAc!W1qj@l%UlCh z_RNnEXj^^vdlS9zoU|G!`5 z`R6&oj~D$s>Byhuk3)|C{1QNlu>X%Ujn@`shbA8KV~3Xa7A4kP7A(GZsD@#Pu?X{( zUzXuif_-g^VH=G{A4k8TUxs14x2M&RXL~;NFkxc*34B+MD0ko6TGlh^{me$u&wWit z;HyVrWt`Ffu#;dz{khteb+bPdRtK3_{WTqB=?kUCisGF46vi$Nh*!W9h{W)hto3OI0<0>o=?FbhrS~rVrPE>k5Jw$3t;#IU5k$>Ol zwxC)jdda3Y-NI~FJssyo*|u(D`pVO=i0E6#pGZ(N1z)&`c&(v)5hIOhL(5FwO*?mu z;{&hgYM%k(RZqsYLEWN4pizvL4P{YHF$IML+9k^cM7Vyz$zOQ?8{lF;Px0pduV!bNc+I-|7r<4% zaAXq?%K6tgem1|0+QDyxD-j07@6UEZp#%ZA=tZ4y2=C0sNB9fU!``1!1G6Q}QX86l zet%Bka(kizHxc;baL2{~#9I1&*8IBu^ud|}EIpqEs4MSSYS*M9j?^Xla zbH$!4P;Tg~31F6D&rbxD41gv>aCD8bk|Hxt>OR>5rfLABhpVC@kIN2oGtzz39+>_a z*|-7)N-=dV)SkRzVWcfGv?-_`cG=SqWhO9SdStzBC;~zq%NtsT;4TDO(g6Vo#nk&X z1uaEr$(2V8M_0@)xoO}wcaSS)YqPYT@NfwpDxJsn{S_l(`-;YI{B#9UCjmIeO8trv zx3fNfy8+&6)i0k*P^i!`1f zZV2yWqG@DaeY}DUQHJ&$2FP-~bab;Y>6V6g(`=SX?J#f&bYyw{BQ`<_eTjq( zlkF)x2Bs`V>FGupU#CxX;M>znV7xpgz_RjS3-c6Le_;y`#;1JujPUlx7&#II_!Z&3 zeA%98ZI=KrDD$kh4cDrQ;OwDg>^c-jeGT_s;M*g?G@`$mklOz?e%%50Y zwgVcbcVbt)rYwr&Q~Q%Cfn19Ut#29I?59=8OQtz zAlHF-ep-$41dv~r<7p>C!jx!beBV!?d=GW>?3gy+Xb=4eetnleAAN`G?;l?;GjbP4 zPZPUF|J$TuiE^Vy78v}iYrxlEaBJ^>&POCcGOEsW=Re`rex@PrA6ULnuLq_6jv@P_ zo8KC-Xw%0J)GJej(;kDT$eaQlD0-#AsIxU0I{*qCIFw~&K?wvtI%O=IjdxRKax1yq zQmIjli@h=n!IDFL2_Mb0IR;Uj5anw8hj!@(g5gdsMST@a*hn9W6lNTtpI%8AZTzcn zQE-6+9A^#4x9J`TqX3yKtR8w2NC8Kp;F_3`3@r~u+j2PCy;|-8sX1#+qMGf_N*=8E zo&4|iDi%r|Xw+&sT*;!mh-ok5`DWRQl*8(gVMMkp#H$6x=W^zFARXS+&19u+Iu{fN zk#g$q&+H17{j@)y9w&?5R^8B>meEtkifi9PkHKxfyruRioEx{Iwn> zvECRU=OpauiCL1*3rBy7JRV<;vjqA`Zv5@bWqNI-s{PBA8_|1b3Dntjm56f@8!8(h z_9G8zuvdC&d>4~ckvZ$Gz=Zy#1|-#Xbo~KgTE?z46g8Fsqgf=Sr2?Xs7rDlw2!cn1 zLg`=)lMrDfUOAm<4jmv9cOyAu4rz-ojATky2-9kISHvoB1$rnmBQxGa?o98HJ2j+l z;f8f@qsQ72ooigC03>tx1Z+5P#J9HSoB(rO^|?s!OITnjxGEwk6Z)ll+l%`GNfb>0 zbY-O_n@pzxdEGB6&dv}!0j!sYaXf1iH$;6s`NyMpH3?u$Aq|uG$lprrCF-h%!C720 zgaD#_%}4JE%^Ba?hzhS530e@Xj?|Mfje&Ggpdl^8Ezr%c^0SDfksy^(Hv~eeML#!< zq3cm5)ZK2SuX!*}bh3p?$})Zn`5a$r<);((IGsn=oGVTD3fkv5zUL4B>PPYdDD)3E zOW$+;;b#HoWaz2eIp1zp`JL8Ir|d^o``gW0zfy}oL=Y zd;yqJygou>z@JJcbDsW3372iLblnDF(uYpa1~@50(c7bSgs_?v+9OM)yQ3pz;< z1c{tLIVv|dW&*ePMEKhT#Z$KnfN`iOaqjH9*}Z7D{621h`^2b6PxS*5Z;*<+4OwvLg)bUK7?`6htIN^BmhvE8(vo7eo9pQUYT1x zRi)IeSrByDcq)9hs41(5=SSVHjst52zYycY)q9dKEd zjX%FTyjypApSXz6@rsmweqL6u7l;x!Rqkf1n^iI`ujKa5t8}G0VRgBmMh*D3cRpuD zGaeSXb5XC1DvJHg?O=bO$mQnt9~U(_Cyc%*Hr_{|6z)Y-Tz|4A4~#NQBc!K~G%kS^ zbqY2iNownqQ6!Ko?ol2*Cn25nCUAB#cR(gjMkBW2`}QqDI`cp}59IS#Mk9dYqlOZC z3b&?{&zL^|o%aWQ;>_Ob(6&>p)>>9XXU~I1!xHakw);w1!+D(|O2vaLN%dGQQO*52 zZ7TP2!UvG}7fO@tZ0LPG_r$lR;2vyw%qh5FH?kZF4M3%ggP@J|ghCq@5q;8)gZuWG zZ|IqRAtasa;aB71l&&|xY6t$C@yRq90=-H9Yo%!)xKikSBNve9HFE8h+>=k#!41n> zbV`vUV4phbftwt~@$ zaLsIey0SKYBy4@4k@-jl;A{GOb2IB`i(7dT{75Y1); z=PNp{4=~UKl`<=-pOZ;u=V0jXLGeb#%naCe24$R#jNV~-s7smwzy?voV1n_)%Sx|G zY3jJiO4feKZKn!bCRBBBU_+EB3rt<@8}3*XEl&9b-lOs1G=XttE6aV!sEFtXJeT)>W}M@{ zy@B;6H^dmqOmFdIci6LRRqTN7R9|1!I{~t&s58HlXv~*0kUM#Ta?GV?kVPq7JvNhP zF~{Q#c-!*~mB{aSU*KsJZB7*y{>vR$>9OPote9fk7kw;J86um|X{(46A@*a`hG!lY zNPPto9U@SizFr9$o_;LVCUb$F?K0;GF-d4;dIMJ>Rge(n5ZxzC2MjtA_;%wpf!%srO%K zi*+X({%+ZP>NJD^8g3r=ZrStjii{||{Hi@Anq4<+Us~(c-hZBsPtoYZmOa)g!F+AD zo6OVR47tk1vKij#m~m&-eFi6xKsi`8I=tyTW~Y=I9M{`|_H6!Rxm28{((9I>=&~up zO)uCTS6%0DFv4znNL|ll+_sadul7RG9MoQmr?0H!aN7ag4&bJ3Z~o0igm}_Z;d)vN zG_N!-rP(a=vJ)-QU%g6_*=*H@$WZlQi#a+5lGT)%z6Z1+tqEbv#*>f_;_pcSOG_5| z2VPfpk+OXb^K(|prZZP|uYr$zXGY<)eU1SXG7|YSdpL!-(maY@b60yBRM6gQcWbj* zlMVDPvmI63iea^PyM^pC!-P55>Y~w?;>rQFY}!TZ>m*GFF{{{b0R$4jy=KdpY~dLD z?(*3y>+&E@CdHZK5~ULAE0CcO=yS)GRxcFfsb;q}^d~(m=?-?^ML6H0jDQMP6-=>)=D+IV8pmC^B%NPwvqx_ zp+*8dR_Mxg+aBn2Q_ECPd{-JtI#}95?h};kPI+EN?M2JH6-_qtNN9@Njah0g{%xOJ zqER>l48oVk*xW&Eqms>CQ8hFdvmGdnMY2LfHR7I#{awgc) zLa0jH(8~_8G@d!u#1Csn6oZ! zU=x>2t_q9xc2~AeW<_(^9A9;7J&TX%-6fM?VQToR-ua?Xn{>l#_8L@JV%|ZHKOlW!S&| z2cho!^FFr-L0z@9Dpw?O?Qh+VlhjfbRhuHF^=gT|Xm$!dKdmh3@6+f$m1ga#M?u|p z7D|Bw(e>xFP3AnO*h(#N!*03km{kRjqR)EOH=>``KMOd%>+-*I}+tj ze`b+H0I92KV5sTJW?khvAK?x(jlr2S+I_a%O0-9`8xRB>?;CUY)6^=8h*I8DF&9my zPaIKYkd-^t>&=Y}&8S_;B)aX&h<8ADC;Ti-=w%&RyKl8D zx>-zfEHijB33y}lHr68dc{3UA3LbHSsBok#`Ab zn!oKCTsl>HJ(mjc&~TEySE2IXkoSYqNw!}&VEUc zNFmiPGcmY-aAa4Izuy%aE5Q^13o|RyOAeA^W~C(mR4C=7R-VBmsw$@HZHD*ji=&ZN z@Uu|ln|xbQzG>e{k~H4`;QW-^{Yf`WwFC6q1ZExf5fQ^EUrKkk*1QOKl(3@xO`1M z#_Rj@f!>Cnecj^NOvX>G(Q~v(Xb+w`nEq7wyV&b`VXqIu42$MWTPGL$r7-%E;+eOg zA(JS`OztMfAvKp&hS|1ei5Lw zWUeHFtrL`pEoBYWK!`}>3J=hx?IIwwg5~fDe$=FA&;{YjHp+1amb;0i1BFX@rX2Nd z3GTH7=g*?vrJWLyD=o(@=jIgta(;sEgP(@)c_&{RvWh@T&87|nxyDz=UF3pF5Ph0< zE+a}J@F=P75_DPPVTDW(Z=s#9?nxslh)%L@T&Q>OC zrsRI9UwFvtuymG{Y@`-pS9W?3^+S)NczQ`*n(AUXbL@~_=ul%?yxY-Z>8X!H5bPw- zL*sI@fNk6XPfpIkBu-+<)dUCVFru7^B=7#G;Mh}Gb(OK8cY1A4jfFl=rBuC9@CUvm zzH>iqG`N3H%jAu+xsxCwHC9x)8PY3LPqy5z3Ehh)mZzWj-P{fSFOGWYjDgzJgNEER zJNLD`>lvBOm4XH26124C4!qzAivIzT#-lQZ}E>Wq^Kj8{< zr7P)0Vq|z*Qi>(r3`hzTgbr(m*HN*DXKzT~p}_hOQmi_DSm~$68o) zUuYC6iILrz|MN~(Zi7szH1j%{y)vT9Rz)`$FIn0!))!Tqsglp_?l?^m4Q|G+H6c@+ zJQF8qbW^nn$iy>)d-Hh5Ar6H|eF} z!2fRCo%haE&(#t#OjMsE$T`MI7lp3e7BjSjSo~>=b+^O`lZa6d;I0gmOQ2xiGA81s z)g`MKqz!Os0uTu;QgTYCJ~6zrB`xo%0_R6AK37 zNGg9HKWUYR&M2#r81Z3M7I(J@+jnJZ&w9s)R{2y+jO^j?XFoP-XipD^@tap^4~LP( z3Rb(|wHcA%*l^chu*b+ z6lR|ysxM9}@!5Y({w&64f7Y*HJG%8N{aK7Z3t0MB0Sj!yAyIS?&7b9%&=)g*{nu(d z6)Z27>yT_)Mw4rQHK&ro6{?>=wdV40@^9zT(+6ImknYszg^phX_q6;ANl(#w>a5-X9YA_PQT!8~p1%JJou<%kEeeVMK+-cLi!JvDJe?3V zSvD<@zR=}uQ}X^y)lf=*nUoy)=aaj{w7^=D?IV6bS}4M|L*q)ff(S2@aQOaLW}vR5 z6X;yjo>8n(=Sb%Z#xxAZ1^y`UzRL_xM1U6BUF}GT7o~}57r5h6ueDRGECsIXsID3ZKqhT0FGS>?ki<){ztUO*&IH3bTMOPQNCDWVgY zVI2GcLkA&yKC(V;#&fqZOy&dhv7-mb7>yyO8TwEz_F%8go*e9$$sV_R7?IB5 z7w=Iaq|^>oNXWYCH;Osb6uA)?HxqBsL6_@og(^OF_}oR+j{S3zWmMxePs#;iPB2O< zr@gS8QpqWeAJX`rd3CXtM!{Ot;OP%|_{>O*m+Kp%^6p44tLJG#NRTTz`KOqni<++o zxS0t_>Dw&pVl_;b7%)MUmRsk5MHrS!F0v72iFe^ojFbs-^vA*swuL&XZ?wbYX~I3XACC_54!~w-QtUcBkX0xzOlYON}tJ z{FVCZWcE%*j|AWE0?WsfnyQDqkZ+nq`5G8CdP=Ta8Xpyw6SLNnmQXZRkC$Rc=VSqq z#11?B>qq$&EFSn8J3O-fXuadbnD$PKPK%dv2{K^(S9_-^=28e~Ts*@9+tJOe!P75z zy7cJDmmf8Fi2See`ivDvhKw<$@{gE=jrYVPUh!t()~>~*QKp%TZdur}S_lxI9AxG_ zQ54iRQg;LEC8n3JW%gDZC+ZEEItU;Vgrsi~1|HcUjZF!2Vl6lq)*GG|VAAMes`OX`l2 ziQ0JpPwPuFBb1RVhI^qBVi&HGFC&Nt*f@gudBah6!x2uOReIky!kPpec*aaj^7S)0;K23@DD!?LlPq8vT5rM#_}^#B^LQ^EphS3!IfXP73~~1r1C01-PC&v0y(-hBnB=Ux4cy;!9)= zsVxU^B~pBu(UFLl1^#o6A6ArG*7U!c0mAlr%tgj?j{sgkp}#>bKgS}<$Hw?rRPth# zc5v;^#?{O=B5Yd`b18JpB=6D8AKccwnHP;*T~Q)QVR;*~`*cUUOk5x72x3j3E;CW) zq?8E(5tpSCGZLqlNt7)}<4!xmjq9a{LLgXBmEY_c_sp)T-xWWqEMUW)rVX_xK_k7P z=vPl<8q;3zK2%OH-dB}J;0?}`mQ%Uav!uu^2yu*TpF@xp@BtX0 zR>T)hiG1Bc04a{KBOGxOLG|SMLlbF(z&Vq-62&~A(o+;5$7T7g|p`AZe*$nDqECp1J zS|pQUSKgKW&Z@R1iOCK#=HvD)xeB zCGO#3;m@cO6om40>X1RjNmqVOJ+q$2n(dUU_$n(eHYC7 zhRnAdMw;!Lg8RhDnSD@8{HC2Bw0u2|N=EX(@bdL?RT%b9^rrvGJpak&-eW7@bndKX zKbXTCyjjw@SF&$Q+KTzpTbTx@cG40U?5J60uwWg#KJ-dWyjJodzzVzJv*A)iY$My- z^;FLTYkgT-l7-*bDSW|#g!5CHw?jWS2(pn@m9!#rrRkVJW}&JnZplt=J@|hR4zpzZ zY?5jbdeo3ySEh%H_e;X(xvjmrE!{oI>z2jPv0ZPo--Sj-$<3%qv7rb|c9W!2a^iQE z0!IN$ZCQ844dW$;p;VZ!&Pmd_g3E9U z{u(nbGr~PvQCZDq2$>XN&s%0!C27{I7m}cPo7c}+WRx{)IxU`{UOn#GY990F_nMEDJygkWKT!;DWL9`QDS3X5jN)+sUqSpF zMRHC5nFJmt*9bTWK)y5b(cW$)*%y>0@GDv_z2Jn$Jv>Nuv@y&?bGoW3mfY7SqrdPL z31fwMGl@>Pi9f~xGQm1iD@>rEr5YlWG!ljGQ^GQl_#lzNJWe$0)b13Sc8DWkg8eky zI;skYcuf)qF;TGyE(ti7QH=YVysEt*%Hd!ncu(c}j`lxzv0nidi(q@MiYUC~d7t(riwhltmnK zNjc~#P9e3l;1!;#YdNPaIeIyT`TXHT?|;h$LADwE>F^NiGCTrH3vwS_%u-3Zk{74q z_zv@^0W{FF0)H$0%j#nzNQ#_~--Qrgm*tpu)&0S0mh5vi$@G&JL$=u~hLB6i46oao zyn^=)5b?^!gZ?P_n6rC4WcYm%quS z_UPo_NtKYmCfm?W+@Vf;!p!gsQ;)^XDT6Esqk`srQ}lmA3?VqrlPq=R9%woA8L>Gt zEZn5_2hjC1W5w-HyLge{@H=hs4Rjr+ERxXWjdZ=Bu6aP{Qzg!sEug3%N9k0{n`oGg-LdGR9h#_rK+aEl;?mCWLRo|IGt)!>*Gv4oUM4upYY zIeDcxXc{x!^vD&g6illyz`ys=BB?o!ZSS*MZ%nP$VWKx#kMML=s^zKpui_<^=UmmB zab|dDy`i+~o6?!vfLz0JQ<{fbiH)_!n&$@=rgB*-#C)N1y=Gc0t*BprM8? z)vOA_{u0oPTavj0Rq(ESXzdm)@msdalH-}(L4A`1&1Bo+E{1_AKEjPFj5aF}MIeqf zYx|Q&ZDXVTU(r?0d$XknfLW~uw&15a#p$EQEiPmUrKLOGWvqcz{g)N28Mni6)myl| zQHIq5mZT@|=)RJ73s7`Stf32dgJgbLwN+r}lsO&Grsj<%7)KVsw%yZqCh zJ5*(n1Cw~gH{0ZYkWGs4?npcH`@5T4TTjw)AvOigJOB+zg_8VA$3s`a^sr9O+-z~Q7*Q!&Q%^mmqZuPCAnjiKNwUDHdBABANys(lZ?bXH(f~j7q&FU=FzlK zAS)}AeIwD&BHE0Da3CGV-4ZSIPM%h4W)a#}y~2bd99LG~SbxIZ<}%`Hr!MCrAz)9! z(CU`cW*Kd3iR9^bdpFUG2`v3^F8b>alaj9oVOc!h!=m#yHAGwJUQH*ayut*e{>)G& zXZ?-&O{iKkeUb4m%2t6;@slRI(r?T1$8}=)SF*Gm;BUNvzy1^WL%05DDdWGQYt6^VAMfwo23A|a#9Fly5Vge|`UMmP3yr0rHYKRlHF3AuwO2L2V=Ffd zH&Xy`5o!XYVr8ZvCcg|zLQjTk;IfZ(XJ)fnjeVaRQ<7_`D>9Lc;i?2x>gIaS%q zvt)K=VwvQqg)S#4NfK-wf=MdsHRMW3#+Zl1Fn!*9&9}#J8rhclxC}-rhvFm(Mk@D* znTEad1AvPtwGXRYzF1DjUr78dEXKRl_!}Wy>qT=)08W2lk?i_*7}goc{DHt7x|LKK zCTw-nYFX(mkw&VWTvzzAL{`2zIB_vckdSmrhKv;5la)m5{_Wo7y#}bSzkzL&%CdRi2i$_x^-BaLo;N}y^5E#=?Xh6r4ZDWl}o14 zOY)+>iD^H9(vwF2nmTXcjIbu!Am)jMLG)Q4ct|)gi>ioa9!Z7C8`vF)8I$-x&O^6N z@;6ygd~7_j=|P2P*=9Y#tH@*1Li@1v%(&&E3_@#n|Ln&Onc#)vO3*NXUshh8AaH^& zrmJ+=q}q4WwYiB8>xvB=7s*kaF?h1!>7NZSkmY10$%yFY>(}Y%#uN@XagJjNFl9l( znM2cAID+A`FdN)MV9F}PuZSvH%7MDb)I5k{A15^G-aL9G_}zVFCK^ww-)*ssdL%lQ zX>nX^c<2I&(j&8`?__i)g?FGmACzll8sG2*WTSs^b3^14;558!qv;k+K(!LimW+1tk5|}zWJ2ZA^p|zY4 zT!k^XYinY77}^oBb3feekf8A(#wy4jxtoJkWoH1{3@D z<=IRKu`ESaj44E0@EdD@@2ar{9`lS!R!lY8jPeQv0hWw4W~id zS;e(fC?-O?d(_+L#Ti$=Ee}klW*z9n-0+eJ+yl}Ifs*2Lk{&HtiMY=f>6mIi7oMiq zB_xD;I>I!AXVF1J?1lp#4NvGeX;2-2?q!4=U+qrb-E|5MuS3O)v&5O#O3!7BHO-); z$}nToROLts017E!W8&^36S70np{g*g4)DiOiP51>rpZhO_A-iG`DCpGc>khQ2pZn=J{d-2iDgoEcIGv)7e{ zxJ`hYY}g%5+V2E|FR^d%@dLRN0k;$TW^8I8nv7Q1bhm8XO!v`s(4NrQtu>yb-w@$x z=DzJ?Qa$G-t+Kt^V1T2SXcht*98+?&OnIC4O-_Uva`0k$Zk3uOG;3&2my!y(j03n@ zN#8;Kqw7zMkdU19$hOtu)3s#+!o7Bh6cr9|l zwL2H|N>+HOjO{WlF3IMhI3>7v3l~nA)jJ#MOziNd>BCVz?ECVS<4*dQIOfCwTMAUObDX09aAq+#TPC5KH?c_ zrqoNSTeZQS05uthidsmeWQBih0W#m={=$FOI{+d!JA6QWp$iQqY4dOrXFmaw&f|}u zj9h89k5*CynSh9iBD=0Z6PPy9WHc+d3 z%U2_e10oZ<;UMTZX0~Bu7`GGl<^y{M`$k8ks`N-eQQ+B-d4ot84K*4{lm3j||A zO2)0>$j69AMhcO(Gc?Qfv2FlQjCB^ni-W+ep-_jiA*nzqnaIPQHL1COM5+B1E>q=a zRFA-SN|{)bsHvQ-gxo`uBVwk}Q%; zXiU*{HdHt)x1Dw|R?^zI1Iumc0o`24h)5WuK*&B%WD^26iO5gPAS7TILKBG_vj3+0 zzDTVhVgCVa_a_6fGE7gt*pA!}4eckeEy(&69QSvaUJhv?wQ>Nle*@dLbm$ft(~0l_ zw#Nasr2zkAEF?l111aAbh=8_*E+6XtGN{CU0z)prG$t1C(b+U@+7pF5IX$7_nTBpG z0n5n>0<1~1I|<=&j3Y#dJqjox1eohgRARL*AI4&30ttOQPgO)#i9vLMc43Yb`rKIB zA?sj6{g$(o|LzPTL)}V^2dUg>#Y}Md7*h_cSMXh2GBT=$MCe}#(Yquz{{Dem&hhqD zN|Hr>rkm4;0HXP(ZCINX4a?VB$0V$z3HI~z+g*4P%XA4DPXZeErz>YEw_Lyj25TjO zcbMRDCK!m~N#m#A<)F_5moL9-zJTd*DxQDT?toCS&iY-WBWG!5=T8UFWYW9LxTN1T z{+QH&w9GXMUA^aYmLJnOztHoZ|1o);k_fq2kv3O<9p-{7)VpY6`n_|>b_SxEnY)3U z$?DJ3`fPSn46>sWRgA5hj3qtXY!G)$IkDbHjguBaKQm%>VL?a3fl+l#GSJlDrfnpD zMWzOv)EZF}j!J1a&lNsuzh11uB*0(P^Tgf4qz76B*A4@QsLA_KT#&0_$Wf!VgEFuW zOqlo8yaZ-ya&1l_%&7;0`-s3qHuDgO_6JL0DR{{R3ViwFb&00000{{{d;LjnM+QjMKUlH9nmMCaHQ3verzUUmOjulD)B-FLgM>uPt~@0Pm%S+Bct;Ccyp9um26^r|*aNnwWO7#LZ2vR9 zYJctiGDc%x{pb4kc(#3?rN+B;-)`MQeC_+*cljBLI}W<9{gr$2KlAjth8a_~7fbp4 z$tw7~z3-s&qqx8o7q~Bz>zk~c{ntKVmc0b`O~+c?2amYhSV8?Pn7uUjI$8T(Vx{aQ zSoY11SWkPf*4z^vt0Si5is}2zR=m3ISRQ*i;-BTdW?bUlCGjf$nsIkMMz*i`nU}uD zbsx6>UG^U_D{+&I37clcP;}-jHrBqYxaU~*pUd`k_d5*cihEdNIcM>;F{1H_eP9gb zXGfRVf-&XpqZKpF-|@%FbH%2MKe?yRW$&%H7vTPB{ExNR!^mokmF3@aU&`LraUW%0 z=RVEM{XXK283%XP^!}#Keecyf=3dU9i!TE+uty>;ejhhkmlG=yKlG;@x!44&gss44 zG5_&_6_*x6%Xi0eU$M6M@m@3~ZZXy#^B14-gWcEu;Pq?9!YwnF$B32pc{K-SGwxq& zmt*kx>EzHExBfA`BbI6y$;su-?$B%QM(pOv-7N=Ze??sOa=!N8`|RBdom}>ckA?k= zY2wR2|CO}w+kULraV&$qPo6o8N#9rFWyZwK@^u|7gTZZa`*Dg`CUF_qrPv=MHbxl6 z8v~8C3}b`+-sU=N%{DyP&oIUNxbm?#+a$+Pd)u~lg_4_7}uf=Tx6#<3`wjGbT00J1lA61IFTy%@8YgIM9DCi_vqm_t=(LlKVVyZht=a zaD}h@UKKgo?5xkO3LluOA{JY&n%}Epr9a(goTJ35SYOLxgmcH>jBau_ zILL|Ehm{zk{V`S{9>Fu<4$|~-2X2(GCWVn3So<($;Xk316&>zVgSNUDm@&eVlvGPhsuDRmO;PY;`OV+zB2dVzIDopV5xti__seSWsc1 z2V)-}jVld5voNBctx;l!ZO0zoksY~rPi((KjVE;9li{}hpe%Sqqb>GJohw$^V5s}?-5!-o(A=q0yLcX}6 z7Op;mywAUP5Qh=Lv~UlX15t@^wh@W*cxw^(eO?#digDQERqvP2h}HPpCJc6ePJg@GZWM9GFasj&-wL};(t_`{zUxyq~MU!!zRyDfVxK z={-Z(k5TQ%+2S&I@hrpmb(Ou=?^x@V?Rv)!cd_s6BQl)W!Fy+Xmi7M9JuDyRPEbjb zVZO)MbRx$H>_(=0i;D|uxr235z|W1&Og#xIB9g&WhW#V}eP^B}yi|Ii&Uzg4>pC*; zJlBzFXCZnUKl4t##4GO%gE6K~l6c!02=^Zul`;Ky(m8v7y~^SHj5fyL+Aof6+yh<1 z#?%=8D{{QZrqWV=Y*UFWWS=Q4?ViGznSDfLjO3g)^O*<7K#xL=Tnx(xq}Ks#^x9imblb1Pn&p}Ni>wxZ-&Hv!D&wZ)iwHyPd>9z zjyH~lgsmE+b-XYh;pdzw2I4bB#dZ7{>MPt7Dg7Sz?VB9B80OE*9W6G}yB#H#n;bu8 zDL%GGIl@e$#&B==4G@Fv`s$ve_L-_k(*s3_?~K7!aXf zXPZxhh>Q`_5{$?(Ff>1Q;BQOt#2)|>xh0Ps(Bds3rEdu8Hv;upo?!{%LLwLTn&L9% z%MgqohG2hP!IORiP@q)54Z+9|RrIN72*Q%bxXko6#}f3HC3r?|`>K(5w}3q2S}~}z z$3DJd`f4n~y+E-YR~UoO`np)S*QJSw+@(c^()5#`|0ek!v&2+={uRK*K1Hm&y-qz{ zz4^9%qW$G}tmJ*nUPhlF4qFwzAg<-t5&v;O`TGd2VE?x>vxuhhR%#|@pAW}5_%B}V z9Z+#fapf2LEO#E+A~&{?xHu$E%emugTf}W_tBeUV60DKZeFm+to?;Zoz@Y39xW?tj zz}<1tSeiS=uEk&V^UZ#?yIm%Gpyw;ztj=s)6)@uG3_FWuC4`^2VgLe}YL($?xiaFd zux#N@HWC-6VCTJFXIxffyp@pwxw~Bfjv7`bKFkjyFo-mpGc*Put|V5UtLHw$Ubq9z zZ;Ve&85dJs$+1G?1{t@w(K}3UB;CGWS@heQUEqyZBRc2exQdI-QsS%z@?p+%5xT%* zx0uvpbUAqq(~E}{=eu(8#0tT)va5)gSZTLQrnu}XXrT)-2Bo<-%`KSYtDY6RH^y0e zUhn1g#s)ZVyLKBpH@1?iLF*BkQ*U9Ie_eE5Wy>72tARxl5xFa%? zizE42yJEC&H^fOuPS^(g9(TzQxOyXNiK*hC*aM_7Y|j&L3*iPW=1tN{oP zR4lS@fFy1P$3)08#g9n7iKgUcAYEQ@R8R5(qAl{D2#3c;;*441xCopRne75I02|$G zisXHepa^n}9>hiF5fzB^CisoOP}mcP7dx5#oJ-;6U&&I1<+(9$ENkOiZD6@;bxT*B zRb(O6sc*1EccMnI!1s??B&EycI}o5Husb$`@&aQ5>|1g* zqTqh`!L!C;K6d8rHtSy;4awyL#spe301sCJ+(oS5HzdS$19`vTtp0-9i_C|aZh|of zh?WUV@>UQSQf-qUz}>lO#D5~^C39{#6A)QBj9A;B2=GoXbCD`GlGIyM;BhrL+6X~O zW%s5lu{UFfX$d57@kzQ80KSBornnRAAb6HHvRsd_v4WWZ3wzLEv*T!N*b5Yv6BHeL zy-*H7>LlP;WGcr&B_IVNe*+v}jvXs#uH@lHq`(Q2#v%&zC-)1 zXPi6?J%GL|b|$gGnqjqXqpxluKoHn_&&0w2U=90^7ns73n-T38gMQpVI6LT^&i4hd z{H*y0jw~6o5-FD!HX7z*sz!{*mvx~+D+NSnp z6S)k|012KV8rmxUeQYyV$MKlVxaaV0X4?_Vhb zdbq0z!#RpX6bvMk5cvB9(`ThltxsHv3FOD!$joiG5@HN2aNxn=O1e&AO@ryF&D#M@ zu$+U%!v>0dydL<`l`)KLFe$OlVoQSPrTU^1_oo?SnSeCUO2TzP{1HBqxIDP!p#yLT zPL8sqan^ESqjauY4~FgQa(*3&jz4CB5iu1I_bEp`BPqPa6v!%Dq>4Ls{W*yN%cr$p zFOKZDxmP&%3d>4Q#mTq1hmTw-1$hFZH_cem&W<$qhn+(3<*#$8Z(T~!>oR$dP-10# z#%$*%oX_Qi04D?UpJdr_?-`$k&AS0g0tCHFB=C{jm`cFcM8Xs43~5e4rzEMOLv^q1 zTTnkp10W^R{NNNKw+K2pChi8?F{v-fu!A@SqERE|tzfG+R|r87qDJUFcU5z821?=-si(-68?$~`geG+-jwPpN|@Hq@Zi5`X%x>H0mcN8nF6(oduPA2yV_B!rYIu2hnd^0#X-dDMn3pXt zuM0iK{AJ4zdrnaEZOAuMHdw^#kS+w>LoY!nI}`7Cg*+v>_BHdOzhbYi%T6lqUzeSE zlH=4Fy^QF7T=x%s*dqfvqc@ORxI9BjiuUreJmIRFB-m%K5e8zc0u~{ANIq&A;Dm1I z2NN`VDv{@LoQIW;qrzzrp%hXc`6yW4x8>@3mjZ3=Mw@4gB_ENUYK!^7YFC;6NI%rqfX)QU0tx88e-eRR|K z*UjPChHSqiJ3bMY27kVuk)Fw`cb#Fl`tn7ir8&>kY7H-ZOf}B{b$B*2I`SbDUSc$n zq%%cxnLo{xbr`x9y|*|*R}?o5*hAOBHs}cs4(aW1bSpCfgOHWLqw$Yw$3Z9^CNi?M z(Muv93L}rHoAR~>Vyqcqn*l=`c~!y;YZ2F?C?(Kyrhd5bXoc4QV;B zIus2mtt<8R@!rOdt0*Oia?>wEQv}#8Vc(MOPhgNU1yK}Vh7XO)PLbDzFuZ^!-j|yY z@}@oXmcJYK4-DB3`Us9vEiwS?3<&m)*rQ2dK*t9B(v*j^QWLusP)$t(a}&3w_%+2Q z4Y2$(#-++Z+y)1iv&OlUuz52r^*;#vKaqhDa0R{%)gUu%~TyKD@BCT{rb;8A^SkespvpgxP!Qx@b1bPO1AZuONDWS+;&`9A3;!Ds zenl9w1vr<>BRqIAo;L&oXgvRRmB*249tv?W>j^7Rn|=&-hwr=?ScFBXS})U7MqwR6 z$PAyfbjP|F3KW(S*Zrx|(;XYM0gz1Oz*&dFS`dBghtw%Um2P-B5So;Sj6`zRu%?En zV`i#X3#X`JPY1_%-_rk8QWMheN}eW&pIj8JroyG6hr3hTmSkc@+IGRd zZcABesy48G2_f9)HdJY~dL_{cuXQW5YI5d^6x4SyUNLlc4wWBOR#Ph15#CHQr)dwH z%95>zJqu}HGgSX%f~g2`5?RiY#mkGio|k_D-1ZtWG0wi$RNdz|6+` zpMP)Y(wdwF+9#@#g)d_yO%$=cU@Vc|G7ckjC$$>TphS5COz&7^WvBr{XKg}9F-z!~ z!04BdEOCY;RRbLVOGrjkKA(8`r=paoCDQ&UC+I@L=40PdvPVhA(E%(0DE<(Y4q=(M zmMnwTG`+q2TU06tltd47kg+dZVKM!Ci^<0GmzZm3E8J|1iWKzzxL9P2;YqS~uvt2I zS-N&pyLZS{#aR*1=*l7W*~9McW(vmIYYH8!WbzK;BKpCa9}x(~s3p=79Fqbfz9jZr zxs-dr;v@5k$gHYCvOKe?D7f6=+c1{2M=bn^BmqJi_lAb4A)kT+yAViRwQxvWa?Sd< z#M+q1v(1TFYAiZEVv`DM)utqWQgA_pwEweVp>X zKNQd+=>i~J$o_dD6H{~E6+W)lVkJrd70`3$Vx^2k&!!9fTB|>Qoe?yYT96ii>xsmO zQIH&E+Lh=h$=QT+A6k*A@Di@k8lVVs^u(FJ#Vu`cocA0Hz>z?rNc4x6f@-`-8x2wJ zt0!F11 z=2F+4wQ)$Hi@GXRAIkV|jk5!kAA->?p?prVvN6%h1E6cvJv64A`PFX45Y&CxBrMm5IZG zf*neUBvyMWg656@DQuS!XqmB9afqq%q8j?E0?v};Zww!V?qM1V7=Hg+MQaeevDwOXw-g^Y@_d9sD$lFRJC?vz6AX+6fzmY6(7d!Nkz}UbQ-Re% zBn4&mVlJAqo}i7(If8|yStS^OUk9j+jU81^gl&tBcrKPt_lFeTsM zlmohJDqcc&WFQw`4%lD1TAn3*%$8KRbkuLl4T^+th*1{_Ur7$Q`g_MMJGYd_n z9F9~8#sYa78>S->#?dX;pQ40Z=4`;21VwggAI*OX9@4lXf7p$V*CQoc>%7R{DK7h3 z#yw@0{$L_gdO&Jnre6>CC(v-s8>Aez(CW4?X)2%RUtG%<)o{iNLkP-iOaDhn8=0p0~9F^~C;0^LI6R6=&LnJ<+d3|83Kuh<2km zKhJoH$y=Hb=ujagx#~k>T!5|w{N~nX&U|zD`>D3##GA`_6K%?zDQ8231H7JGD>f`a zoB2h!Vx?b6@O&XPOUAW4d;lx7zv$NaUxmPvQuVJ(IcgvHR@v61hrcnKk|72-R5Hfd zg`vO7fS-C`UsFf-nJTtJUn}{H4x%4tTM0FfWHniI#mGkS4~+gui>0aUXx36RH8Mgh z34g7_jU)9QSs@XTYBeU8ass{CVia0k!7l*<0V-Wqs#NiylXKm^e-{_5+(E>P5v9j# z*6tmA{+-c&1C_a>f{$Xr$h}_ z7kZ+SU!E+rA?4LtVI;dRN^=b85m}@(1RoHM0g%ysyVTTJ`1ff~FhoEis{FGt0Ae~_ z>Z{pGmJiTIA)*agrE!4Zbv+g-GP>&&A`$?GiAi2aA(SL?{N&1-X6V#< zC;!lH>s$f!DXNl*k}+)w#OyLpqT4Kbl2%$zF@S4*sSHcLFb2Mus7VCQ3_T`|>~tpN zhrF+eieNq6xtJ{Lxm^rf^aj!L-qmt4K>k20Zb@J<{wsO&7>Mc=Sepgg0k|Kil;)ni zk%Ag)&8-LHg++l`#05Q5s_L&h1tT*f04v&@`CfDkCRRvA}E~%A6M9DQ;-n-oj@W8cg558F|rKMR9 z8aHz=crFk^0I-HaZHupN7pL#BPKAC%8TUuZEgK0SM5@X*c$FBRQsToA$I2;8KXe7( z&+ciKPYdf>7m2Kr#5Qc$W!*YtXI48ln>_%E(nk{4loL|-3d^Jz(v&rGXO7%73RN}f zLaa!9T1G1A_3G2OyGxdZdm(*{K=eeL0crIE*mXqp$l^^nO?M#wN5_b(7P|`b8pDD4 zwqVdcX`s5dJ!7~5>`?5gfN~#s1M&{AVs^E)f|PCs=B3SvUL8%bKr_I_wsZFTXxSvK zFA7^@FpK8H!qnz=SGbcauSyRiP-^L3;|BE8#GCJ1=7 znw>9Md3O@BTn{3o%~##Hy+eSzg=^Bl_{y{<%B-;4mvV|KjxAVsYn@sCgK{6GJu~EQFj}iENk1?LATcl;J4WT_dx_$i3G{^(>@%8CFR~W1~w<_T; zLNzP-kC8y@vcw^=6*3WPD*``?;w(6XlcG#hl!-M1Sn0@|Y(E6F*9$pM&z!znm#~C! z0u+#2=_EX+u01Mp_Z6WFN#wo1LaWOp+6)qPz4BnWLG~F37>oZ7q4?L z39Elb%5JBS$J{k#U($qMX>LDRZXfSiz*zJxVqgkrC>er7+F#m$i0DMwhl45 zxouHVV44XBZ@UYVgV;_Y6Pz2oqJb*}ut78w6lJGA}%MWd9%W;A{yUA)l= z-*$6u+0Bj+^NvOzN>J@GLS-GXoNe_Hhuw5Mkf^K?d-^442<)SCD@!wzRN*78$jNsi zT20YYx_y}fQz}RCUnS&hB2pD$UbnO8G7uqD3_u%67KF->DmXdbVg!p@VJ1^IM4*<; z6|n8>St}|b8k10L{=weab9v+WWEI*$R7I*(;zce}l60VwfuyrG53&^L%Hq-{fqdP$vP7Cj`Z38h7E z4FdRy+GS|C;U$enajDahBkazoQJbxOUnv=;djv9Is<~(}6I;c1*P&S1JQKGWh-*&@ zf34KsF6buc*KKEr$WV|8U~gZwH@^`~kV}wOq4u3`_9Z{83w}RozZos zV5DGJ1rwX=`0B+zR1U@pF3zlaS~BJ}EXAdQ3Ez%a&|$<+ci#%iFxN2?;28?~mm%!5 zsD`~cQ1TY^R4JBOa!nZBH5}HJI8> zEpj%8abtfoqyznpz(-h!@`#KQw$@P(sQ-b-TiFca#t#9zm(cqa31w*nz-LDF%zWLzztZ+C`u#8|9&vda6igtxTZ5zvGe z6{^*Uy2)!)bl{Zswk(D!2P27-f#1`67KJXsT(v4RcJysoSa5XL$=DLt-%n2OP zge)sXX<@<3OzhD4?K5RR6KBJ^Ic|ELY%+Rj5rk7gbYPKgD4lx(QI`%y57i4`t@6Z_ z1TP>0S=0SDwTpK?ktQK(QTwzVH7PEOB0F!hK z_uRxZ{`Ce57b@5@j58Vdg?N*3?*vvU5Q_5^3&`ASy(=trr3gP67zkT3xFcKQ$3+Yh zAMBqFH4P$1%zn0V3``O7Nc=8k1}25b52tbXv(ymm;U$OjNTA#u0a0EDm{Gt$!b+@y zr8Uf69)P6X9C0ORLYN;zMmkjt8BgNFLUeBXw_tt|Nr(D%pS%oROMe3AY-Evpl;ET* zpII3ptxiK1EwX;yy6T|QZc579F=n`RaI%sjI|&XL+e_hzjrs#<=#OI36k`?H4x-tG zrkK@k&?uz?{+b{PiBEAHiCN9am5saNtu9EeViA*uWHS&=rlhYE*`^RNi3uI;k1)J^ zrvs7QfdhTlpY<hWP(dTO+{krxfXlc^;b@?PW(Dud87n| z==txQppRq6o^a=+m}6QmNzD2c)sTlpT%DNs%Kh$i6d-kt6jQjI5i7V&%yj8p@@XF$ z9Vhc`wv`!&i32G0JT~RzaA?;5Na8RXKqonCKj6V_?#h4QgX>@LVB&+NAAoSCUh*rC z`VGLR2T5?ra4*ZZ+lKZxnhAPMHp4mc{+!UsPZ*1pc4Ozyd`+B5AX4``T#<800j^A`IN4e>?pd-N z@E_|Xra!;p6JI3Vrav{O4W^Gi~*9IBjy_BYyFUOUCF)=%B;2?L~!F2=xvs3>r-&$ZkFCd8W_rkMXJU{#YCl z?Dm~LBm=%LAW$#`A#_qZKU}}=R7~*8hcKA=DA2Kv=fKDh8PIuLhfeJEZ6)`^5I@Y|cdL5sOivKqb8{@(43}2Qd zJu3w}0%YdnSl-Eg&ZJRa5<@`@oZj*ncCHYa-e5l)4{?Zc0HTi!VNzsEwm`+ehz5#b zo|X}mx0JA;Mr?TR@q;182fXvhZZfM0dqOe9JdO{bu)AdT1r>je15P3ft;qstA?DDN zDk%o7rZE8DPSOUX*x5P9NU7@;}rbi;{Owe%94)L0PE_bQ=tL0?G zjw!xgNj&2+ecn(v-A(k_RCl^q%MhpqPolIfhJ!_yts?raQKn(X#be4gbQuByTLf8Q zL@5CM8QXQ>OdENGw3r1QGS$g&4;K+DK+Yc<;8w=u4&xgE2Yhze6xW5!sw_d29)%GW zc#a>%!>`DQx({@U-DXD_2_8d-7`(uqE}n!`ZXX!Lb730FqIQ}-(37Dyo5k_;seD>Y zQI~;I)sv>!e8Xa9PX4Lf4>^`k=N;dFj+Cyvk4#h|j~RpFtnu@d`LZ=>c2w_2B}*cG zm1eO8+HJbKw3^*ivb|m90Z*#DL(&-{LYl|;s4T^%*F$bzH3zq5CnhhZ`phn#^_Bl$ zR{vjDs!@3Kj(9&$zN3J}$9p}(7iXhcWq}?ZT>iFeq3}V8si6^u$UmJiYD&URGWgE{@La^M>?JJSL?pFt# z6uDZ?cPa5td4Cp&obkA*ir}Uir>xH~E9SltV?+xO=UO#Tp&MCq930up$w$fIi4hmw z*-Gx_QDaB8?Iili+m=QA02R57y?85Xjcx>g0LodbanUdt*hk^`InbhyLK~H&m149J zhTBZUrGC1RC1rGl;9Z32JP;?L?yKb(Bf#RNH7H1&3vUIw4(4lRRD(6j3MdF9o0>VY zTd|b#Fbjw-OB^f)QOKJUQho9c_k8L*6*NTUwUp6fRt+1wutYm5SPQ)+Pfny_@oR)YE2?2#+ zU=qs|#4n>Wikb$2^TAomIw$OiXbpRnjSQs_1c-}w)rjycr}f;{!i`UPTjaoasf_$G z64(%Rz!V9ZpI>lGUEFqMFncZ&c?2QPKy3gX!nkH;vYZ0EYvRn||I2}%1Z=5I%>-v8 zza{Jdr-A4{%1EqJyQptSVU>^uwVG88qlzv{W2O|cy1t&HXB=YON6&>N&qlCG;Cj*; zL);Agdie1)QgJn>Ax3a8HB7H9-m{|Dmk$66!xV@GDPIhJv{ufLHF=w^f;rKrB?@}738S+B97fOT3zROT^8zU(tlEW0cqm>e&70UM*P#LV#0mfD`iIZgyj7JVhnmIICqMZ z$8cmfsRV=;qX)u(ab~-q?F+e+{J6kPeBpqrSVwe_{S`t%(ea$otAh`cG5T?x_rjH| zt~42hP~LV%(T=d+u>~kCfed-{)G!OhFCo3!3%7v9gq_Ge^LBZuld`MMg%tw?m9*}sc9T-0f5NK{9<(??P3lmQ$>I)M;-37`U zi&b)Yt|Bq9mGTm?+eN0j%KI{5_p(7j&zk;onLn}Ea-JeI?Jm¨>F(s)c;}L1L$R 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 Dnj|g7m zs51BLY^Ra%{ALCc+@B?bPh&8js!b5Q^9}5yil*F<7YWRz>3jV9Qg}!u2g;%2CX$3- zCjJI>FA_%VIz7iMervR1LcfU6iiTjH-+Gz8BRZhJjVDFej1CfVjf@O6h9sIdD0?N~< z;FMzFoc1ZUIPy;jxZ2lyy zh+b?>@_Ylo`{*m{Zed@T!a+P^h@#_7djRAKXc%r|&x^#kM#maAcEE{7pXgD{%8{+m ztKw123b)9^Ng^r`6O#4gdWbQGx-meSwafeoKUSa+$!`3gT@ z!zcx5q(r*A9eX|+CM|PJ^pknNuOrXkxhuMvi;k1TLsj2HrO)RJDoK0soVUkeAYxW_ z<{nXSt-y!$E73j6?Cafc13~srX_*sr!Ez%lz5>{h&Lgva-jk;6>1||!H#o*~X7VIh z2A|}R;(WKEN^@xg9H@K}L^z_AD@Gf1?=h^sxp9nRp~t_u@e_Ta>|00y$bb$(i&KJk zZ{!7-79Qtx`SyOGvdpX7?W06J&6?z4h3BGE#WIBlc}C={+HOep7Ug6kq+zE=$qRan z_Z}wwD~nj(c57DC$Zpx zNC&K?>IsokR2H4jpl?K6NJY{r=9}Bzi>C-i^wdnxq|cy>R-}gkYdsnDNG&>Ct*7T5 zoVwLQJ+OYkJwhEb6`3%S;y*o-mL*dMsoT>9ui5a$uG5sp&lQd-MP?Bn)py%y!dt$| zKaKozm1{4$+Yof_UnF(%X;^acwW1`7DDABo2XmJ}>@t%+5{7Hj@q+CmsO86aj*}Kl&af+3VXKNIPKPE4hMCiaCy*p7EL7f1Cc_? z$*gbcx%CyYuW{-r#}dFqs@AZ!Y+8yP)2Fo72e%~A?9;)gj7nW>v&Q6GjhJtU@2x043>iYIL6_<79n!!Mlvh|*Ec|Db=x9G-%t*yb#HHTg#YST zRaL~>lwV(ws-O;l>_Sd+q@0!;uYQ!iSgIJI6|F}*FS%Qk3gI)^4ySVhgV%dL-SkrR z1e4=96O9}RsS_QIT z?z4Jd_S1)qU}#7a?l*-0t6oAc)?$23XuOQ9)CN;f;K^jB+992ifSS*$@p^*2=;{?RTBlXWI?MJIXb=G~w7VoGPHb2oy}+*Oe(nW< z0VzHaA@e2o^+qQmwM3#W;n4_Hj&|dqz}yev3pD)vee)m$-zDqvWG6}{&f{Y>*2DF) z7jWa2E`u!Pl;^U^^?pUH_VaX4#7HA+dsk7^de)-9TF8W9hJ;f@G22&!w~kr}HDo_B zWyKNQY$7V`>S#65OCmHUiO@F>HF)#Hvl6|mvLxU+=2TT?(?IbY~OUZ zcH8|UE@CBsJFo!mC>Tf}nqoWGLW-zlYi`O?lF)-iDSfzUWUVma4j{ne$TarP8MSeb zV83iem9~;D&B$CM^rlY`v5@5+YS*Dtk-=a%Vs7jOa3(K_v+3nIT%`$J`9I>#qZMai zjS(UKa`x4V?)gX&p6ZBH$WaviO(`QKP&L4T_RXWu4W&_OD#0=8Twk?#B%y2BufY4! zAw^J>SgKwzpIgf!*8Pf)g6?PlZ++pDIxsTMH;T2(j0ZlqXv=~w0rH5!{-8v-EA}6h9t+u|y_e3GZ_ll|`77RHF-@Qo<=S?5 z=x&5nJrGr1E3EDZnTjoPg{b#Gm{nmzNT2(11KujraDTEAnuu4N^43kZLtBWl-2jtU z2(+W4BO33X1e0hn62#(IiECMMibSxs<`Eg0#JMQhpQq8``>fm;1Rz{Xn~U zuz3t+5de(u6SkbHHu@f2YQ+xmx&utqJDuLm{miazg4_=r&fIaP(89lMh&Ci%DI;EU zCOj9nL834_viVvBO%E_nl)mf?un@zFUYtC~%&TVFJEY-+^ri$*!xYN#xOJJyD+PxbBxF!iD z%1rD|>BCl>ixMDnN}>f?9ROR5kv8FTR^a-mt-T_k3>T+2mX1-15;2uMS>ALLx}&Rt zA-QsWPZ&5eq6^o*|3%`3;17SHYXOL_dpJlXA>0b|HsY@- zeFg6YwDfRoSxR~Y<>Mz2H(cvhwFq|_oD7R=Iug+&jLW)ekV7Q* zd*|IJ?c9g^7#*zXlikjSL zVkWUsLOw$1!NJULwJ@H~M-^|6vLOjeZmI9vZcWdXc0=GWj^+iJb|;xJ;?R#rAYz1I zvhzevu;0p!8rddKIG7`Hk0j2_H+u-})7F#lL40UwFOjOs&qW;pS$8YH+TT)G?_hAd z+~w?PrZq-f#!3aovX#CFN268Cnt!nlqYk1;KSbuB!n;zW78Fi1A36YG`A8N2{#|W? zYRtmJjbg7U<|`9NS!4DP+t1}o^u zdk{CIxg<&t(0FLjK|#VYw+If(}1(=mIDLzf8cpaMTFt&ALTjCi63S}WN!O$ZD> zYu?|WqPN}|x3VX;`jhBo9uvjrAYIB&GR8}^6fx*k8{_$Z0H#%;D**oh03VA81ONa4 z009360763o00Kv)y-k+vEU;|ZXI5HjXaY=tMS}@2|5b_aZN&NRyRRBm=QlGlLLnh= zH#ajU_Lys|J;vT^uQA5ocVo`KNB823zsLUm{d>mWbN-&S|Gr*-|F!z@{QG|Yz2ff> z`+T>S|L!^%FFY@)*NpnJ=V<4+=aG4yc3yOjaelVeIh|F{Ugt=B!!iGU^!H6$f8YMS z;C%P@f^(ob&wurlIwa3Hf9f2@`RS|!wR4*D`TF-f-}CnV_tt;M*ykkvu4m6NeMKE% zpVQ~dzw>LezByl>t3Sv5dofpaUOeg*b#k2S8G>_Zf8WklL(8MffjFFxAgn(yx_bS zr*JOmeZTs@PUr8m&wGC7?VqRKvo89pD?P97b&$HUdixpx-lV%c%bSdFp}sd)}}2 z8lw85KQ(l9|LP*zdEvP(hW~lbl3KMM&%|O?)s#B(vQ|_r{xfk_;|cSu!86UyvZy&V zYI+TRHh9LYr?Ey>O;6Sv%W|`z&VoE|`S)huxeL9n{_mF8S)W_*)p-96*cs=V5Orzw zUERDhkGWrG#LmR78~Ha>=TPSm?Y!cvLFPH!RxW_U{u0f6sf=mlbnr?$r%trSyjd^EYzstx2%wKS!*XmLt_A zRE*`!ZQ55{^Y9hpP~7^ zLFE^Qw;uaFZ)D_8+`bpXIo7@58hGrVF}R23zn1a%|M1T9|NlD^ei`gDb4l4`wp#Tg z35YYAbszo)tWs$`tI}h|ia|)I{B=&_AcP7nOz_F#ShE2`b}gFAY zYibm*IfrNfr(uAdBkQwQ>b`)`=~#7lT{pQcbdcG`d&>xXvVaz|^+5kJMUWf*%TqdzA^5hC8^6 zp?Dl;kb37T9?swA6n1M>b9`W6pkV(h%v#Nkd8@UWVNS71d97+o*I25|n#7yXUIF5) ziA{M(l3@L}O-cn5*Q#5ok?T+uzUwfHNM3P$*E(y1ys&|96WCqgaO4L-LW)q!@ALW- zdV2@2zCr)oN@k)c_8}-Z$3W80LA?c%KS6jMs3s;6*E>aU7chM8 zEH%eTkSSz7%N7pxe9m*vxXR{waaExei6~dQE0=1vE{O8@P1WckZVY$f)l49!;VAQHmY-*JQO&N^Duw|}{+3ZGgw)3sO&3#@6XktG5tLgt*6+n{mk@nP@~ zUP3CtiJ9DiuoWrJ0b7-OJO0D0&CX+>O@*_OiiN!^SRfrI{F=e(q(ZcYX-Hk zy|Z;{%(OLfW&<{J%f9+VetwWXzo?TX203>~!74{KGz}SJtQ0j3`hn5owbtBlk30M? z<$O(u0o8PD6Wvw9d1Jeplgz0-_dmW$rSk^^Wa7@b zUG1tm>KH3|iq&Kkxl|{~Vkd@e!$YNKE{gfW{j7M1kdo-cbUUkRIIldHqL+4tZL)yI zcptOJK^Ik49(AZK2=6mgL~aEXVjoEzz82A`CW_IZEwV_B?k3|eUrn$#f^1o_e?nF* z$r?e{17*HZXj_M@dfoibX-Mx3GyeGJ3IA$em6DewHML3|ysDdtIJ8o1Q~FEQkMZx{ z|M^{G{r%@@kncdc0_-n;YfSbfgwK-vhl8Eta7L*#sTf`u*^dk(-Ckeyr=i>c$2-s& zs12xjtpZ+||8BuhAo0M*Lm^QXTUHhoMP>1~^bE^8Q>ezbP(!PXv?#n>4=()T(HI5b zg-LM11eK`CEMCHl%YQBZ?r`jYxs`8^{q@hdJ^$^IBF0pdjmn6339jL%dUfxRDiSM3s^Imbn$NfwT#v_W+x@JuC*e}d-*tZ$W-l7Hma=M52cqPj|ejl?uCN!=Ps!Wmhd>JB#Vp2)m( zBKg1F_%p{9)t7je=^R-|FU$lPo{?y<0p3GJwhI059sYDOSuNqc?lVc8x-+Pkpmk3~ zUQ|)GE`L(7!;>&i7&<1Q+94=#Syxw`#F7F1w7ih{SBNR~W`goRDS)K5y%8<(-)pNo zNaS8FvXLJ+fpUfwNz9!$025?m@G%@4Q3SI>Q;>n@ZOON1WOC~C9d?F`*aqKSY{eG6 zk989ko&eXnp*_kan;SLZbAx_@>xMeuVDS zQHM&@e6RKbgWi3nJg(#?Oy}OUu6^Q>;O;yGdVj+uzZ$q6 z#LDND96#6lg5Wv+tB_OW^oK5_l^k5i>|HLfqlsCr_1 z_`!$TspBr5GF?*LNm^`sjVbe}Ibq+&FQhD9RkczW&}txwk5LGf=@ez3*Vo_2`>dr4 zX|m3vBkI?J`=U!cuU5VX1eyHR&mF%6!%q4*z1Dxp{_QG zZ|mHYzhF>fNa;w(VopWDc^>a^^qo*Re<#zv|8hEvU5PX$wtZYAv!_&?_p=iEi(#)3 zqerx2cwgmOWP2qQ>}9onYIqy2$qEN}C_aDKQNLa^F2r7|E|+$1S@!xdJDQaX)d|r# zM%N2Qpgdlws`t2vPk|?nLR=g^+Ktz*5^afHAMdNWg}zJxWY$;o{~Q8EdG*PsRWSN0 zLL&ts!4Z2=_di4?PU9mI)Nz^?6f03}q$rh4;w%cP*{WbU&m(GLm{oGd(Tbk-MyO<% zBdER!iwjzQ=`h|yz7u298kbwsh__@LQ~;)B0j`K=kib(5$O_^Ey-`hmrcuq7?R!GI#Jlim#^_{L^43G5_*m24 zllG{3<}O^G^aPbK_;CC}6$WA;*i+AAx3-#FxWOmas<=@k43}c1(TW8JBgOc(uCv!XwaeZPjVVQuf%G&j+ z>bg=fZhd7HFbzB(Mv573XJ+g)4A1)*^3pymbJb!&#;Np?nBrK10s6Z+IKAqe&+Kc2 zR?G$31TdXMO6P%^xi4Yr4WMi9#Vd?v%~5Sm+`5>Xs6A%gcJOqQ&1K!n_d<9`Xw*0? zIlQ8Ut&c}k;Nj3<4Bkqo9>lU^%eYRBDYFCti#E%JiLnu1B0Lp!4V)9^E!e2G)9ayk zt0wfYj-0s!t~QJWf#aCJS6KAH60!c#GdEt6S%rV9&>_J!Dga(?Z~sI3Q{zO*j8v7e zg`1F;*K|`s1DeOG5XE_^F;XpAW|kH^B1kXkuN_H#jC+iM4NqxUIif{Go62p9x$_^} z<~0hb;=_D#&8lf|z7@7A#!n;tR;$=otGM(;k;gevxIO0lL={_-!I3Oyv>WGckxl33FV%_> zS#3I}Ll7*a);8xIVaS%TZCEOrb3|$2321#GBRK69ae7^*Ia8f;-q-YyADq!>7`#b- z8bd4Oaw`=TbNX?V`+d_jH*KJ>PkY|{S=FM*H>Y7$v+Wx4WI z*;JGH>t>=&qnR>EYt)Itn?XQNo`fM?5TTYN7*!1;i6D$(ddMv=6QNqmVj-NDzt7J_ z_gJ7>36ha%#HiBfEij?=47>GH25C_*Z8KUo(bdX7*WM4yHyt-!2c9nRf*i##;5^lL zhXnW)$x>jP6RCz262iPrj4gdP6^Y3ECz2=!Tr|9y$vehlEgWLmJ?%!Qv{+i~NH{AO?}RlxF_lB3)$smG z1u^2zy6ECZS3?RP$!W^UGKp%Q=z*DlgdbgfPa}qnru{Xph`{c3S=wd)2dNsVOBugy zNluRc-v*_*IFxv%7F(tS9Re6wA7_`oL+2{p1+O%bq*#}8ID|vjy&cvD3`^kwWq{k% z86GC_9JiMg7A>ys! z!J;nU#E@npNZ<$rYKg@|vkKp)kdm0DvKqLHMxInM8-eb;37Ri;1qJilFH40tHB$b$ z`u0Hzb(tvJF$;l5JT9^XAK6`H^{0AJeiG4^E8l5Ea7wz|L`%cv*6w-}a{yhaan}zH z|7X2fA)m$ud?QQ8TNd0$ttE+=mVry{6dl(pxaw6=LteTHw=;g*C^4k^CqgCS^>D3= zcq983)SH~=4#J#Hrj2v_hZ3}7;nA;^kShCojlh>%|N1KUs3^BVdWt2wXtdp_9L?MW z^JN2iGjsj<@T?F~rlpMNaz!W|)nH1k{flE$ngpn90Wm3U)_<%HP49#Fd}0!!Kv+qb z`@0mC5#xD7rqSs4(6?(NQkSu)8yZ3*>@rO?e_8UCLQwULfV1!_&8de2T{Uct#c7)m zI)R8YP-2hRttK@Sky#e$))-U3vz?#B`S~Lf0NsH3lyDXQ+uUiP8^5&0V6LJ0g#y9F zRMis&R1^HjX)UjGScHK8;FGq^L3Qy9s-u$4(yLYlJ}y=A#~7X{H{Sw9>r!2F4WLep z8mgG1hZV`xKz7Y@xCdHHzH=9Qxx=Cqp{3HG@&HGXa0IYG4#n^4% znsAV*F0pB*Z;qCw1{KQWXhWQ!xy&G&<5GN2?Frbdr(}nYm~_y@Vj=y5Eu}fPG8y&= zqF_)xj};@2bCan|Evep50bh43gy#@*MD6Choc@inS_>ftjPnMlKVKToKf6u(W$+92 z(oH3aCVj8a2MmeDlP(PejylL$7%HzrQdb}+$1CUuwl1gbHnI>5;>WmVoS4*gY*c-u z!b`cv*tD60)zld(=Oj(&XBYg^B9l;Y2F%kHjYFOQN0jcjwg-vQ*q2z(bFYLn;18t} z%Wtyi1d`I{z^8Mc7j+me7bBRKn8Y(*3net<5^7;kG?0gP&3T6{ZVAA`u-J2wGGm$` zUE)s9ZG5>E-KvCLp2(2UC<3*;{YTkv?sE9PYYBi4OdhmUIhDJ!+R!17?Ydf2rD-qQ zah26iRym5#xf0n7P?FQvJf*W>Y)R}LXG~jB7RHMEq^4mk2_t5ewbcHyhr^May7oV1 zETppR*g)HlI{|=BI=hW@1pCU{bZ~uXc2W%UCAQ{Mpk5UXIo0l@_v4BEwK+_T?>Vae zQHRpX*|N^_EWhk;`~onUAnzmX`{RQjYKU*n=bhUc=#~f|HwC;4%sI?c}l`x z)G}J(9=lAH5T6(|Q2l_Ks*l9UPbuQDH9LKwottd$_p%C{zbrSM#L$xecgBbywc_c3+?SprBf& z@lu=@b}6=W>YYc^DQJuD_lc{eeqQ(Yd0eB%DqzRyha}~@+B*F`@n?6v0{!mJbC#t3 z6rCi78n^kW>CQ4c@{(mY{uK6yh0*_c=(St5=R(~$rh5rE5x9D~%|9)(JrcG}xJaiOI#kc!2zg)N#3!m05LNxSF6xOni@yL7PyU+~OG~ofBIUe!xV7ssO#{FSH-WgutX{=4U09 zx$JvFe1Ub?(;*%2)vxo#Z^WE)Ul)v%w0{$&(zdN05Emrash*0ft{e{R0`RzN#wVvr z-pj*ZkFOC=9CoxXATFLGTv8kuuh$oQTu*?ooI4<{yCa?pvo5J$>0iSq$wUjF-l%>C z%oh1aLlW133+!>fG570uyc($0j~z4oAzKJ&mec11OzpDU+5SXVAJ*LO=1Hw&oVATq zo%BO2jZ3x`@nP0)*IdC3L`hldE8*WR!l;KOr-tYoGfhpy)k#ea5%o`Xp>$aqoz3&g zu{111)Lr`xd29PydMtpV_rU7Fq*dq(s z0fve)BU5iVtbepGc#vgpf)xgatnb1^d_u6CNt~5cS>WX~eOjb?l*!k z+xRvKUzhCDm0GlfeL~N=Wt_rES+N|4Uoag-b(IOMphu`on5`SaI)6EUbqtQwp3x5S=6$sP zYWO4o5}F^H1HQ<84{@6$Qtx-E6zS!iQG!O?X$od=8wQt!pJxa*#x<}^H4!`x^BTvh z8g18u@{iMIt0PO8*Bh@GZQ_xm`-MmgTcjYwo9P9DGmkt=TQFp48}olUZDc@~b{qM- z|Ls@%sFrDdEBRpNpD8`NLr^hd`YWLEAblAXZ9PNGq#LXl;4#x#WEWZSVNkqMaa*N& zr@1!PHpRR6)od8oXCfu6zhaIMnM*Uw&!*!h41>8{sLdbGEq|;2vLjD&dV9%})d5=s zJAE>AjG1p$@?-WKQnfK~osOTSI1jJwRShZ2dG@|pBFP!QD2cc-1~M6mdZg3McEdev zMAwG3G9u^Ow%jFf#1)KEwRg0Rx;$IG;}OE}V*6h~`*f4-h$Pcwzr+;9n|yXkd}Z1E ztf$0s?`!u%oWp@$+FLb<7XWLJ6$Rfa(RKJ(Qsm|=H1z3n9`G>Jg|Ss3GecyVq=jo$ zV=h}*FPHRjTh~a3ZnBl==JuU7mN00lVFoAEw%aa=WeJnSp3wy+ZgLJeN+Ju1KexU8 zL;2aW9rIrH%Com_Kr$Qs0x*86vCTm#rX?ZF;Fs;+FkC~I9Xy6pT=+MNxYvJ zGKWQ16(1=18#1nKsee>R{yt9NHv};q6o}2Ab;fBI!gWl(54V7?3gF}Z1eA1eHT82z zmTqGNJK$MEw5S#}gBvlBxq{9k7j$$;HdS$OiQ1yV??YsY*# z#;vLzwU+*RrDq_xmJh<1kll8vhRj2^kkWkkZ z$fx*c?x>8q=4RSdP6GifQQIrF8hD@R2spCn*G+YYXDqj3u%E$>ac5(?9d3HSj(5=r zfuC!K4?7m@v2ObbmKobmuA|Q;FuT-xd|sz0`_z%TGfD=Ob^QH!U3(_GJzs^zTg`l` zC^WiTjZ0su1U}#698Dp1q9=k=0;~+nWt1hzAz}=68d;xeZB038T#rg+#2k9mp+oc+ z!AI2A6DG$Hm(C)5P-mt|0S5fx)|!>*Y&&3tb0_C-&LSXR`eT^D4r!WIFjz~|R)%W? zjw{5rgCVl7Y}`gXspE+Xlg)D20aCK-`BKC`RiGk**lb5N`}Xi4IO2p@UFr@l&eq;) zJWPvV`UMe#)BPKo4E0H|w9}JQ3d-BVw9qo!-KUfwKcu_+yH9n1hAC_q91sVKy3?vh5;f*rx{b*@Z1V+%(Y^7InRdZDx@D^ zq+lqmwy#}G=ax8z_8UrnxS?h2pkUN1&K#xwnsHFdHkz1OJM@N+j`2AIU;38hd|4%71 zn^>irj2#KiKCa*%$aO)}H{6kJ>QTjfF`yI-|4VBwXd0-iwp^|2GksjXCO6Zh0f&jYk`=uHD z6BvWeAnK7`yoN#!MGA+gjE**1;P4fSDx}}9 zh@KVbKjhypi%98+Hpj0=EY}MABAX)@7^@{*jnEVcKcug=+x6Ns1zHkAGVq%Xi*2CB ze}mCSMHL%N`p`M3?Wpdj;a#ODc>Lk}2+KKb#`4uA^u_Q%S%9s#Y&9K?3#ZSSVP7(s z3cnjYJWnQted=GojzDXz58(&zO!c@tioZX6PPpu($EpMY*e_lwJq23j`%gOPYG@V` zR6U`Jzta`bqXRw4eqE=c`kP9T8i`s1sI2LUP&H9(elEeE+5#cpP9Kb0*WvJX)51qL z{G%v#2PbVp&u{PH(w|?Ibm^UL85dE_QU7jn^W5nNy%XsSInzO^WBh%!F88wRy3lW^ zZwOjPq6+hCS_1!Rn4!yNDHemg)Q#1kZ0bL~lGk0-!tRdGz$*7xLN6#g-t93fY4 zU)Ou`eLp?Ym<#oQ0Ik{JV@}ttg51@JZ08bedphc zO18N&(Q#1z3<%BHKYWHG*HyU=TLqz)L=;%h!_n{^UNo zBL3wAl&NGbWT>;D+Db62b=j_FZy(Tzw z!J`F26XAC)SW(vbDwQIihmx^IZmiE{OKFJ(hsDz0u?FLKZ93h413oQm*!GALq91n9uTFt# zo6oR4f61#fZ69b*X$L|Dv=2amRH+YT1-h;&h^NR{$2OsM_riu<;|bZEDu39|1;xMs+KkAV+8)29 z<4%-n@1q&($^kX4`>;=e6+BF>?AvJRRiDj9%Sa8@ylIpXNN=55;ndH=svb=;)Qeqt zhotQEZCLhMHA=JC+KO7fC9l${aHyM55u|ud-Fto#-Lyqs_D2J5_~`^RfX`o6eDp;maEgoBR87AJ5qcnCBQJ5&ce>;uX{ug2#ub=3`= z_3y*yt-dN;gy6aaY6u9{qurQdtsK;?l(ER#{rP1|3C8+eDfYRFix!Vt>RXdVdm-nl zm=xFhirCzBP(Y{o?)li*;Ptbss#5(l5Va7LdfP4NAtSW`3dMCXlS--vY1giM?aW^c zh5E_mT1_(|^-AmQwLDeVeqtT|Zbx+wYYHgpZl`mAYZZxX`@#Gc)u|3TS{or1tA7?CRkFGJZ0`rZS2VARIO1l)J6fRD6?t}9kkgU1p7hhws5 z>ku)d80~-!VDRVMK?Ymi)gvTtY7M__qlz)(FP%)|a^c3|oGgFhSjMHx3P7`!wffGHN`N;xb)pY^#$X-l9v6#%%5 zp!$Sj-4Z!v&zA;=k1ju5vYsYUE{)`|eM&%`zwLLPkv%l`j6m%XGhN2?j$N}|886#L ziPwd*jqmhw&lF5^xS{!gp>el?HDUxBx^OV`0K5q`H9yfj*?m!oLE$`uy5ligJLMM6 zB5knlv+gzsORpEf@9)Kr4LB}H_eljHlqEz0SB6?@lI3Xo#Ld#y?b^a$K6x6D@nruH zksQ3UaVObdOAQT4X9w9=#8X_e{+UPT5HbXxBaas-ltD*MO;?tDRK$?ahEjLT`IJ?v zaexDUws}5p$S;iQACzpxr9?2^u#0xikQ63~j9_tnZNE?Qg$e;L!B&6HsiofkK0i?r z_NEK0O8Q3!p$jC}SkhwA+aD$Gobzt&Qr@}PD)zOj{>>|BmGg?GG1OiK*^P9GyZgai z+G@U2w<-JS`?k6+t*$2x4l7natOG+q`Mjj*($qj>>x4ZRx?$WM1_^p#EiF(|dz#Xm zdfA3pZK<@Z!cMtwvUoFP8r~u$6|Ec0%lAqbM^XBI?cAXCV&W`Gkv+O42I(6=!HxmQ z3V2>`^bNhmb`Te~e8Rk@M39CtY0sE=nhQB0fDoBijvc$@3W}>jPtRlf*O5et3Y}v8 zHd6A#z-XSOfB(2Hx0T~GpK@aPQrqpR*xxAHf|a+a7TJ2%)h9o33iRS@XnF0Ue@Y9Z zv+D`&Q1ZDnA~fTt6C-LFSm3;@$xwgRF<>D*bnmM){l2zWep$6N)!Mr7S_i5OP%u4G zmUf2d`nZ;mqf1(?*)EY55=o1QptybP9n-K^JAcd2^^lzep#Jleyg#kOFR&3bMPE+t za((Zo1qq{Ncc;2vA9D>$NyyLbL5Zy{(!nc)Ftur#k9YoL$w3i}0pRH=+;AR#@SXZ6 zpYV;f7Mp^xH!^o9CqmP+gDjqgu0IRAL0CU>-dL#sq3%4uUIocKR@3147V!~SYsr2U zW2Vj%gO`=J`K}y@zB%_=ui|q#@vavYi7-1c6_ePk)}>2vZ1OUz|SKOhrxTN))2;r&7!K?Hm`6ckL>$(!;8; zk!teinl7hf7<4;2-`XnjA(9wzpoQ+;`p|RVs5ef)gqovc^sJ&cY_gQS?7kJ99s`_3 zAGg1Kw`z;f<+GljDAaKv zL&8eMf@O>zsDmMdBxbVHkV_=5WmoEw9y`rSO}B2`!-TH>Pk&tbRQdxPdUQ_~$~+#L zC%&zo&Ck&4Xg)M_JUz=6)~nJ>OT(&?DcY0!g_H(Quls8QtNwwaB#*$yqE(FQ0Oa^I z?7W&+Ja?vIx2jEGGqJa;Ym0R2h-WS5l*Qcg}~Yl!Xk&n80hM)ek$tUpO!!&pt=E&*7sj@Op`?}<;+#N#}9(p^vq zPrPe>?mn@9Rf~;l9-h~~Ret5EeuZ_eYVFdi*Z_*4W0A^EV-&6$3a=b7?8c}MUY)FnLnXtK`QQG#9gHbsISuem z0$d&cdC~f(Y46wn^MBb7yIC3h_k4l>|2f|Z!JhB5rKqh)Gb*iH0b~}N>0ju7T*8(i zno)O@Rc@6?iyb7;Ufi8v2A3_H{gxNIp)N%G>T2+U1(xKbO>kyP6sB+iRb}SFEc- z6oYko6UImtRz6~d&|2G&aqGql4YHTi!iiiTY%%`_oAiH@S}3B!;`&EwDOE~qdBgt~ zTC2I&GLqw5BR<$4tz{n>Aj_VxSvR~rc3-6z@#;5zrUh<{ZW}l>b@tL|wpzLKPBraN zD!*XPG+S@eih^#f=(b}$3zNG1)Bt}VhQ0SH=JfL9a{i!6r_3#dK&uGvq)5w@U;_w|(r1Z- zYU>8F=z2c)gA)8w^Lzzd36)mqmeD-NC`hvX83wjIIUjL>CIpGWg1oU)@q4CyTmu4S z0^^)8!+}{Al`v#8PaYf)5H8c5rdy6;HJ@qqn_etI6pM@ z10J6^i)){nFGX_sdb{=}a#y|ZiOZ5}x7lIZ6g|=kn(b$Yvb~A{?h{+7qfulZOflS< z`%O8YdJ*NI)HQ(3oN+b42g=NirS)fPvLje|D3L38%I%IK2@FmDD;nKWAfZg%cKD#2 zQ~WsXo%Pt_T8f~`GBtYLjVJKZJ=Y4ai=h9g+@`NY$SGj3>UxQMr!Nmg`5$zd5_qRz zoiNvr8+)+JM#s)fQ%84IRckA~kgf`+6UNfc9}@YOwz0b4j-jy8mqM}AZ68fYi8#%`yqUYPI37PW7g7Ku$?ahj&aBb@6uIQ&5J-#ksf&%}gy(J`nRerX@X`=9q zMHAW?A9_(Q+0XvCDG4NXp)Zr8gAYZyWW8M zNOl`bNirhHH>Ld@H~f0qGbm4MCqkLao;^SDXmo9WU;l;mUW{^A*>x)!2%t|}egxRK z@F5vY-Tk{|=IKp1XGGN&%Qvl?1BtQL{j^A#Kf_ZA0XlV&*EFCNl)TXN-rL zQt@NTmLR#?uHXXL*VeXUcsqA z1E?u-*TUHUM;)TMp;pwqn})2buBQy~gC=0JuDo8az+4SgbT@`^0@VRVlcfNnfDYJP zJ0~))gr-YXG5(;Tod!OcAbb@oSFT<3E^s8TGl(TQeI0SF>OUk++AFUANj_t4U7tfR zTe~mn30~=FoJONT1d6egXcV%^iq7e~Q|zTcS+NT{+(z|9yU(hQpCx$}``Nx<=rE8b z-`5Yd@r9NhnNeg^KKW}lv9NnYE08@B)Hw=#)CXNs{Dlrxn%4uR)c4v|(FF&5_ne0@VEe+64E)lP0vWjMzxL2hh?zs0K1(AJYp0wmWPn6q|Us9qzYO9qwGF z`yX6&W>P}4@XQG&Z9GK?&Ef>F{=^lsx1V`2jDe)bkthwfd!z_y%gbd~{{0WKz^t@W znX9+e!ccu%%7v*MC5;CS1UpbAp|J8|Q|6d)RLw%blY?R&TZo@cyAr?+Qy4o zRdy$voqx2=8{}UT_`OUMZKuZ1^VH`0`{LrZ4C(*$`>3cWh?S4}b}c?Ste+n_)6?qr zy|y_%;dDOL!E@}|iyATQ?bg>eXFbaA84}&2OR;1nmgxF>Fl~+UDz+k%FMw|h+__TwXWJlaG9z3?te;=Qb`{_D!fU64> z!;pqoN7O!KyLsKw>V2YNVUV@zehHL1ZPU_#K3Wm4*`H`90{mOoNM@IIC5-rXzjO(r z#X1Y?Aml~Gv>w*oLd9zN{;K*pm2#0~w8$c_z)`|6;A;7zClqU1`x#vRUlIF}+IlVn zigupfo=?|G>o>x`wDTsozlsjUZ*-j@-aDz$eCWcXcIxU zw5dD?<*~7akjiYTni3K^j6!E6XSYc|XkLLQCRf)z}am%yMn2M-RgUA?a2b7Po zrOna#q@DL6N9=4RJZ}w{tOOY}p>dW@bWBHE443j#f+|yVfFm?r?%zI4O{>&MgU#&m z1OmDBry&`*_Cr9so=;ZTf>8SzEn#`1ZEL~McFS~aqY&&ruUP+YuNaONGG`N!0l4Of zKOidTe||bQY6>Zk*!wqhH|C>2D+s> zuah)V(V#4mc=RTv{eH@x!H=}5FF>$w&)mGsKuqf)p{WQEy_%pi*6g`26{RtLPT%<- zvH9;_lx0&=+TLWu&8dwN9@bv$5l(z7=u;Yn}K@22d~) z-0L#l_K`*eXjfH;k=_9*No_|XmHVJLR_BoZw8$Qm9`ph+>Qug%B9wt=vh|J2GyBAi;GuEz z5CdWH%bOzt-$`87+$nKM!M|;Is})K2K)wG0S}|5o1@-wD6ZPaCaXA&QZ*b-#z{sYB zyo%r6xwTNuBxXr9HCC=)?Ru!Yp5@s>?1`l+k)!P7aN74Up+pI22+#%ExEd% zR=rgvgdkX4_9nT@=7O2#%p}QTQAs~Xi{b^^X?GTS+H(X2g@?rN3E#2BE?}L;${uzP z4wad;u6QLXbiYVeCiFk8aA@=J_|qCZrqkv75@xPJ8h#tjor!?3$E+L{3uj%dUqK!t zTb*yS#W7UU1xiD7I;}QFn%ywO77%PwX7f^2>O87eOfcZ`w2YAF9xL51wGc2UfP?o= z=p91;pRAO_J7xuWbuzy(GV&B5guZ;w=qoq0BVU^KCE52I^unNG_G`I{>ULcM7_3v z&!%ej#_M6FYyf#A-{d%eaactt!z3Gvp$Yd7jq;T51f#_3C~BO}n_IV4a%U^T7waW6 z5|@1c(v=jeQ+AQ8@o(vPQGNK4oX}H`-HmV-Poi8EP}>h)jgC+Cx=fj9XsKQKbFECf z97{k4;_SB7j%O|@Z==WUA=FoR5}jzoLXxm`Y5Q%G-og%|)85B0$JUKF_5x-AE4X)u zngq^rorGDF~Yq>Sp*|`FbBX zL5<8*>Go{c+JMO9o}(1>XE|A*^!Ryu_D|^+L9A7`oYxSXpSHA+5fpG7vQ{Wgya)gN zh5xU@5chs38;{$VKGe;Jy7~X?OK*4*^!n=1gx~T^xAnpG}J0Cv_??5$L zqFWHil1T}RB^5Sq`KkzF)PVNCua(nCWrY|?K zTQ8pjHV?xeDkk}YLNP;Sbc@hgvyMp{dJwkAM51h{uInrrnoxWq5Oy|~MLJD(Jc!#0 z4YU|^L!#dL3a#i^fYx3iDu+Z?$po894*EiOO9NKl%lMrX$HWt2?dX}0P)?2O9dia` zp{{FiVx(YAmHEP74H(z$wJ=C`i!ZJdt;>q=&O4~CCN!p`3yMte$>4e;mUW{xpoP(D z@A8{_G2<5wJp??fDDw6q;aCzavLxoMN`?xeK-n|5SXuSX@p^12M`P@ET~c?ibT|DI z8;&z=Ro6|%ADz#N!*R`+ZT!LanpX2DL*pa{zt2Xl!Y7Yy!)n5?J(Vz9?4j7D>124- z>2+}HE@fj#)CB~W7Sc*$r$_n&0GgjGrt%I51Z0BNLlG)HAS;=WyIH&9?VH^S=+k&> zLsStXR!crzB|67P*PPCAO@{uhPZ)nCt7FJeF>+@u_`I%}@dBKTs$LW+Si9K_1Sz(=K5ZnqiF$Ir|l~q5*eyXWr$uFJiQHn(@I%|~C*_tj%3iLL6Ra6f z0!srfZN!&3?lfiRw$MS^VsQmJjSpnr!I)e6NB|Uqec8+~(hL&5Z1~C3^=et=kp&4~ z5+la3Ipf)X9*rWsDOS)HraMfg1b>3P=M+8K9@NcSO!kzo6}G8%dy*vjhs{WK=g@xC z(mrL%cr1hfvPd&MJ7Ab3zt|_5mcyr~Rl5o!SZ|_ydO~}TgE69$J!K1zG>C(~Bnw$c zu5c&vxz79+mQu=2NIMc!?7g1iN7DnuGF^)Z6b0hOj0Aj*u4)-ST&Oj-?8s9DwZ*=1 z;$mZG$*$Xtm%2RF&5fCy=$7)|BMFYGZF(xeylQ#e|M;WVG&Lqcknm&^$KsBCrsw{) zq+@T;<8CH41@&xX4RCufUUeZ0n^NJB0AKy~C~+duq5AhL3DZh`*C&`}>W2oN?2tjx zLY9>s6V(p;E*o_cxe4J5RKTSUQ{&W1Zd@nQ)PRL>jn07fWHuK2U;F)#u|0R8X|?12 zaZmOyY7u2gijiQ*ZnZJMPVqe-$K?#tGM1zjV%*J$1`1kL=uMp9{J9_^R0)}gWIN)A zPcCEETnb{czjeqkxs9!CKP8w)8saw}L!Vn>A3y^Ay4I59f@x_9d2mW2-QtOs*8wue zPN@?;%gi^}`{0CLOQpf7;HenYJ7jn&#AKDVa&v1*rsScL$ya#1M+(ou(SJo5@NwFQ zuu=%~p-FoMyoiXV3D3W^o`26fV31K{cVJ8{O#M zm7PGOKv6MebZ+Tg$b{anAfu)5 zR7-Mcrr*S-kx~NKsT21iNgk>9YL@g;e@qbFPh4=MWx!uNUv;yZ#YZ&m09t3wyK|{2~}$AxO$%~JRzQ$ zKU`EbpIboAPFb=n8LS_sJY4&K0G+g?8aUJd03VA81ONa4009360763o02=@U00000 I000000M+8LNdN!< literal 0 HcmV?d00001 diff --git a/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.fai b/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.fai new file mode 100644 index 000000000..04a438b94 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.fai @@ -0,0 +1,2 @@ +chrM 16571 6 60 61 +chr20 1000000 16861 60 61 diff --git a/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.gzi b/src/test/resources/htsjdk/samtools/reference/Homo_sapiens_assembly18.trimmed.fasta.gz.gzi new file mode 100644 index 0000000000000000000000000000000000000000..a536602330c535046c98712ee884727974910ba2 GIT binary patch literal 264 zcmWe&fB=0jDC0kbzBmWU|HlYs$b5kE|1yCY4$cs9hCj?;{*ozB{%;mAKj1l(|BDsO zx3q-lWBADi=AZ0_@_(>{`Azqs{O=rK{tIJ>xeVVp!Fdbr%(v8m j*vs&l2h6|J0Ofz;1@jxPLir#0!2A{&h`TI~@`L#Ra!DY2 literal 0 HcmV?d00001 diff --git a/src/test/resources/htsjdk/samtools/reference/crlf.fasta b/src/test/resources/htsjdk/samtools/reference/crlf.fasta new file mode 100644 index 000000000..70c878536 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/reference/crlf.fasta @@ -0,0 +1,4 @@ +>a test CR+LF +ACTG +>b test CR+LF +ACTG diff --git a/src/test/resources/htsjdk/samtools/reference/crlf.fasta.fai b/src/test/resources/htsjdk/samtools/reference/crlf.fasta.fai new file mode 100644 index 000000000..923386e04 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/reference/crlf.fasta.fai @@ -0,0 +1,2 @@ +a 4 15 4 6 +b 4 36 4 5 diff --git a/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta b/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta new file mode 100644 index 000000000..24cff02a9 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta @@ -0,0 +1,4 @@ +>a test white space +ACTG +>b test whitespace +ACTG diff --git a/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta.fai b/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta.fai new file mode 100644 index 000000000..bb15aa584 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/reference/header_with_white_space.fasta.fai @@ -0,0 +1,2 @@ +a 4 20 4 5 +b 4 44 4 5 From 909ac4d300e1d569534befd6b1ccf535e33bc0f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Tue, 25 Apr 2017 22:28:50 +0200 Subject: [PATCH 103/137] More informative IO error for FASTQ reader (#865) --- src/main/java/htsjdk/samtools/fastq/FastqReader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/fastq/FastqReader.java b/src/main/java/htsjdk/samtools/fastq/FastqReader.java index d5d8f1889..76ead119d 100755 --- a/src/main/java/htsjdk/samtools/fastq/FastqReader.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqReader.java @@ -136,7 +136,7 @@ private FastqRecord readNextRecord() { return frec ; } catch (IOException e) { - throw new SAMException(String.format("Error reading fastq '%s'", getAbsolutePath()), e); + throw new SAMException(error(e.getMessage()), e); } } @@ -177,7 +177,7 @@ public void close() { try { reader.close(); } catch (IOException e) { - throw new SAMException("IO problem in fastq file " + getAbsolutePath(), e); + throw new SAMException(error(e.getMessage()), e); } } From ad8aa02aa2ed02b9cb2bb8801c3e6897e91ec3f6 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Tue, 2 May 2017 14:04:54 -0400 Subject: [PATCH 104/137] Yf add unknown so (#862) - added a "unknown" enum value to the SortOrder enum. It behaves exactly like unsorted, but the sam-spec allows for it. - wrapped getSortOrder() with a try-catch to translate any unknown values to 'unknown' - added an error for non-conforming tags. - added to the validation a check for both SO and GO to see that they are legal values. - added tests covering SO and GO tags --- src/main/java/htsjdk/samtools/SAMFileHeader.java | 75 ++++++++++++++-------- .../java/htsjdk/samtools/SAMTextHeaderCodec.java | 19 ++++++ .../java/htsjdk/samtools/SAMValidationError.java | 3 + .../java/htsjdk/samtools/ValidateSamFileTest.java | 16 ++++- .../cram/lossy/QualityScorePreservationTest.java | 4 +- 5 files changed, 87 insertions(+), 30 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMFileHeader.java b/src/main/java/htsjdk/samtools/SAMFileHeader.java index b94598185..f2750d4cc 100644 --- a/src/main/java/htsjdk/samtools/SAMFileHeader.java +++ b/src/main/java/htsjdk/samtools/SAMFileHeader.java @@ -24,6 +24,8 @@ package htsjdk.samtools; +import htsjdk.samtools.util.CollectionUtil; +import htsjdk.samtools.util.Log; import htsjdk.samtools.util.StringLineReader; import java.io.StringWriter; @@ -47,14 +49,17 @@ public static final String SORT_ORDER_TAG = "SO"; public static final String GROUP_ORDER_TAG = "GO"; public static final String CURRENT_VERSION = "1.5"; - public static final Set ACCEPTABLE_VERSIONS = - new HashSet(Arrays.asList("1.0", "1.3", "1.4", "1.5")); + public static final Set ACCEPTABLE_VERSIONS = CollectionUtil.makeSet("1.0", "1.3", "1.4", "1.5"); + private SortOrder sortOrder = null; + private GroupOrder groupOrder = null; + + private static final Log log = Log.getInstance(SAMFileHeader.class); /** * These tags are of known type, so don't need a type field in the text representation. */ public static final Set STANDARD_TAGS = - new HashSet(Arrays.asList(VERSION_TAG, SORT_ORDER_TAG, GROUP_ORDER_TAG)); + new HashSet<>(Arrays.asList(VERSION_TAG, SORT_ORDER_TAG, GROUP_ORDER_TAG)); @Override Set getStandardTags() { @@ -65,11 +70,11 @@ * Ways in which a SAM or BAM may be sorted. */ public enum SortOrder { - unsorted(null), queryname(SAMRecordQueryNameComparator.class), coordinate(SAMRecordCoordinateComparator.class), - duplicate(SAMRecordDuplicateComparator.class); // NB: this is not in the SAM spec! + duplicate(SAMRecordDuplicateComparator.class), // NB: this is not in the SAM spec! + unknown(null); private final Class comparator; @@ -106,16 +111,14 @@ public SAMRecordComparator getComparatorInstance() { none, query, reference } - private List mReadGroups = - new ArrayList(); - private List mProgramRecords = new ArrayList(); - private final Map mReadGroupMap = - new HashMap(); - private final Map mProgramRecordMap = new HashMap(); + private List mReadGroups = new ArrayList<>(); + private List mProgramRecords = new ArrayList<>(); + private final Map mReadGroupMap = new HashMap<>(); + private final Map mProgramRecordMap = new HashMap<>(); private SAMSequenceDictionary mSequenceDictionary = new SAMSequenceDictionary(); - final private List mComments = new ArrayList(); + final private List mComments = new ArrayList<>(); private String textHeader; - private final List mValidationErrors = new ArrayList(); + private final List mValidationErrors = new ArrayList<>(); public SAMFileHeader() { setAttribute(VERSION_TAG, CURRENT_VERSION); @@ -128,11 +131,11 @@ public SAMFileHeader(final SAMSequenceDictionary dict) { } public String getVersion() { - return (String) getAttribute("VN"); + return getAttribute(VERSION_TAG); } public String getCreator() { - return (String) getAttribute("CR"); + return getAttribute("CR"); } public SAMSequenceDictionary getSequenceDictionary() { @@ -249,26 +252,47 @@ public SAMProgramRecord createProgramRecord() { } public SortOrder getSortOrder() { - final String so = getAttribute("SO"); - if (so == null || so.equals("unknown")) { - return SortOrder.unsorted; + if (sortOrder == null) { + final String so = getAttribute(SORT_ORDER_TAG); + if (so == null) { + sortOrder = SortOrder.unsorted; + } else { + try { + return SortOrder.valueOf(so); + } catch (IllegalArgumentException e) { + log.warn("Found non conforming header SO tag: " + so + ". Treating as 'unknown'."); + sortOrder = SortOrder.unknown; + } + } } - return SortOrder.valueOf((String) so); + return sortOrder; } public void setSortOrder(final SortOrder so) { - setAttribute("SO", so.name()); + sortOrder = so; + setAttribute(SORT_ORDER_TAG, so.name()); } public GroupOrder getGroupOrder() { - if (getAttribute("GO") == null) { - return GroupOrder.none; + if (groupOrder == null) { + final String go = getAttribute(GROUP_ORDER_TAG); + if (go == null) { + groupOrder = GroupOrder.none; + } else { + try { + return GroupOrder.valueOf(go); + } catch (IllegalArgumentException e) { + log.warn("Found non conforming header GO tag: " + go + ". Treating as 'none'."); + groupOrder = GroupOrder.none; + } + } } - return GroupOrder.valueOf((String)getAttribute("GO")); + return groupOrder; } public void setGroupOrder(final GroupOrder go) { - setAttribute("GO", go.name()); + groupOrder = go; + setAttribute(GROUP_ORDER_TAG, go.name()); } /** @@ -372,7 +396,7 @@ public String getSAMString() { public static class PgIdGenerator { private int recordCounter; - private final Set idsThatAreAlreadyTaken = new HashSet(); + private final Set idsThatAreAlreadyTaken = new HashSet<>(); public PgIdGenerator(final SAMFileHeader header) { for (final SAMProgramRecord pgRecord : header.getProgramRecords()) { @@ -400,7 +424,6 @@ public String getNonCollidingId(final String recordId) { idsThatAreAlreadyTaken.add(newId); return newId; } - } } } diff --git a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java index 402ea3ce8..908e8360b 100644 --- a/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java +++ b/src/main/java/htsjdk/samtools/SAMTextHeaderCodec.java @@ -228,6 +228,25 @@ private void parseHDLine(final ParsedHeaderLine parsedHeaderLine) { if (!parsedHeaderLine.requireTag(SAMFileHeader.VERSION_TAG)) { return; } + + final String soString = parsedHeaderLine.getValue(SAMFileHeader.SORT_ORDER_TAG); + try { + if (soString != null) SAMFileHeader.SortOrder.valueOf(soString); + } catch (IllegalArgumentException e) { + reportErrorParsingLine(HEADER_LINE_START + parsedHeaderLine.getHeaderRecordType() + + " line has non-conforming SO tag value: "+ soString + ".", + SAMValidationError.Type.HEADER_TAG_NON_CONFORMING_VALUE, null); + } + + final String goString = parsedHeaderLine.getValue(SAMFileHeader.GROUP_ORDER_TAG); + try { + if (goString != null) SAMFileHeader.GroupOrder.valueOf(goString); + } catch (IllegalArgumentException e) { + reportErrorParsingLine(HEADER_LINE_START + parsedHeaderLine.getHeaderRecordType() + + " line has non-conforming GO tag value: "+ goString + ".", + SAMValidationError.Type.HEADER_TAG_NON_CONFORMING_VALUE, null); + } + transferAttributes(mFileHeader, parsedHeaderLine.mKeyValuePairs); } diff --git a/src/main/java/htsjdk/samtools/SAMValidationError.java b/src/main/java/htsjdk/samtools/SAMValidationError.java index d560b119e..452e92cf5 100644 --- a/src/main/java/htsjdk/samtools/SAMValidationError.java +++ b/src/main/java/htsjdk/samtools/SAMValidationError.java @@ -171,6 +171,9 @@ HEADER_RECORD_MISSING_REQUIRED_TAG, + /** Header tag contains illegal value */ + HEADER_TAG_NON_CONFORMING_VALUE, + /** Date string is not ISO-8601 */ INVALID_DATE_STRING(Severity.WARNING), diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index 16bd6e1ce..292758b8c 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -499,10 +499,24 @@ public void duplicateReadsOutOfOrder() throws Exception { "@RG\tID:0\tSM:Hi,Mom!\n" + "E\t147\tchr1\t15\t255\t10M\t=\t2\t-30\tCAACAGAAGC\t)'.*.+2,))\tU2:Z:CAA"; + final String SOTagCorrectlyProcessTestData = + "@HD\tVN:1.0\tSO:NOTKNOWN\n" + + "@SQ\tSN:chr1\tLN:101\n" + + "@RG\tID:0\tSM:Hi,Mom!\n" + + "E\t147\tchr1\t15\t255\t10M\t=\t2\t-30\tCAACAGAAGC\t)'.*.+2,))\tU2:Z:CAA"; + + final String GOTagCorrectlyProcessTestData = + "@HD\tVN:1.0\tGO:NOTKNOWN\n" + + "@SQ\tSN:chr1\tLN:101\n" + + "@RG\tID:0\tSM:Hi,Mom!\n" + + "E\t147\tchr1\t15\t255\t10M\t=\t2\t-30\tCAACAGAAGC\t)'.*.+2,))\tU2:Z:CAA"; + return new Object[][]{ {E2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.E2_BASE_EQUALS_PRIMARY_BASE}, {E2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_E2_LENGTH}, - {U2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_U2_LENGTH} + {U2TagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_U2_LENGTH}, + {SOTagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.HEADER_TAG_NON_CONFORMING_VALUE}, + {GOTagCorrectlyProcessTestData.getBytes(), SAMValidationError.Type.HEADER_TAG_NON_CONFORMING_VALUE} }; } diff --git a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java index 575485e82..a33766762 100644 --- a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java +++ b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java @@ -97,12 +97,10 @@ public void test2() { } } - private SAMFileHeader samFileHeader = new SAMFileHeader(); - private SAMRecord buildSAMRecord(String seqName, String line) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - baos.write("@HD\tVN:1.0\tGO:none SO:coordinate\n".getBytes()); + baos.write("@HD\tVN:1.0\tGO:none\tSO:coordinate\n".getBytes()); baos.write(("@SQ\tSN:" + seqName + "\tLN:247249719\n").getBytes()); baos.write(line.replaceAll("\\s+", "\t").getBytes()); baos.close(); From dd78f7717a0b1b260727cd990b2ad0e583d0c40e Mon Sep 17 00:00:00 2001 From: Len Trigg Date: Wed, 3 May 2017 06:54:24 +1200 Subject: [PATCH 105/137] Obey the tmpDir setting in several constructors that currently ignore it (#826) * Obey the tmpDir setting in several constructors that currently ignore it. As part of this, made one constructor follow the existing convention of calling initWriter() (This private method had an unused parameter, which has been removed), and ensured that both initWriter and initializeBAMWriter both call setTempDirectory() when needed. * Test that setMaxRecordsInRam and setTempDirectory settings on SAMFileWriterFactory make it to the writer implementation --- .../java/htsjdk/samtools/SAMFileWriterFactory.java | 36 +++++++++++++--------- .../java/htsjdk/samtools/SAMFileWriterImpl.java | 10 +++++- .../htsjdk/samtools/SAMFileWriterFactoryTest.java | 24 ++++++++++++++- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMFileWriterFactory.java b/src/main/java/htsjdk/samtools/SAMFileWriterFactory.java index b788fbe89..30b36d7b3 100644 --- a/src/main/java/htsjdk/samtools/SAMFileWriterFactory.java +++ b/src/main/java/htsjdk/samtools/SAMFileWriterFactory.java @@ -174,6 +174,14 @@ public SAMFileWriterFactory setMaxRecordsInRam(final int maxRecordsInRam) { } /** + * Gets the maximum number of records held in RAM before spilling to disk during sorting. + * @see #setMaxRecordsInRam(int) + */ + public int getMaxRecordsInRam() { + return maxRecordsInRam; + } + + /** * Turn on or off the use of asynchronous IO for writing output SAM and BAM files. If true then * each SAMFileWriter creates a dedicated thread which is used for compression and IO activities. */ @@ -211,6 +219,14 @@ public SAMFileWriterFactory setTempDirectory(final File tmpDir) { } /** + * Gets the temporary directory that will be used when sorting data. + * @see #setTempDirectory(File) + */ + public File getTempDirectory() { + return tmpDir; + } + + /** * Set the flag output format only when writing text. * Default value: [[htsjdk.samtools.SAMTextWriter.samFlagFieldOutput.DECIMAL]] */ @@ -253,7 +269,6 @@ public SAMFileWriter makeBAMWriter(final SAMFileHeader header, final boolean pre if (this.createIndex && !createIndex) { log.warn("Cannot create index for BAM because output file is not a regular file: " + outputFile.getAbsolutePath()); } - if (this.tmpDir != null) ret.setTempDirectory(this.tmpDir); initializeBAMWriter(ret, header, presorted, createIndex); if (this.useAsyncIo) return new AsyncSAMFileWriter(ret, this.asyncOutputBufferSize); @@ -268,6 +283,7 @@ private void initializeBAMWriter(final BAMFileWriter writer, final SAMFileHeader if (maxRecordsInRam != null) { writer.setMaxRecordsInRam(maxRecordsInRam); } + if (this.tmpDir != null) writer.setTempDirectory(this.tmpDir); writer.setHeader(header); if (createIndex && writer.getSortOrder().equals(SAMFileHeader.SortOrder.coordinate)) { writer.enableBamIndexConstruction(); @@ -294,14 +310,7 @@ public SAMFileWriter makeSAMWriter(final SAMFileHeader header, final boolean pre ? new SAMTextWriter(new Md5CalculatingOutputStream(new FileOutputStream(outputFile, false), new File(outputFile.getAbsolutePath() + ".md5")), samFlagFieldOutput) : new SAMTextWriter(outputFile, samFlagFieldOutput); - ret.setSortOrder(header.getSortOrder(), presorted); - if (maxRecordsInRam != null) { - ret.setMaxRecordsInRam(maxRecordsInRam); - } - ret.setHeader(header); - - if (this.useAsyncIo) return new AsyncSAMFileWriter(ret, this.asyncOutputBufferSize); - else return ret; + return initWriter(header, presorted, ret); } catch (final IOException ioe) { throw new RuntimeIOException("Error opening file: " + outputFile.getAbsolutePath()); } @@ -324,7 +333,7 @@ public SAMFileWriter makeSAMWriter(final SAMFileHeader header, final boolean pre if (samFlagFieldOutput == SamFlagField.NONE) { samFlagFieldOutput = Defaults.SAM_FLAG_FIELD_FORMAT; } - return initWriter(header, presorted, false, new SAMTextWriter(stream, samFlagFieldOutput)); + return initWriter(header, presorted, new SAMTextWriter(stream, samFlagFieldOutput)); } /** @@ -338,24 +347,23 @@ public SAMFileWriter makeSAMWriter(final SAMFileHeader header, final boolean pre */ public SAMFileWriter makeBAMWriter(final SAMFileHeader header, final boolean presorted, final OutputStream stream) { - return initWriter(header, presorted, true, new BAMFileWriter(stream, null, this.getCompressionLevel(), this.deflaterFactory)); + return initWriter(header, presorted, new BAMFileWriter(stream, null, this.getCompressionLevel(), this.deflaterFactory)); } /** * Initialize SAMTextWriter or a BAMFileWriter and possibly wrap in AsyncSAMFileWriter - * * @param header entire header. Sort order is determined by the sortOrder property of this arg. * @param presorted if true, SAMRecords must be added to the SAMFileWriter in order that agrees with header.sortOrder. - * @param binary do we want to generate a BAM or a SAM * @param writer SAM or BAM writer to initialize and maybe wrap. */ - private SAMFileWriter initWriter(final SAMFileHeader header, final boolean presorted, final boolean binary, + private SAMFileWriter initWriter(final SAMFileHeader header, final boolean presorted, final SAMFileWriterImpl writer) { writer.setSortOrder(header.getSortOrder(), presorted); if (maxRecordsInRam != null) { writer.setMaxRecordsInRam(maxRecordsInRam); } + if (this.tmpDir != null) writer.setTempDirectory(this.tmpDir); writer.setHeader(header); if (this.useAsyncIo) return new AsyncSAMFileWriter(writer, this.asyncOutputBufferSize); diff --git a/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java b/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java index 5e0ecdb45..31a8604dc 100644 --- a/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java +++ b/src/main/java/htsjdk/samtools/SAMFileWriterImpl.java @@ -111,7 +111,11 @@ void setMaxRecordsInRam(final int maxRecordsInRam) { } this.maxRecordsInRam = maxRecordsInRam; } - + + int getMaxRecordsInRam() { + return maxRecordsInRam; + } + /** * When writing records that are not presorted, specify the path of the temporary directory * for spilling to disk. Must be called before setHeader(). @@ -123,6 +127,10 @@ void setTempDirectory(final File tmpDir) { } } + File getTempDirectory() { + return tmpDir; + } + /** * Must be called before addAlignment. Header cannot be null. */ diff --git a/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java b/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java index 8c15a1656..0b8d7b5ac 100644 --- a/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java +++ b/src/test/java/htsjdk/samtools/SAMFileWriterFactoryTest.java @@ -167,7 +167,29 @@ private void createSmallBamToOutputStream(final OutputStream outputStream,boolea fillSmallBam(writer); writer.close(); } - + + @Test(description="check that factory settings are propagated to writer") + public void testFactorySettings() throws Exception { + final SAMFileWriterFactory factory = new SAMFileWriterFactory(); + factory.setCreateIndex(false); + factory.setCreateMd5File(false); + final File wontBeUsed = new File("wontBeUsed.tmp"); + final int maxRecsInRam = 271828; + factory.setMaxRecordsInRam(maxRecsInRam); + factory.setTempDirectory(wontBeUsed); + final SAMFileHeader header = new SAMFileHeader(); + header.setSortOrder(SAMFileHeader.SortOrder.coordinate); + header.addSequence(new SAMSequenceRecord("chr1", 123)); + try (final SAMFileWriter writer = factory.makeBAMWriter(header, false, new ByteArrayOutputStream())) { + Assert.assertEquals(maxRecsInRam, ((SAMFileWriterImpl) writer).getMaxRecordsInRam()); + Assert.assertEquals(wontBeUsed, ((SAMFileWriterImpl) writer).getTempDirectory()); + } + try (final SAMFileWriter writer = factory.makeSAMWriter(header, false, new ByteArrayOutputStream())) { + Assert.assertEquals(maxRecsInRam, ((SAMFileWriterImpl) writer).getMaxRecordsInRam()); + Assert.assertEquals(wontBeUsed, ((SAMFileWriterImpl) writer).getTempDirectory()); + } + } + private int fillSmallBam(SAMFileWriter writer) { final SAMRecordSetBuilder builder = new SAMRecordSetBuilder(); builder.addUnmappedFragment("HiMom!"); From e1cf07eb3f98843c0d0320b8b0e147ae8fc62e3c Mon Sep 17 00:00:00 2001 From: Daniel Cameron Date: Wed, 17 May 2017 04:38:47 +1000 Subject: [PATCH 106/137] Defensive programming around test case race condition to address #776 (#790) --- .../java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java b/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java index ce4d44599..e35dadc94 100644 --- a/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java +++ b/src/test/java/htsjdk/samtools/util/AsyncBufferedIteratorTest.java @@ -74,9 +74,15 @@ public void testBackgroundBlocks() throws InterruptedException { TestCloseableIterator it = new TestCloseableIterator(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); AsyncBufferedIterator abi = new AsyncBufferedIterator(it, 3, 2, "testBackgroundBlocks"); Assert.assertNotNull(getThreadWithName("testBackgroundBlocks")); - Thread.sleep(10); // how do we write this test and not be subject to race conditions? + // how do we write this test and not be subject to race conditions? // should have read 9 records: 2*3 in the buffers, and another 3 read but - // blocking waiting to be added + // blocking waiting to be added + for (int i = 0; i < 64; i++) { + if (it.consumed() >= 9) { + break; + } + Thread.sleep(1); + } Assert.assertEquals(it.consumed(), 9); abi.close(); } From e2801eeeb31e439b050da77195a09daf0e727529 Mon Sep 17 00:00:00 2001 From: jacarey Date: Tue, 16 May 2017 15:41:57 -0400 Subject: [PATCH 107/137] Adding OpenJDK8 to travis configuration --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 270855ea7..a1c1b2bcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ cache: - $HOME/.m2 jdk: - oraclejdk8 + - openjdk8 script: ./gradlew test jacocoTestReport; after_success: - bash <(curl -s https://codecov.io/bash) From d7bae17d96601ac06d79c0cbe53923eddf3846e1 Mon Sep 17 00:00:00 2001 From: Chris Norman Date: Fri, 26 May 2017 12:55:03 -0400 Subject: [PATCH 108/137] Fix ordering of assignments in FastQReader constructor. (#878) --- .../java/htsjdk/samtools/fastq/FastqReader.java | 2 +- .../samtools/fastq/FastqReaderWriterTest.scala | 29 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/fastq/FastqReader.java b/src/main/java/htsjdk/samtools/fastq/FastqReader.java index 76ead119d..c5d52f8dc 100755 --- a/src/main/java/htsjdk/samtools/fastq/FastqReader.java +++ b/src/main/java/htsjdk/samtools/fastq/FastqReader.java @@ -90,8 +90,8 @@ public FastqReader(final BufferedReader reader) { public FastqReader(final File file, final BufferedReader reader,boolean skipBlankLines) { this.fastqFile = file; this.reader = reader; - this.nextRecord = readNextRecord(); this.skipBlankLines = skipBlankLines; + this.nextRecord = readNextRecord(); } public FastqReader(final File file, final BufferedReader reader) { diff --git a/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala b/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala index 60e08efbd..00f62e91a 100644 --- a/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala +++ b/src/test/scala/htsjdk/samtools/fastq/FastqReaderWriterTest.scala @@ -3,7 +3,7 @@ package htsjdk.samtools.fastq import java.io.{BufferedReader, File, StringReader} import htsjdk.UnitSpec -import htsjdk.samtools.SAMUtils +import htsjdk.samtools.{SAMException, SAMUtils} import htsjdk.samtools.util.IOUtil import scala.util.Random @@ -106,6 +106,33 @@ class FastqReaderWriterTest extends UnitSpec { in.close() } + it should "honor skipBlankLines when requested" in { + val fastq = + """ + | + |@SL-XBG:1:1:4:1663#0/2 + |NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN + |+SL-XBG:1:1:4:1663#0/2 + |BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + """.stripMargin + val reader = new BufferedReader(new StringReader(fastq)) + val in = new FastqReader(null, reader, true) + while (in.hasNext) in.next() + } + + it should "fail on blank lines when skipBlankLines is false" in { + val fastq = + """ + | + |@SL-XBG:1:1:4:1663#0/2 + |NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN + |+SL-XBG:1:1:4:1663#0/2 + |BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB + """.stripMargin + val reader = new BufferedReader(new StringReader(fastq)) + an[SAMException] shouldBe thrownBy { val in = new FastqReader(null, reader, false) } + } + it should "fail on a truncated file" in { val fastq = """ From 0be8345e18b41a232a239703e6c48fc4916a0f41 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Fri, 26 May 2017 22:21:46 -0400 Subject: [PATCH 109/137] - fixed a small NPE in MD5CalculatingOutputStream (#881) --- src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java index f3b3f0779..8b4c643a3 100755 --- a/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/Md5CalculatingOutputStream.java @@ -68,7 +68,7 @@ public Md5CalculatingOutputStream(OutputStream os, Path digestFile) { } public Md5CalculatingOutputStream(OutputStream os, File digestFile) { - this(os, digestFile.toPath()); + this(os, digestFile == null ? (Path) null : digestFile.toPath()); } @Override From cc8a1a13f7b0a4af5273fad7d8708dc1f871e5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Sat, 27 May 2017 19:29:08 +0200 Subject: [PATCH 110/137] Index.writeBasedOnFeaturePath throws instead of silently failing (#841) * **Breaking Change** This is an API change. Code relying on the old behavior will have to be updated. We believe this should have minimal impact and most clients using Index will not have to make any changes. Anyone with their own implementation of Index should update to match the new behavior. * Changing behavior of Index.writeBasedOnFeaturePath() to throw instead of silently failing when it was unable to write an index. --- src/main/java/htsjdk/tribble/index/AbstractIndex.java | 3 +-- src/main/java/htsjdk/tribble/index/Index.java | 3 ++- src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java | 6 ++---- src/test/java/htsjdk/tribble/index/IndexTest.java | 10 ++++++++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/htsjdk/tribble/index/AbstractIndex.java b/src/main/java/htsjdk/tribble/index/AbstractIndex.java index b1cc1364c..ac90e5d2b 100644 --- a/src/main/java/htsjdk/tribble/index/AbstractIndex.java +++ b/src/main/java/htsjdk/tribble/index/AbstractIndex.java @@ -386,8 +386,7 @@ public void write(final Path idxPath) throws IOException { @Override public void writeBasedOnFeaturePath(final Path featurePath) throws IOException { if (!Files.isRegularFile(featurePath)) { - logger.warn("Index not written into ", featurePath); - return; + throw new IOException("Cannot write based on a non-regular file: " + featurePath.toUri()); } write(Tribble.indexPath(featurePath)); } diff --git a/src/main/java/htsjdk/tribble/index/Index.java b/src/main/java/htsjdk/tribble/index/Index.java index c5a63ff6e..51982c6d2 100644 --- a/src/main/java/htsjdk/tribble/index/Index.java +++ b/src/main/java/htsjdk/tribble/index/Index.java @@ -92,11 +92,11 @@ public default void write(final File idxFile) throws IOException { /** * Write an appropriately named and located Index file based on the name and location of the featureFile. - * If featureFile is not a normal file, the index will silently not be written. * * Default implementation delegates to {@link #writeBasedOnFeaturePath(Path)} * * @param featureFile + * @throws IOException if featureFile is not a normal file. */ public default void writeBasedOnFeatureFile(File featureFile) throws IOException { writeBasedOnFeaturePath(featureFile.toPath()); @@ -107,6 +107,7 @@ public default void writeBasedOnFeatureFile(File featureFile) throws IOException * If featureFile is not a normal file, the index will silently not be written. * * @param featurePath + * @throws IOException if featureFile is not a normal file. */ public void writeBasedOnFeaturePath(Path featurePath) throws IOException; diff --git a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java index 43b1a2b40..d7cc31cef 100644 --- a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java +++ b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java @@ -66,8 +66,6 @@ MAGIC_NUMBER = bb.order(ByteOrder.LITTLE_ENDIAN).getInt(); } - private static final Log LOGGER = Log.getInstance(TabixIndex.class); - private final TabixFormat formatSpec; private final List sequenceNames; private final BinningIndexContent[] indices; @@ -226,12 +224,12 @@ public void write(final Path tabixPath) throws IOException { * Writes to a path with appropriate name and directory based on feature path. * * @param featurePath Path being indexed. + * @throws IOException if featureFile is not a normal file. */ @Override public void writeBasedOnFeaturePath(final Path featurePath) throws IOException { if (!Files.isRegularFile(featurePath)) { - LOGGER.warn("Index not written into ", featurePath); - return; + throw new IOException("Cannot write based on a non-regular file: " + featurePath.toUri()); } write(Tribble.tabixIndexPath(featurePath)); } diff --git a/src/test/java/htsjdk/tribble/index/IndexTest.java b/src/test/java/htsjdk/tribble/index/IndexTest.java index 06fb311a5..d1ff18eb7 100644 --- a/src/test/java/htsjdk/tribble/index/IndexTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexTest.java @@ -3,6 +3,7 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import htsjdk.HtsjdkTest; +import htsjdk.samtools.util.IOUtil; import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.TestUtils; import htsjdk.tribble.Tribble; @@ -121,4 +122,13 @@ public void testWritePathIndex(final File inputFile, final IndexFactory.IndexTyp } } } + + @Test(dataProvider = "writeIndexData") + public void testWriteBasedOnNonRegularFeatureFile(final File inputFile, final IndexFactory.IndexType type, final FeatureCodec codec) throws Exception { + final File tmpFolder = IOUtil.createTempDir("NonRegultarFeatureFile", null); + // create the index + final Index index = IndexFactory.createIndex(inputFile, codec, type); + // try to write based on the tmpFolder + Assert.assertThrows(IOException.class, () -> index.writeBasedOnFeatureFile(tmpFolder)); + } } From 9bef3fc745337e54dee1656279c98a215af95671 Mon Sep 17 00:00:00 2001 From: skashin Date: Tue, 30 May 2017 13:53:22 -0400 Subject: [PATCH 111/137] =?UTF-8?q?The=20existing=20code=20was=20throwing?= =?UTF-8?q?=20a=20NPE=20when=20using=20a=20CRAMFileReader=20for=20?= =?UTF-8?q?=E2=80=A6=20(#879)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * The existing code was throwing a NPE when using a CRAMFileReader for InputResource.Type.PATH * Converted multiple if statements to a switch statement --- .../java/htsjdk/samtools/SamReaderFactory.java | 191 +++++++++++---------- 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index 3d6a80fa2..73544a1cc 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -303,106 +303,111 @@ public SamReader open(final SamInputResource resource) { return reader; } } - if (type == InputResource.Type.SEEKABLE_STREAM || type == InputResource.Type.URL) { - if (SamStreams.sourceLikeBam(data.asUnbufferedSeekableStream())) { - final SeekableStream bufferedIndexStream; - if (indexDefined && indexMaybe.asUnbufferedSeekableStream() != null) { - bufferedIndexStream = IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()); - } else { - // TODO: Throw an exception here? An index _may_ have been provided, but we're ignoring it - bufferedIndexStream = null; - } + switch (type) { + case PATH: case SEEKABLE_STREAM: case URL: + if (SamStreams.sourceLikeBam(data.asUnbufferedSeekableStream())) { + final SeekableStream bufferedIndexStream; + if (indexDefined && indexMaybe.asUnbufferedSeekableStream() != null) { + bufferedIndexStream = IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()); + } else { + // TODO: Throw an exception here? An index _may_ have been provided, but we're ignoring it + bufferedIndexStream = null; + } - primitiveSamReader = new BAMFileReader( - IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), - bufferedIndexStream, - false, - asynchronousIO, - validationStringency, - this.samRecordFactory, - this.inflaterFactory - ); - } else if (SamStreams.sourceLikeCram(data.asUnbufferedSeekableStream())) { - if (referenceSource == null) { - referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); - } - SeekableStream bufferedIndexStream = indexDefined ? - IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()) : - null; - primitiveSamReader = new CRAMFileReader( - IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), - bufferedIndexStream, referenceSource, validationStringency); - } else { - // assume its a SAM file/no index - LOG.warn("Unable to detect file format from input URL or stream, assuming SAM format."); - primitiveSamReader = new SAMTextReader( - IOUtil.toBufferedStream(data.asUnbufferedInputStream()), - validationStringency, this.samRecordFactory); - } - } else if (type == InputResource.Type.SRA_ACCESSION) { - primitiveSamReader = new SRAFileReader(data.asSRAAccession()); - } else { - InputStream bufferedStream = - IOUtil.maybeBufferInputStream( - data.asUnbufferedInputStream(), - Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE) + primitiveSamReader = new BAMFileReader( + IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), + bufferedIndexStream, + false, + asynchronousIO, + validationStringency, + this.samRecordFactory, + this.inflaterFactory ); - File sourceFile = data.asFile(); - // calling asFile is safe even if indexMaybe is a Google Cloud Storage bucket - // (in that case we just get null) - final File indexFile = indexMaybe == null ? null : indexMaybe.asFile(); - if (SamStreams.isBAMFile(bufferedStream)) { - if (sourceFile == null || !sourceFile.isFile()) { - // check whether we can seek - final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); - // do not close bufferedStream, it's the same stream we're getting here. - SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); - if (null == sourceSeekable || null == indexSeekable) { - // not seekable. - // it's OK that we consumed a bit of the stream already, this ctor expects it. - primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, - validationStringency, this.samRecordFactory, this.inflaterFactory); - } else { - // seekable. - // need to return to the beginning because it's the same stream we used earlier - // and read a bit from, and that form of the ctor expects the stream to start at 0. - sourceSeekable.seek(0); - primitiveSamReader = new BAMFileReader( - sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, - this.samRecordFactory, this.inflaterFactory); + } else if (SamStreams.sourceLikeCram(data.asUnbufferedSeekableStream())) { + if (referenceSource == null) { + referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); } + SeekableStream bufferedIndexStream = indexDefined ? + IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()) : + null; + primitiveSamReader = new CRAMFileReader( + IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), + bufferedIndexStream, referenceSource, validationStringency); } else { - bufferedStream.close(); - primitiveSamReader = new BAMFileReader( - sourceFile, indexFile, false, asynchronousIO, - validationStringency, this.samRecordFactory, this.inflaterFactory); - } - } else if (BlockCompressedInputStream.isValidFile(bufferedStream)) { - primitiveSamReader = new SAMTextReader(new BlockCompressedInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isGzippedSAMFile(bufferedStream)) { - primitiveSamReader = new SAMTextReader(new GZIPInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isCRAMFile(bufferedStream)) { - if (referenceSource == null) { - referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); + // assume its a SAM file/no index + LOG.warn("Unable to detect file format from input URL or stream, assuming SAM format."); + primitiveSamReader = new SAMTextReader( + IOUtil.toBufferedStream(data.asUnbufferedInputStream()), + validationStringency, this.samRecordFactory); } - if (sourceFile == null || !sourceFile.isFile()) { - primitiveSamReader = new CRAMFileReader(bufferedStream, indexFile, referenceSource, validationStringency); + break; + case SRA_ACCESSION: + primitiveSamReader = new SRAFileReader(data.asSRAAccession()); + break; + case FILE: case INPUT_STREAM: + InputStream bufferedStream = + IOUtil.maybeBufferInputStream( + data.asUnbufferedInputStream(), + Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE) + ); + File sourceFile = data.asFile(); + // calling asFile is safe even if indexMaybe is a Google Cloud Storage bucket + // (in that case we just get null) + final File indexFile = indexMaybe == null ? null : indexMaybe.asFile(); + if (SamStreams.isBAMFile(bufferedStream)) { + if (sourceFile == null || !sourceFile.isFile()) { + // check whether we can seek + final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); + // do not close bufferedStream, it's the same stream we're getting here. + SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); + if (null == sourceSeekable || null == indexSeekable) { + // not seekable. + // it's OK that we consumed a bit of the stream already, this ctor expects it. + primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, + validationStringency, this.samRecordFactory, this.inflaterFactory); + } else { + // seekable. + // need to return to the beginning because it's the same stream we used earlier + // and read a bit from, and that form of the ctor expects the stream to start at 0. + sourceSeekable.seek(0); + primitiveSamReader = new BAMFileReader( + sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, + this.samRecordFactory, this.inflaterFactory); + } + } else { + bufferedStream.close(); + primitiveSamReader = new BAMFileReader( + sourceFile, indexFile, false, asynchronousIO, + validationStringency, this.samRecordFactory, this.inflaterFactory); + } + } else if (BlockCompressedInputStream.isValidFile(bufferedStream)) { + primitiveSamReader = new SAMTextReader(new BlockCompressedInputStream(bufferedStream), validationStringency, this.samRecordFactory); + } else if (SamStreams.isGzippedSAMFile(bufferedStream)) { + primitiveSamReader = new SAMTextReader(new GZIPInputStream(bufferedStream), validationStringency, this.samRecordFactory); + } else if (SamStreams.isCRAMFile(bufferedStream)) { + if (referenceSource == null) { + referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); + } + if (sourceFile == null || !sourceFile.isFile()) { + primitiveSamReader = new CRAMFileReader(bufferedStream, indexFile, referenceSource, validationStringency); + } else { + bufferedStream.close(); + primitiveSamReader = new CRAMFileReader(sourceFile, indexFile, referenceSource, validationStringency); + } + } else if (sourceFile != null && isSra(sourceFile)) { + if (bufferedStream != null) { + bufferedStream.close(); + } + primitiveSamReader = new SRAFileReader(new SRAAccession(sourceFile.getPath())); } else { - bufferedStream.close(); - primitiveSamReader = new CRAMFileReader(sourceFile, indexFile, referenceSource, validationStringency); - } - } else if (sourceFile != null && isSra(sourceFile)) { - if (bufferedStream != null) { - bufferedStream.close(); - } - primitiveSamReader = new SRAFileReader(new SRAAccession(sourceFile.getPath())); - } else { - if (indexDefined) { - bufferedStream.close(); - throw new RuntimeException("Cannot use index file with textual SAM file"); + if (indexDefined) { + bufferedStream.close(); + throw new RuntimeException("Cannot use index file with textual SAM file"); + } + primitiveSamReader = new SAMTextReader(bufferedStream, sourceFile, validationStringency, this.samRecordFactory); } - primitiveSamReader = new SAMTextReader(bufferedStream, sourceFile, validationStringency, this.samRecordFactory); - } + break; + default: throw new SAMException("Opening SamReader for " + type + " is not supported"); } // Apply the options defined by this factory to this reader From 630aa48a45ac581fbc7182d6a109196b54e99313 Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Wed, 31 May 2017 10:08:02 -0400 Subject: [PATCH 112/137] =?UTF-8?q?Revert=20"The=20existing=20code=20was?= =?UTF-8?q?=20throwing=20a=20NPE=20when=20using=20a=20CRAMFileReader=20for?= =?UTF-8?q?=20=E2=80=A6=20(#879)"=20(#885)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9bef3fc745337e54dee1656279c98a215af95671. --- .../java/htsjdk/samtools/SamReaderFactory.java | 191 ++++++++++----------- 1 file changed, 93 insertions(+), 98 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SamReaderFactory.java b/src/main/java/htsjdk/samtools/SamReaderFactory.java index 73544a1cc..3d6a80fa2 100644 --- a/src/main/java/htsjdk/samtools/SamReaderFactory.java +++ b/src/main/java/htsjdk/samtools/SamReaderFactory.java @@ -303,111 +303,106 @@ public SamReader open(final SamInputResource resource) { return reader; } } - switch (type) { - case PATH: case SEEKABLE_STREAM: case URL: - if (SamStreams.sourceLikeBam(data.asUnbufferedSeekableStream())) { - final SeekableStream bufferedIndexStream; - if (indexDefined && indexMaybe.asUnbufferedSeekableStream() != null) { - bufferedIndexStream = IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()); - } else { - // TODO: Throw an exception here? An index _may_ have been provided, but we're ignoring it - bufferedIndexStream = null; - } - - primitiveSamReader = new BAMFileReader( - IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), - bufferedIndexStream, - false, - asynchronousIO, - validationStringency, - this.samRecordFactory, - this.inflaterFactory - ); - } else if (SamStreams.sourceLikeCram(data.asUnbufferedSeekableStream())) { - if (referenceSource == null) { - referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); - } - SeekableStream bufferedIndexStream = indexDefined ? - IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()) : - null; - primitiveSamReader = new CRAMFileReader( - IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), - bufferedIndexStream, referenceSource, validationStringency); + if (type == InputResource.Type.SEEKABLE_STREAM || type == InputResource.Type.URL) { + if (SamStreams.sourceLikeBam(data.asUnbufferedSeekableStream())) { + final SeekableStream bufferedIndexStream; + if (indexDefined && indexMaybe.asUnbufferedSeekableStream() != null) { + bufferedIndexStream = IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()); } else { - // assume its a SAM file/no index - LOG.warn("Unable to detect file format from input URL or stream, assuming SAM format."); - primitiveSamReader = new SAMTextReader( - IOUtil.toBufferedStream(data.asUnbufferedInputStream()), - validationStringency, this.samRecordFactory); + // TODO: Throw an exception here? An index _may_ have been provided, but we're ignoring it + bufferedIndexStream = null; } - break; - case SRA_ACCESSION: - primitiveSamReader = new SRAFileReader(data.asSRAAccession()); - break; - case FILE: case INPUT_STREAM: - InputStream bufferedStream = - IOUtil.maybeBufferInputStream( - data.asUnbufferedInputStream(), - Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE) - ); - File sourceFile = data.asFile(); - // calling asFile is safe even if indexMaybe is a Google Cloud Storage bucket - // (in that case we just get null) - final File indexFile = indexMaybe == null ? null : indexMaybe.asFile(); - if (SamStreams.isBAMFile(bufferedStream)) { - if (sourceFile == null || !sourceFile.isFile()) { - // check whether we can seek - final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); - // do not close bufferedStream, it's the same stream we're getting here. - SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); - if (null == sourceSeekable || null == indexSeekable) { - // not seekable. - // it's OK that we consumed a bit of the stream already, this ctor expects it. - primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, - validationStringency, this.samRecordFactory, this.inflaterFactory); - } else { - // seekable. - // need to return to the beginning because it's the same stream we used earlier - // and read a bit from, and that form of the ctor expects the stream to start at 0. - sourceSeekable.seek(0); - primitiveSamReader = new BAMFileReader( - sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, - this.samRecordFactory, this.inflaterFactory); - } - } else { - bufferedStream.close(); - primitiveSamReader = new BAMFileReader( - sourceFile, indexFile, false, asynchronousIO, + + primitiveSamReader = new BAMFileReader( + IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), + bufferedIndexStream, + false, + asynchronousIO, + validationStringency, + this.samRecordFactory, + this.inflaterFactory + ); + } else if (SamStreams.sourceLikeCram(data.asUnbufferedSeekableStream())) { + if (referenceSource == null) { + referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); + } + SeekableStream bufferedIndexStream = indexDefined ? + IOUtil.maybeBufferedSeekableStream(indexMaybe.asUnbufferedSeekableStream()) : + null; + primitiveSamReader = new CRAMFileReader( + IOUtil.maybeBufferedSeekableStream(data.asUnbufferedSeekableStream()), + bufferedIndexStream, referenceSource, validationStringency); + } else { + // assume its a SAM file/no index + LOG.warn("Unable to detect file format from input URL or stream, assuming SAM format."); + primitiveSamReader = new SAMTextReader( + IOUtil.toBufferedStream(data.asUnbufferedInputStream()), + validationStringency, this.samRecordFactory); + } + } else if (type == InputResource.Type.SRA_ACCESSION) { + primitiveSamReader = new SRAFileReader(data.asSRAAccession()); + } else { + InputStream bufferedStream = + IOUtil.maybeBufferInputStream( + data.asUnbufferedInputStream(), + Math.max(Defaults.BUFFER_SIZE, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE) + ); + File sourceFile = data.asFile(); + // calling asFile is safe even if indexMaybe is a Google Cloud Storage bucket + // (in that case we just get null) + final File indexFile = indexMaybe == null ? null : indexMaybe.asFile(); + if (SamStreams.isBAMFile(bufferedStream)) { + if (sourceFile == null || !sourceFile.isFile()) { + // check whether we can seek + final SeekableStream indexSeekable = indexMaybe == null ? null : indexMaybe.asUnbufferedSeekableStream(); + // do not close bufferedStream, it's the same stream we're getting here. + SeekableStream sourceSeekable = data.asUnbufferedSeekableStream(); + if (null == sourceSeekable || null == indexSeekable) { + // not seekable. + // it's OK that we consumed a bit of the stream already, this ctor expects it. + primitiveSamReader = new BAMFileReader(bufferedStream, indexFile, false, asynchronousIO, validationStringency, this.samRecordFactory, this.inflaterFactory); - } - } else if (BlockCompressedInputStream.isValidFile(bufferedStream)) { - primitiveSamReader = new SAMTextReader(new BlockCompressedInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isGzippedSAMFile(bufferedStream)) { - primitiveSamReader = new SAMTextReader(new GZIPInputStream(bufferedStream), validationStringency, this.samRecordFactory); - } else if (SamStreams.isCRAMFile(bufferedStream)) { - if (referenceSource == null) { - referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); - } - if (sourceFile == null || !sourceFile.isFile()) { - primitiveSamReader = new CRAMFileReader(bufferedStream, indexFile, referenceSource, validationStringency); } else { - bufferedStream.close(); - primitiveSamReader = new CRAMFileReader(sourceFile, indexFile, referenceSource, validationStringency); - } - } else if (sourceFile != null && isSra(sourceFile)) { - if (bufferedStream != null) { - bufferedStream.close(); + // seekable. + // need to return to the beginning because it's the same stream we used earlier + // and read a bit from, and that form of the ctor expects the stream to start at 0. + sourceSeekable.seek(0); + primitiveSamReader = new BAMFileReader( + sourceSeekable, indexSeekable, false, asynchronousIO, validationStringency, + this.samRecordFactory, this.inflaterFactory); } - primitiveSamReader = new SRAFileReader(new SRAAccession(sourceFile.getPath())); } else { - if (indexDefined) { - bufferedStream.close(); - throw new RuntimeException("Cannot use index file with textual SAM file"); - } - primitiveSamReader = new SAMTextReader(bufferedStream, sourceFile, validationStringency, this.samRecordFactory); + bufferedStream.close(); + primitiveSamReader = new BAMFileReader( + sourceFile, indexFile, false, asynchronousIO, + validationStringency, this.samRecordFactory, this.inflaterFactory); + } + } else if (BlockCompressedInputStream.isValidFile(bufferedStream)) { + primitiveSamReader = new SAMTextReader(new BlockCompressedInputStream(bufferedStream), validationStringency, this.samRecordFactory); + } else if (SamStreams.isGzippedSAMFile(bufferedStream)) { + primitiveSamReader = new SAMTextReader(new GZIPInputStream(bufferedStream), validationStringency, this.samRecordFactory); + } else if (SamStreams.isCRAMFile(bufferedStream)) { + if (referenceSource == null) { + referenceSource = ReferenceSource.getDefaultCRAMReferenceSource(); + } + if (sourceFile == null || !sourceFile.isFile()) { + primitiveSamReader = new CRAMFileReader(bufferedStream, indexFile, referenceSource, validationStringency); + } else { + bufferedStream.close(); + primitiveSamReader = new CRAMFileReader(sourceFile, indexFile, referenceSource, validationStringency); + } + } else if (sourceFile != null && isSra(sourceFile)) { + if (bufferedStream != null) { + bufferedStream.close(); + } + primitiveSamReader = new SRAFileReader(new SRAAccession(sourceFile.getPath())); + } else { + if (indexDefined) { + bufferedStream.close(); + throw new RuntimeException("Cannot use index file with textual SAM file"); } - break; - default: throw new SAMException("Opening SamReader for " + type + " is not supported"); + primitiveSamReader = new SAMTextReader(bufferedStream, sourceFile, validationStringency, this.samRecordFactory); + } } // Apply the options defined by this factory to this reader From dcd20ff5946b569a798632150e028feb9011950b Mon Sep 17 00:00:00 2001 From: Nils Homer Date: Wed, 7 Jun 2017 11:37:13 -0700 Subject: [PATCH 113/137] Adding an overlaps function to IntervalList. (#877) * Adding an overlaps function to IntervalList. Finds all intervals in the first list that overlap any interval in the second list. --- .../java/htsjdk/samtools/util/IntervalList.java | 58 ++++++++++++++ .../htsjdk/samtools/util/IntervalListTest.java | 92 ++++++++++++++++++++-- 2 files changed, 145 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/IntervalList.java b/src/main/java/htsjdk/samtools/util/IntervalList.java index 929671a84..26403c512 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalList.java +++ b/src/main/java/htsjdk/samtools/util/IntervalList.java @@ -739,6 +739,64 @@ public static IntervalList difference(final Collection lists1, fin subtract(lists2, lists1)); } + /** + * A utility function for finding the intervals in the first list that have at least 1bp overlap with any interval + * in the second list. + * + * @param lhs the first collection of IntervalLists + * @param lhs the second collection of IntervalLists + * @return an IntervalList comprising of all intervals in the first IntervalList that have at least 1bp overlap with + * any interval in the second. + */ + public static IntervalList overlaps(final IntervalList lhs, final IntervalList rhs) { + return overlaps(Collections.singletonList(lhs), Collections.singletonList(rhs)); + } + + /** + * A utility function for finding the intervals in the first list that have at least 1bp overlap with any interval + * in the second list. + * + * @param lists1 the first collection of IntervalLists + * @param lists2 the second collection of IntervalLists + * @return an IntervalList comprising of all intervals in the first collection of lists that have at least 1bp + * overlap with any interval in the second lists. + */ + public static IntervalList overlaps(final Collection lists1, final Collection lists2) { + if(lists1.isEmpty()){ + throw new SAMException("Cannot call overlaps with the first collection having empty list of IntervalLists."); + } + + final SAMFileHeader header = lists1.iterator().next().getHeader().clone(); + header.setSortOrder(SAMFileHeader.SortOrder.unsorted); + + // Create an overlap detector on list2 + final IntervalList overlapIntervals = new IntervalList(header); + for (final IntervalList list : lists2) { + SequenceUtil.assertSequenceDictionariesEqual(header.getSequenceDictionary(), + list.getHeader().getSequenceDictionary()); + overlapIntervals.addall(list.getIntervals()); + } + final OverlapDetector detector = new OverlapDetector<>(0, 0); + final int dummy = -1; // NB: since we don't actually use the returned objects, we can use a dummy value + for (final Interval interval : overlapIntervals.sorted().uniqued()) { + detector.addLhs(dummy, interval); + } + + // Go through each input interval in in lists1 and see if overlaps any interval in lists2 + final IntervalList merged = new IntervalList(header); + for (final IntervalList list : lists1) { + SequenceUtil.assertSequenceDictionariesEqual(header.getSequenceDictionary(), + list.getHeader().getSequenceDictionary()); + for (final Interval interval : list.getIntervals()) { + if (detector.overlapsAny(interval)) { + merged.add(interval); + } + } + } + + return merged; + } + @Override public boolean equals(final Object o) { if (this == o) return true; diff --git a/src/test/java/htsjdk/samtools/util/IntervalListTest.java b/src/test/java/htsjdk/samtools/util/IntervalListTest.java index 983820bbe..e138ee0e1 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalListTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalListTest.java @@ -25,10 +25,7 @@ package htsjdk.samtools.util; import htsjdk.HtsjdkTest; -import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMSequenceDictionary; -import htsjdk.samtools.SAMSequenceRecord; -import htsjdk.samtools.SamFileHeaderMerger; +import htsjdk.samtools.*; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; import org.testng.annotations.BeforeTest; @@ -377,12 +374,97 @@ public void testSubtractSingletonIntervalLists(final IntervalList fromLists, fin } @Test(dataProvider = "subtractSingletonData") - public void testSubtractSingletonasListIntervalList(final IntervalList fromLists, final IntervalList whatLists, final IntervalList list) { + public void testSubtractSingletonAsListIntervalList(final IntervalList fromLists, final IntervalList whatLists, final IntervalList list) { Assert.assertEquals( CollectionUtil.makeCollection(IntervalList.subtract(Collections.singletonList(fromLists), Collections.singletonList(whatLists)).iterator()), CollectionUtil.makeCollection(list.iterator())); } + @DataProvider(name = "overlapsSingletonData") + public Object[][] overlapSingletonData() { + final IntervalList two_overlaps_one = new IntervalList(fileHeader); + final IntervalList three_overlaps_two = new IntervalList(fileHeader); + final IntervalList three_overlaps_one = new IntervalList(fileHeader); + final IntervalList one_overlaps_three = new IntervalList(fileHeader); + + // NB: commented lines below are there to show the intervals in the first list that will not be in the resulting list + + two_overlaps_one.add(new Interval("1", 50, 150)); + //two_overlaps_one.add(new Interval("1", 301, 500)); + two_overlaps_one.add(new Interval("2", 1, 150)); + two_overlaps_one.add(new Interval("2", 250, 270)); + two_overlaps_one.add(new Interval("2", 290, 400)); + + three_overlaps_two.add(new Interval("1", 25, 400)); + three_overlaps_two.add(new Interval("2", 200, 600)); + //three_overlaps_two.add(new Interval("3", 50, 470)); + + three_overlaps_one.add(new Interval("1", 25, 400)); + three_overlaps_one.add(new Interval("2", 200, 600)); + //three_overlaps_one.add(new Interval("3", 50, 470)); + + one_overlaps_three.add(new Interval("1", 1, 100)); + one_overlaps_three.add(new Interval("1", 101, 200)); + one_overlaps_three.add(new Interval("1", 202, 300)); + one_overlaps_three.add(new Interval("2", 200, 300)); + //one_overlaps_three.add(new Interval("2", 100, 150)); + + return new Object[][]{ + new Object[]{list1, list1, list1}, // should return itself + new Object[]{list1, IntervalList.invert(list1), new IntervalList(list1.getHeader())}, // should be empty + new Object[]{list2, list1, two_overlaps_one}, + new Object[]{list3, list2, three_overlaps_two}, + new Object[]{list3, list1, three_overlaps_one}, + new Object[]{list1, list3, one_overlaps_three} + }; + } + + @DataProvider(name = "overlapsData") + public Object[][] overlapData() { + final IntervalList three_overlaps_one_and_two = new IntervalList(fileHeader); + + three_overlaps_one_and_two.add(new Interval("1", 25, 400)); + three_overlaps_one_and_two.add(new Interval("2", 200, 600)); + //three_overlaps_one_and_two.add(new Interval("3", 50, 470)); + + return new Object[][]{ + new Object[]{CollectionUtil.makeList(list3), CollectionUtil.makeList(list1, list2), three_overlaps_one_and_two}, + }; + } + + @Test(dataProvider = "overlapsData") + public void testOverlapsIntervalLists(final List fromLists, final List whatLists, final IntervalList list) { + Assert.assertEquals( + CollectionUtil.makeCollection(IntervalList.overlaps(fromLists, whatLists).iterator()), + CollectionUtil.makeCollection(list.iterator())); + } + + @Test(dataProvider = "overlapsSingletonData") + public void testOverlapsSingletonIntervalLists(final IntervalList fromLists, final IntervalList whatLists, final IntervalList list) { + Assert.assertEquals( + CollectionUtil.makeCollection(IntervalList.overlaps(fromLists, whatLists).iterator()), + CollectionUtil.makeCollection(list.iterator())); + } + + @Test(dataProvider = "overlapsSingletonData") + public void testOverlapsSingletonAsListIntervalList(final IntervalList fromLists, final IntervalList whatLists, final IntervalList list) { + Assert.assertEquals( + CollectionUtil.makeCollection(IntervalList.overlaps(Collections.singletonList(fromLists), Collections.singletonList(whatLists)).iterator()), + CollectionUtil.makeCollection(list.iterator())); + } + + @Test(expectedExceptions = SAMException.class) + public void testOverlapsEmptyFirstList() { + IntervalList.overlaps(Collections.emptyList(), Collections.singletonList(list1)); + } + + @Test + public void testOverlapsEmptySecondList() { + Assert.assertEquals( + CollectionUtil.makeCollection(IntervalList.overlaps(Collections.singletonList(list1), Collections.emptyList()).iterator()), + Collections.emptyList()); + } + @DataProvider(name = "VCFCompData") public Object[][] VCFCompData() { return new Object[][]{ From 98c2438dbd86869e5ac2638ecd5a61021ced9c7e Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Thu, 8 Jun 2017 10:49:10 -0400 Subject: [PATCH 114/137] overloading BlockCompressedInputStream.checkTerminator to support NIO (#890) * adding overloads of BlockCompressedInputStream.checkTerminator to support java.nio.Path and SeekableByteChannel * adding additional tests to BlockCompressedTerminatorTest --- .../samtools/util/BlockCompressedInputStream.java | 102 ++++++++++++++++----- .../util/BlockCompressedTerminatorTest.java | 87 +++++++++++++++--- 2 files changed, 155 insertions(+), 34 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java index 066a0c001..e108d1bb3 100755 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java @@ -32,16 +32,15 @@ import htsjdk.samtools.seekablestream.SeekableStream; import htsjdk.samtools.util.zip.InflaterFactory; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; +import java.io.*; import java.net.URL; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.Arrays; -import java.util.zip.Inflater; /** * Utility class for reading BGZF block compressed files. The caller can treat this file like any other InputStream. @@ -587,41 +586,98 @@ private int unpackInt32(final byte[] buffer, final int offset) { public enum FileTermination {HAS_TERMINATOR_BLOCK, HAS_HEALTHY_LAST_BLOCK, DEFECTIVE} + /** + * + * @param file the file to check + * @return status of the last compressed block + * @throws IOException + */ public static FileTermination checkTermination(final File file) throws IOException { - final long fileSize = file.length(); + return checkTermination(file == null ? null : file.toPath()); + } + + /** + * + * @param path to the file to check + * @return status of the last compressed block + * @throws IOException + */ + public static FileTermination checkTermination(final Path path) throws IOException { + try( final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ) ){ + return checkTermination(channel); + } + } + + /** + * check the status of the final bzgipped block for the given bgzipped resource + * + * @param channel an open channel to read from, + * the channel will remain open and the initial position will be restored when the operation completes + * this makes no guarantee about the state of the channel if an exception is thrown during reading + * + * @return the status of the last compressed black + * @throws IOException + */ + public static FileTermination checkTermination(SeekableByteChannel channel) throws IOException { + final long fileSize = channel.size(); if (fileSize < BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length) { return FileTermination.DEFECTIVE; } - final RandomAccessFile raFile = new RandomAccessFile(file, "r"); + final long initialPosition = channel.position(); + boolean exceptionThrown = false; try { - raFile.seek(fileSize - BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length); - byte[] buf = new byte[BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length]; - raFile.readFully(buf); - if (Arrays.equals(buf, BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK)) { + channel.position(fileSize - BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length); + + //Check if the end of the file is an empty gzip block which is used as the terminator for a bgzipped file + final ByteBuffer lastBlockBuffer = ByteBuffer.allocate(BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length); + readFully(channel, lastBlockBuffer); + if (Arrays.equals(lastBlockBuffer.array(), BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK)) { return FileTermination.HAS_TERMINATOR_BLOCK; } - final int bufsize = (int)Math.min(fileSize, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE); - buf = new byte[bufsize]; - raFile.seek(fileSize - bufsize); - raFile.read(buf); - for (int i = buf.length - BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length; - i >= 0; --i) { + + //if the last block isn't an empty gzip block, check to see if it is a healthy compressed block or if it's corrupted + final int bufsize = (int) Math.min(fileSize, BlockCompressedStreamConstants.MAX_COMPRESSED_BLOCK_SIZE); + final byte[] bufferArray = new byte[bufsize]; + channel.position(fileSize - bufsize); + readFully(channel, ByteBuffer.wrap(bufferArray)); + for (int i = bufferArray.length - BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK.length; + i >= 0; --i) { if (!preambleEqual(BlockCompressedStreamConstants.GZIP_BLOCK_PREAMBLE, - buf, i, BlockCompressedStreamConstants.GZIP_BLOCK_PREAMBLE.length)) { + bufferArray, i, BlockCompressedStreamConstants.GZIP_BLOCK_PREAMBLE.length)) { continue; } - final ByteBuffer byteBuffer = ByteBuffer.wrap(buf, i + BlockCompressedStreamConstants.GZIP_BLOCK_PREAMBLE.length, 4); + final ByteBuffer byteBuffer = ByteBuffer.wrap(bufferArray, + i + BlockCompressedStreamConstants.GZIP_BLOCK_PREAMBLE.length, + 4); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); - final int totalBlockSizeMinusOne = byteBuffer.getShort() & 0xFFFF; - if (buf.length - i == totalBlockSizeMinusOne + 1) { + final int totalBlockSizeMinusOne = byteBuffer.getShort() & 0xFFFF; + if (bufferArray.length - i == totalBlockSizeMinusOne + 1) { return FileTermination.HAS_HEALTHY_LAST_BLOCK; } else { return FileTermination.DEFECTIVE; } } return FileTermination.DEFECTIVE; + } catch (final Throwable e) { + exceptionThrown = true; + throw e; } finally { - raFile.close(); + //if an exception was thrown we don't want to reset the position because that would be likely to throw again + //and suppress the initial exception + if(!exceptionThrown) { + channel.position(initialPosition); + } + } + } + + /** + * read as many bytes as dst's capacity into dst or throw if that's not possible + * @throws EOFException if channel has fewer bytes available than dst's capacity + */ + static void readFully(SeekableByteChannel channel, ByteBuffer dst) throws IOException { + final int bytesRead = channel.read(dst); + if (bytesRead < dst.capacity()){ + throw new EOFException(); } } diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java index d9d20ccef..4a14bd920 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedTerminatorTest.java @@ -23,38 +23,103 @@ */ package htsjdk.samtools.util; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import htsjdk.HtsjdkTest; +import htsjdk.samtools.SeekableByteChannelFromBuffer; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.EOFException; import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; /** * @author alecw@broadinstitute.org */ public class BlockCompressedTerminatorTest extends HtsjdkTest { private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/samtools/util"); + private static final File DEFECTIVE = new File(TEST_DATA_DIR, "defective_bgzf.bam"); + private static final File NO_TERMINATOR = new File(TEST_DATA_DIR, "no_bgzf_terminator.bam"); - @Test - public void testFileWithTerminator() throws Exception { + @DataProvider + public Object[][] getFiles() throws IOException { + return new Object[][]{ + {getValidCompressedFile(), BlockCompressedInputStream.FileTermination.HAS_TERMINATOR_BLOCK}, + {NO_TERMINATOR, BlockCompressedInputStream.FileTermination.HAS_HEALTHY_LAST_BLOCK}, + {DEFECTIVE, BlockCompressedInputStream.FileTermination.DEFECTIVE} + }; + } + + @Test( dataProvider = "getFiles") + public void testCheckTerminationForFiles(File compressedFile, BlockCompressedInputStream.FileTermination expected) throws IOException { + Assert.assertEquals(BlockCompressedInputStream.checkTermination(compressedFile), expected); + } + + @Test( dataProvider = "getFiles") + public void testCheckTerminationForPaths(File compressedFile, BlockCompressedInputStream.FileTermination expected) throws IOException { + try(FileSystem fs = Jimfs.newFileSystem("test", Configuration.unix())){ + final Path compressedFileInJimfs = Files.copy(compressedFile.toPath(), fs.getPath("something")); + Assert.assertEquals(BlockCompressedInputStream.checkTermination(compressedFileInJimfs), expected); + } + } + + @Test( dataProvider = "getFiles") + public void testCheckTerminationForSeekableByteChannels(File compressedFile, BlockCompressedInputStream.FileTermination expected) throws IOException { + try(SeekableByteChannel channel = Files.newByteChannel(compressedFile.toPath())){ + Assert.assertEquals(BlockCompressedInputStream.checkTermination(channel), expected); + } + } + + @Test(dataProvider = "getFiles") + public void testChannelPositionIsRestored(File compressedFile, BlockCompressedInputStream.FileTermination expected) throws IOException { + final long position = 50; + try(SeekableByteChannel channel = Files.newByteChannel(compressedFile.toPath())){ + channel.position(position); + Assert.assertEquals(channel.position(), position); + Assert.assertEquals(BlockCompressedInputStream.checkTermination(channel), expected); + Assert.assertEquals(channel.position(), position); + } + } + + private static File getValidCompressedFile() throws IOException { final File tmpCompressedFile = File.createTempFile("test.", ".bgzf"); tmpCompressedFile.deleteOnExit(); final BlockCompressedOutputStream os = new BlockCompressedOutputStream(tmpCompressedFile); os.write("Hi, Mom!\n".getBytes()); os.close(); - Assert.assertEquals(BlockCompressedInputStream.checkTermination(tmpCompressedFile), - BlockCompressedInputStream.FileTermination.HAS_TERMINATOR_BLOCK); + return tmpCompressedFile; } @Test - public void testValidFileWithoutTerminator() throws Exception { - Assert.assertEquals(BlockCompressedInputStream.checkTermination(new File(TEST_DATA_DIR, "no_bgzf_terminator.bam")), - BlockCompressedInputStream.FileTermination.HAS_HEALTHY_LAST_BLOCK); + public void testReadFullyReadsBytesCorrectly() throws IOException { + try(final SeekableByteChannel channel = Files.newByteChannel(DEFECTIVE.toPath())){ + final ByteBuffer readBuffer = ByteBuffer.allocate(10); + Assert.assertTrue(channel.size() > readBuffer.capacity()); + BlockCompressedInputStream.readFully(channel, readBuffer); + + ByteBuffer expected = ByteBuffer.allocate(10); + channel.position(0).read(expected); + Assert.assertEquals(readBuffer.array(), expected.array()); + } } - @Test - public void testDefectiveFile() throws Exception { - Assert.assertEquals(BlockCompressedInputStream.checkTermination(new File(TEST_DATA_DIR, "defective_bgzf.bam")), - BlockCompressedInputStream.FileTermination.DEFECTIVE); + @Test(expectedExceptions = EOFException.class) + public void testReadFullyThrowWhenItCantReadEnough() throws IOException { + try(final SeekableByteChannel channel = Files.newByteChannel(DEFECTIVE.toPath())){ + final ByteBuffer readBuffer = ByteBuffer.allocate(1000); + Assert.assertTrue(channel.size() < readBuffer.capacity()); + BlockCompressedInputStream.readFully(channel, readBuffer); + } } + + + } From 9e0199a0036f05e0c0f8bdb6e959c757d69a462b Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Thu, 8 Jun 2017 19:12:28 -0400 Subject: [PATCH 115/137] Fix SAMRecord.getReadLength() so it does not throw an exception if null bases --- src/main/java/htsjdk/samtools/SAMRecord.java | 3 ++- .../java/htsjdk/samtools/SAMRecordUnitTest.java | 24 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 8e61d150d..9ba0b9966 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -262,7 +262,8 @@ public void setReadBases(final byte[] value) { * @return number of bases in the read. */ public int getReadLength() { - return getReadBases().length; + final byte[] readBases = getReadBases(); + return readBases == null ? 0 : readBases.length; } /** diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index 29118197f..acb9a636d 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -776,7 +776,7 @@ public void testNullHeaderRecordValidation() { } @Test - private void testNullHeaderDeepCopy() { + public void testNullHeaderDeepCopy() { SAMRecord sam = createTestRecordHelper(); sam.setHeader(null); final SAMRecord deepCopy = sam.deepCopy(); @@ -805,13 +805,13 @@ private void testNullHeaderCigar(SAMRecord rec) { } @Test - private void testNullHeadGetCigarSAM() { - SAMRecord sam = createTestRecordHelper(); + public void testNullHeadGetCigarSAM() { + final SAMRecord sam = createTestRecordHelper(); testNullHeaderCigar(sam); } @Test - private void testNullHeadGetCigarBAM() { + public void testNullHeadGetCigarBAM() { SAMRecord sam = createTestRecordHelper(); SAMRecordFactory factory = new DefaultSAMRecordFactory(); BAMRecord bamRec = factory.createBAMRecord( @@ -1039,4 +1039,20 @@ public SAMRecord createTestSamRec() { return(rec); } + + @DataProvider + public Object [][] readBasesGetReadLengthData() { + return new Object[][]{ + { null, 0 }, + { SAMRecord.NULL_SEQUENCE, 0 }, + { new byte[] {'A', 'C'}, 2 } + }; + } + + @Test(dataProvider = "readBasesGetReadLengthData") + public void testNullReadBasesGetReadLength(final byte[] readBases, final int readLength) { + final SAMRecord sam = createTestRecordHelper(); + sam.setReadBases(readBases); + Assert.assertEquals(sam.getReadLength(), readLength); + } } From 7e79bbac3be60eeec86ea705498098e023ee6999 Mon Sep 17 00:00:00 2001 From: Pierre Lindenbaum Date: Fri, 9 Jun 2017 05:00:25 +0200 Subject: [PATCH 116/137] use Locatable interface (#887) --- src/main/java/htsjdk/samtools/util/IntervalTreeMap.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java b/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java index 259308732..ebec2f484 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java +++ b/src/main/java/htsjdk/samtools/util/IntervalTreeMap.java @@ -165,16 +165,16 @@ public int size() { } /** * Test overlapping interval - * @param key the interval + * @param key the Locatable * @return true if it contains an object overlapping the interval */ - public boolean containsOverlapping(final Interval key) { + public boolean containsOverlapping(final Locatable key) { final IntervalTree tree = mSequenceMap.get(key.getContig()); return tree!=null && tree.overlappers(key.getStart(), key.getEnd()).hasNext(); } - public Collection getOverlapping(final Interval key) { + public Collection getOverlapping(final Locatable key) { final List result = new ArrayList(); final IntervalTree tree = mSequenceMap.get(key.getContig()); if (tree != null) { @@ -187,10 +187,10 @@ public boolean containsOverlapping(final Interval key) { } /** * Test if this contains an object that is contained by 'key' - * @param key the interval + * @param key the Locatable * @return true if it contains an object is contained by 'key' */ - public boolean containsContained(final Interval key) { + public boolean containsContained(final Locatable key) { final IntervalTree tree = mSequenceMap.get(key.getContig()); if(tree==null) return false; final Iterator> iterator = tree.overlappers(key.getStart(), key.getEnd()); @@ -204,7 +204,7 @@ public boolean containsContained(final Interval key) { } - public Collection getContained(final Interval key) { + public Collection getContained(final Locatable key) { final List result = new ArrayList(); final IntervalTree tree = mSequenceMap.get(key.getContig()); if (tree != null) { From 112758bd4a7546dd85e70d5ec4ad55a58374b53d Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Fri, 9 Jun 2017 09:01:35 -0400 Subject: [PATCH 117/137] Added test using setReadString() --- src/main/java/htsjdk/samtools/SAMRecord.java | 4 +++- src/main/java/htsjdk/samtools/util/StringUtil.java | 6 ++++++ .../java/htsjdk/samtools/SAMRecordUnitTest.java | 22 +++++++++++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index 9ba0b9966..f93b2d72c 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -238,7 +238,9 @@ public void setReadString(final String value) { mReadBases = NULL_SEQUENCE; } else { final byte[] bases = StringUtil.stringToBytes(value); - SAMUtils.normalizeBases(bases); + if (bases != null) { + SAMUtils.normalizeBases(bases); + } setReadBases(bases); } } diff --git a/src/main/java/htsjdk/samtools/util/StringUtil.java b/src/main/java/htsjdk/samtools/util/StringUtil.java index 90492533e..a885ba2db 100644 --- a/src/main/java/htsjdk/samtools/util/StringUtil.java +++ b/src/main/java/htsjdk/samtools/util/StringUtil.java @@ -312,6 +312,9 @@ public static String bytesToString(final byte[] buffer, final int offset, final } return byteBuffer; */ + if (s == null) { + return null; + } final byte[] byteBuffer = new byte[s.length()]; s.getBytes(0, byteBuffer.length, byteBuffer, 0); return byteBuffer; @@ -319,6 +322,9 @@ public static String bytesToString(final byte[] buffer, final int offset, final @SuppressWarnings("deprecation") public static byte[] stringToBytes(final String s, final int offset, final int length) { + if (s == null) { + return null; + } final byte[] byteBuffer = new byte[length]; s.getBytes(offset, offset + length, byteBuffer, 0); return byteBuffer; diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index acb9a636d..1bfe26345 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -1041,7 +1041,7 @@ public SAMRecord createTestSamRec() { } @DataProvider - public Object [][] readBasesGetReadLengthData() { + public Object [][] readBasesArrayGetReadLengthData() { return new Object[][]{ { null, 0 }, { SAMRecord.NULL_SEQUENCE, 0 }, @@ -1049,10 +1049,26 @@ public SAMRecord createTestSamRec() { }; } - @Test(dataProvider = "readBasesGetReadLengthData") - public void testNullReadBasesGetReadLength(final byte[] readBases, final int readLength) { + @Test(dataProvider = "readBasesArrayGetReadLengthData") + public void testReadBasesGetReadLength(final byte[] readBases, final int readLength) { final SAMRecord sam = createTestRecordHelper(); sam.setReadBases(readBases); Assert.assertEquals(sam.getReadLength(), readLength); } + + @DataProvider + public Object [][] readBasesStringGetReadLengthData() { + return new Object[][]{ + { null, 0 }, + { SAMRecord.NULL_SEQUENCE_STRING, 0 }, + { "AC", 2 } + }; + } + + @Test(dataProvider = "readBasesStringGetReadLengthData") + public void testReadStringGetReadLength(final String readBases, final int readLength) { + final SAMRecord sam = createTestRecordHelper(); + sam.setReadString(readBases); + Assert.assertEquals(sam.getReadLength(), readLength); + } } From 9b84fe816039aba122c421191b13824f099cefdb Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Fri, 9 Jun 2017 11:00:11 -0400 Subject: [PATCH 118/137] Cleaned up SAMUtils (mostly cosmetic) (#888) * Fixing-up SamUtils: - adding more information to some exceptions - fixing java-doc - java8 <> - if/else alignment & braces - removed use of deprecated SAMRecordUtils class - @Deprecated all methods and members of SAMRecordUtils - fixed javadoc @deprecated across the board. --- .../java/htsjdk/samtools/DuplicateSetIterator.java | 2 +- src/main/java/htsjdk/samtools/SAMRecordUtil.java | 27 +- src/main/java/htsjdk/samtools/SAMUtils.java | 302 ++++----- .../htsjdk/samtools/filter/FilteringIterator.java | 2 +- src/main/java/htsjdk/tribble/util/HTTPHelper.java | 3 + .../variantcontext/GenotypeLikelihoods.java | 6 + .../variantcontext/filter/FilteringIterator.java | 2 +- src/main/java/htsjdk/variant/vcf/VCFEncoder.java | 715 +++++++++++---------- 8 files changed, 547 insertions(+), 512 deletions(-) diff --git a/src/main/java/htsjdk/samtools/DuplicateSetIterator.java b/src/main/java/htsjdk/samtools/DuplicateSetIterator.java index f3b9b072a..6e833035b 100644 --- a/src/main/java/htsjdk/samtools/DuplicateSetIterator.java +++ b/src/main/java/htsjdk/samtools/DuplicateSetIterator.java @@ -114,7 +114,7 @@ public DuplicateSetIterator(final CloseableIterator iterator, } @Deprecated - /** Do not use this method as the first duplicate set will not be compared with this scoring strategy. + /** @deprecated Do not use this method as the first duplicate set will not be compared with this scoring strategy. * Instead, provide a comparator to the constructor that has the scoring strategy set. */ public void setScoringStrategy(final DuplicateScoringStrategy.ScoringStrategy scoringStrategy) { this.comparator.setScoringStrategy(scoringStrategy); diff --git a/src/main/java/htsjdk/samtools/SAMRecordUtil.java b/src/main/java/htsjdk/samtools/SAMRecordUtil.java index d778789d7..9435934c5 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordUtil.java +++ b/src/main/java/htsjdk/samtools/SAMRecordUtil.java @@ -23,23 +23,28 @@ */ package htsjdk.samtools; -import htsjdk.samtools.util.SequenceUtil; -import htsjdk.samtools.util.StringUtil; - import java.util.Arrays; import java.util.Collection; import java.util.List; /** * - * Use {@link SAMRecord#reverseComplement()} instead, which defaults to making a copy of attributes for reverse - * complement rather than changing them in-place. - * * @author alecw@broadinstitute.org + * + * @deprecated 10/27/2016 Use {@link SAMRecord} constants and functions */ @Deprecated public class SAMRecordUtil { + /** + * @deprecated 6/5/2017 Use {@link SAMRecord#TAGS_TO_REVERSE_COMPLEMENT} + */ + @Deprecated public static List TAGS_TO_REVERSE_COMPLEMENT = Arrays.asList(SAMTag.E2.name(), SAMTag.SQ.name()); + + /** + * @deprecated 6/5/2017 Use {@link SAMRecord#TAGS_TO_REVERSE} + */ + @Deprecated public static List TAGS_TO_REVERSE = Arrays.asList(SAMTag.OQ.name(), SAMTag.U2.name()); /** @@ -48,7 +53,11 @@ * or attributes. If a copy is needed use {@link #reverseComplement(SAMRecord, boolean)}. * See {@link #TAGS_TO_REVERSE_COMPLEMENT} {@link #TAGS_TO_REVERSE} * for the default set of tags that are handled. + * + * @deprecated 6/5/2017 Use {@link SAMRecord#reverseComplement} but note that the default behavior there is different + * It will default to making a copy, not reverse-complementing in-place! */ + @Deprecated public static void reverseComplement(final SAMRecord rec) { rec.reverseComplement(TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, true); } @@ -61,7 +70,10 @@ public static void reverseComplement(final SAMRecord rec) { * * @param rec Record to reverse complement. * @param inplace Setting this to false will clone all attributes, bases and qualities before changing the values. + * + * @deprecated 6/5/2017 Use {@link SAMRecord#reverseComplement} */ + @Deprecated public static void reverseComplement(final SAMRecord rec, boolean inplace) { rec.reverseComplement(TAGS_TO_REVERSE_COMPLEMENT, TAGS_TO_REVERSE, inplace); } @@ -70,7 +82,10 @@ public static void reverseComplement(final SAMRecord rec, boolean inplace) { * Reverse complement bases and reverse quality scores. In addition reverse complement any * non-null attributes specified by tagsToRevcomp and reverse and non-null attributes * specified by tagsToReverse. + * + * @deprecated 6/5/2017 Use {@link SAMRecord#reverseComplement} */ + @Deprecated public static void reverseComplement(final SAMRecord rec, final Collection tagsToRevcomp, final Collection tagsToReverse, boolean inplace) { rec.reverseComplement(tagsToRevcomp, tagsToReverse, inplace); } diff --git a/src/main/java/htsjdk/samtools/SAMUtils.java b/src/main/java/htsjdk/samtools/SAMUtils.java index d439a4a83..5b81de979 100644 --- a/src/main/java/htsjdk/samtools/SAMUtils.java +++ b/src/main/java/htsjdk/samtools/SAMUtils.java @@ -43,14 +43,17 @@ import java.util.TreeMap; import java.util.regex.Pattern; - /** * Utilty methods. */ public final class SAMUtils { - /** regex for semicolon, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} */ + /** + * regex for semicolon, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} + */ private static final Pattern SEMICOLON_PAT = Pattern.compile("[;]"); - /** regex for comma, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} */ + /** + * regex for comma, used in {@link SAMUtils#getOtherCanonicalAlignments(SAMRecord)} + */ private static final Pattern COMMA_PAT = Pattern.compile("[,]"); // Representation of bases, one for when in low-order nybble, one for when in high-order nybble. @@ -87,27 +90,26 @@ private static final byte COMPRESSED_K_HIGH = (byte) (COMPRESSED_K_LOW << 4); private static final byte COMPRESSED_D_HIGH = (byte) (COMPRESSED_D_LOW << 4); private static final byte COMPRESSED_B_HIGH = (byte) (COMPRESSED_B_LOW << 4); - - private static final byte [] COMPRESSED_LOOKUP_TABLE = - new byte[]{ - '=', - 'A', - 'C', - 'M', - 'G', - 'R', - 'S', - 'V', - 'T', - 'W', - 'Y', - 'H', - 'K', - 'D', - 'B', - 'N' - }; - + + private static final byte[] COMPRESSED_LOOKUP_TABLE = { + '=', + 'A', + 'C', + 'M', + 'G', + 'R', + 'S', + 'V', + 'T', + 'W', + 'Y', + 'H', + 'K', + 'D', + 'B', + 'N' + }; + public static final int MAX_PHRED_SCORE = 93; /** @@ -135,8 +137,8 @@ * Convert from a byte array with bases stored in nybbles, with for example,=, A, C, G, T, N represented as 0, 1, 2, 4, 8, 15, * to a a byte array containing =AaCcGgTtNn represented as ASCII. * - * @param length Number of bases (not bytes) to convert. - * @param compressedBases Bases represented as nybbles, in BAM binary format. + * @param length Number of bases (not bytes) to convert. + * @param compressedBases Bases represented as nybbles, in BAM binary format. * @param compressedOffset Byte offset in compressedBases to start. * @return New byte array with bases as ASCII bytes. */ @@ -215,7 +217,7 @@ private static byte charToCompressedBaseLow(final byte base) { case 'b': return COMPRESSED_B_LOW; default: - throw new IllegalArgumentException("Bad base passed to charToCompressedBaseLow: " + Character.toString((char)base) + "(" + base + ")"); + throw new IllegalArgumentException("Bad base passed to charToCompressedBaseLow: " + Character.toString((char) base) + "(" + base + ")"); } } @@ -279,21 +281,22 @@ private static byte charToCompressedBaseHigh(final byte base) { case 'b': return COMPRESSED_B_HIGH; default: - throw new IllegalArgumentException("Bad base passed to charToCompressedBaseHigh: " + Character.toString((char)base) + "(" + base + ")"); + throw new IllegalArgumentException("Bad base passed to charToCompressedBaseHigh: " + Character.toString((char) base) + "(" + base + ")"); } } - + /** * Returns the byte corresponding to a certain nybble + * * @param base One of COMPRESSED_*_LOW, a low-order nybble encoded base. * @return ASCII base, one of =ACGTNMRSVWYHKDB. * @throws IllegalArgumentException if the base is not one of =ACGTNMRSVWYHKDB. */ - private static byte compressedBaseToByte(byte base){ - try{ + private static byte compressedBaseToByte(byte base) { + try { return COMPRESSED_LOOKUP_TABLE[base]; - }catch(IndexOutOfBoundsException e){ - throw new IllegalArgumentException("Bad base passed to charToCompressedBase: " + Character.toString((char)base) + "(" + base + ")"); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException("Bad base passed to charToCompressedBase: " + Character.toString((char) base) + "(" + base + ")"); } } @@ -304,7 +307,7 @@ private static byte compressedBaseToByte(byte base){ * @return ASCII base, one of ACGTN=. */ private static byte compressedBaseToByteLow(final int base) { - return compressedBaseToByte((byte)(base & 0xf)); + return compressedBaseToByte((byte) (base & 0xf)); } /** @@ -314,13 +317,13 @@ private static byte compressedBaseToByteLow(final int base) { * @return ASCII base, one of ACGTN=. */ private static byte compressedBaseToByteHigh(final int base) { - return compressedBaseToByte((byte)((base >> 4) & 0xf)); + return compressedBaseToByte((byte) ((base >> 4) & 0xf)); } /** * Convert bases in place into canonical form, upper case, and with no-call represented as N. * - * @param bases + * @param bases byte array of bases to "normalize", in place. */ static void normalizeBases(final byte[] bases) { for (int i = 0; i < bases.length; ++i) { @@ -434,11 +437,11 @@ static int reg2bin(final int beg, final int end) { /** * Handle a list of validation errors according to the validation stringency. * - * @param validationErrors List of errors to report, or null if there are no errors. - * @param samRecordIndex Record number of the SAMRecord corresponding to the validation errors, or -1 if - * the record number is not known. + * @param validationErrors List of errors to report, or null if there are no errors. + * @param samRecordIndex Record number of the SAMRecord corresponding to the validation errors, or -1 if + * the record number is not known. * @param validationStringency If STRICT, throw a SAMFormatException. If LENIENT, print the validation - * errors to stderr. If SILENT, do nothing. + * errors to stderr. If SILENT, do nothing. */ public static void processValidationErrors(final List validationErrors, final long samRecordIndex, @@ -464,11 +467,10 @@ public static void processValidationError(final SAMValidationError validationErr } else if (validationStringency == ValidationStringency.LENIENT) { System.err.println("Ignoring SAM validation error: " + validationError); } - } private static final SAMHeaderRecordComparator HEADER_RECORD_COMPARATOR = - new SAMHeaderRecordComparator( + new SAMHeaderRecordComparator<>( SAMReadGroupRecord.PLATFORM_UNIT_TAG, SAMReadGroupRecord.LIBRARY_TAG, SAMReadGroupRecord.DATE_RUN_PRODUCED_TAG, @@ -476,7 +478,8 @@ public static void processValidationError(final SAMValidationError validationErr SAMReadGroupRecord.SEQUENCING_CENTER_TAG, SAMReadGroupRecord.PLATFORM_TAG, SAMReadGroupRecord.DESCRIPTION_TAG, - SAMReadGroupRecord.READ_GROUP_ID_TAG // We don't actually want to compare with ID but it's suitable + SAMReadGroupRecord.READ_GROUP_ID_TAG + // We don't actually want to compare with ID but it's suitable // "just in case" since it's the only one that's actually required ); @@ -497,11 +500,11 @@ public static String calculateReadGroupRecordChecksum(final File input, final Fi // Sort the read group records by their first final SamReader reader = SamReaderFactory.makeDefault().referenceSequence(referenceFasta).open(input); - final List sortedRecords = new ArrayList(reader.getFileHeader().getReadGroups()); + final List sortedRecords = new ArrayList<>(reader.getFileHeader().getReadGroups()); Collections.sort(sortedRecords, HEADER_RECORD_COMPARATOR); for (final SAMReadGroupRecord rgRecord : sortedRecords) { - final TreeMap sortedAttributes = new TreeMap(); + final TreeMap sortedAttributes = new TreeMap<>(); for (final Map.Entry attributeEntry : rgRecord.getAttributes()) { sortedAttributes.put(attributeEntry.getKey(), attributeEntry.getValue()); } @@ -539,7 +542,7 @@ public static void chainSAMProgramRecord(final SAMFileHeader header, final SAMPr final List pgs = header.getProgramRecords(); if (!pgs.isEmpty()) { - final List referencedIds = new ArrayList(); + final List referencedIds = new ArrayList<>(); for (final SAMProgramRecord pg : pgs) { if (pg.getPreviousProgramGroupId() != null) { referencedIds.add(pg.getPreviousProgramGroupId()); @@ -560,7 +563,7 @@ public static void chainSAMProgramRecord(final SAMFileHeader header, final SAMPr /** * Strip mapping information from a SAMRecord. - * + *

* WARNING: by clearing the secondary and supplementary flags, * this may have the affect of producing multiple distinct records with the * same read name and flags, which may lead to invalid SAM/BAM output. @@ -568,7 +571,7 @@ public static void chainSAMProgramRecord(final SAMFileHeader header, final SAMPr */ public static void makeReadUnmapped(final SAMRecord rec) { if (rec.getReadNegativeStrandFlag()) { - SAMRecordUtil.reverseComplement(rec); + rec.reverseComplement(true); rec.setReadNegativeStrandFlag(false); } rec.setDuplicateReadFlag(false); @@ -622,13 +625,13 @@ public static boolean cigarMapsNoBasesToRef(final Cigar cigar) { /** * Tests if the provided record is mapped entirely beyond the end of the reference (i.e., the alignment start is greater than the * length of the sequence to which the record is mapped). + * * @param record must not have a null SamFileHeader */ public static boolean recordMapsEntirelyBeyondEndOfReference(final SAMRecord record) { if (record.getHeader() == null) { throw new SAMException("A non-null SAMHeader is required to resolve the mapping position: " + record.getReadName()); - } - else { + } else { return record.getHeader().getSequence(record.getReferenceIndex()).getSequenceLength() < record.getAlignmentStart(); } } @@ -646,7 +649,6 @@ public static int compareMapqs(final int mapq1, final int mapq2) { else return mapq1 - mapq2; } - /** * Hokey algorithm for combining two MAPQs into values that are comparable, being cognizant of the fact * that in MAPQ world, 1 > 255 > 0. In this algorithm, 255 is treated as if it were 0.01, so that @@ -655,11 +657,17 @@ public static int compareMapqs(final int mapq1, final int mapq2) { * invocations of this method. */ public static int combineMapqs(int m1, int m2) { - if (m1 == 255) m1 = 1; - else m1 *= 100; + if (m1 == 255) { + m1 = 1; + } else { + m1 *= 100; + } - if (m2 == 255) m2 = 1; - else m2 *= 100; + if (m2 == 255) { + m2 = 1; + } else { + m2 *= 100; + } return m1 + m2; @@ -682,15 +690,15 @@ public static long findVirtualOffsetOfFirstRecordInBam(final File bamFile) { * reference sequence. Note that clipped portions, and inserted and deleted bases (vs. the reference) * are not represented in the alignment blocks. * - * @param cigar The cigar containing the alignment information + * @param cigar The cigar containing the alignment information * @param alignmentStart The start (1-based) of the alignment - * @param cigarTypeName The type of cigar passed - for error logging. + * @param cigarTypeName The type of cigar passed - for error logging. * @return List of alignment blocks */ public static List getAlignmentBlocks(final Cigar cigar, final int alignmentStart, final String cigarTypeName) { if (cigar == null) return Collections.emptyList(); - final List alignmentBlocks = new ArrayList(); + final List alignmentBlocks = new ArrayList<>(); int readBase = 1; int refBase = alignmentStart; @@ -721,7 +729,7 @@ public static long findVirtualOffsetOfFirstRecordInBam(final File bamFile) { refBase += length; break; default: - throw new IllegalStateException("Case statement didn't deal with " + cigarTypeName + " op: " + e.getOperator()); + throw new IllegalStateException("Case statement didn't deal with " + cigarTypeName + " op: " + e.getOperator() + "in CIGAR: " + cigar); } } return Collections.unmodifiableList(alignmentBlocks); @@ -729,7 +737,7 @@ public static long findVirtualOffsetOfFirstRecordInBam(final File bamFile) { /** * @param alignmentStart The start (1-based) of the alignment - * @param cigar The cigar containing the alignment information + * @param cigar The cigar containing the alignment information * @return the alignment start (1-based, inclusive) adjusted for clipped bases. For example if the read * has an alignment start of 100 but the first 4 bases were clipped (hard or soft clipped) * then this method will return 96. @@ -753,7 +761,7 @@ public static int getUnclippedStart(final int alignmentStart, final Cigar cigar) /** * @param alignmentEnd The end (1-based) of the alignment - * @param cigar The cigar containing the alignment information + * @param cigar The cigar containing the alignment information * @return the alignment end (1-based, inclusive) adjusted for clipped bases. For example if the read * has an alignment end of 100 but the last 7 bases were clipped (hard or soft clipped) * then this method will return 107. @@ -791,7 +799,7 @@ public static String getMateCigarString(final SAMRecord rec) { /** * Returns the Mate Cigar or null if there is none. * - * @param rec the SAM record + * @param rec the SAM record * @param withValidation true if we are to validate the mate cigar before returning, false otherwise. * @return Cigar object for the read's mate, or null if there is none. */ @@ -835,11 +843,11 @@ public static int getMateCigarLength(final SAMRecord rec) { */ public static int getMateAlignmentEnd(final SAMRecord rec) { if (rec.getMateUnmappedFlag()) { - throw new RuntimeException("getMateAlignmentEnd called on an unmapped mate."); + throw new RuntimeException("getMateAlignmentEnd called on an unmapped mate: " + rec); } final Cigar mateCigar = SAMUtils.getMateCigar(rec); if (mateCigar == null) { - throw new SAMException("Mate CIGAR (Tag MC) not found."); + throw new SAMException("Mate CIGAR (Tag MC) not found:" + rec); } return CoordMath.getEnd(rec.getMateAlignmentStart(), mateCigar.getReferenceLength()); } @@ -854,15 +862,14 @@ public static int getMateAlignmentEnd(final SAMRecord rec) { */ public static int getMateUnclippedStart(final SAMRecord rec) { if (rec.getMateUnmappedFlag()) - throw new RuntimeException("getMateUnclippedStart called on an unmapped mate."); + throw new RuntimeException("getMateUnclippedStart called on an unmapped mate: " + rec); final Cigar mateCigar = getMateCigar(rec); if (mateCigar == null) { - throw new SAMException("Mate CIGAR (Tag MC) not found."); + throw new SAMException("Mate CIGAR (Tag MC) not found: " + rec); } return SAMUtils.getUnclippedStart(rec.getMateAlignmentStart(), mateCigar); } - /** * @param rec the SAM record * @return the mate alignment end (1-based, inclusive) adjusted for clipped bases. For example if the mate @@ -873,20 +880,20 @@ public static int getMateUnclippedStart(final SAMRecord rec) { */ public static int getMateUnclippedEnd(final SAMRecord rec) { if (rec.getMateUnmappedFlag()) { - throw new RuntimeException("getMateUnclippedEnd called on an unmapped mate."); + throw new RuntimeException("getMateUnclippedEnd called on an unmapped mate: " + rec); } final Cigar mateCigar = SAMUtils.getMateCigar(rec); if (mateCigar == null) { - throw new SAMException("Mate CIGAR (Tag MC) not found."); + throw new SAMException("Mate CIGAR (Tag MC) not found: " + rec); } return SAMUtils.getUnclippedEnd(getMateAlignmentEnd(rec), mateCigar); } /** * @param rec the SAM record - * Returns blocks of the mate sequence that have been aligned directly to the - * reference sequence. Note that clipped portions of the mate and inserted and - * deleted bases (vs. the reference) are not represented in the alignment blocks. + * Returns blocks of the mate sequence that have been aligned directly to the + * reference sequence. Note that clipped portions of the mate and inserted and + * deleted bases (vs. the reference) are not represented in the alignment blocks. */ public static List getMateAlignmentBlocks(final SAMRecord rec) { return getAlignmentBlocks(getMateCigar(rec), rec.getMateAlignmentStart(), "mate cigar"); @@ -896,12 +903,12 @@ public static int getMateUnclippedEnd(final SAMRecord rec) { * Run all validations of the mate's CIGAR. These include validation that the CIGAR makes sense independent of * placement, plus validation that CIGAR + placement yields all bases with M operator within the range of the reference. * - * @param rec the SAM record - * @param cigar The cigar containing the alignment information - * @param referenceIndex The reference index + * @param rec the SAM record + * @param cigar The cigar containing the alignment information + * @param referenceIndex The reference index * @param alignmentBlocks The alignment blocks (parsed from the cigar) - * @param recordNumber For error reporting. -1 if not known. - * @param cigarTypeName For error reporting. "Read CIGAR" or "Mate Cigar" + * @param recordNumber For error reporting. -1 if not known. + * @param cigarTypeName For error reporting. "Read CIGAR" or "Mate Cigar" * @return List of errors, or null if no errors. */ @@ -916,16 +923,15 @@ public static int getMateUnclippedEnd(final SAMRecord rec) { if (referenceIndex != SAMRecord.NO_ALIGNMENT_REFERENCE_INDEX) { SAMFileHeader samHeader = rec.getHeader(); if (null == samHeader) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.MISSING_HEADER, cigarTypeName + " A non-null SAMHeader is required to validate cigar elements for: ", rec.getReadName(), recordNumber)); - } - else { + } else { final SAMSequenceRecord sequence = samHeader.getSequence(referenceIndex); final int referenceSequenceLength = sequence.getSequenceLength(); for (final AlignmentBlock alignmentBlock : alignmentBlocks) { if (alignmentBlock.getReferenceStart() + alignmentBlock.getLength() - 1 > referenceSequenceLength) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.CIGAR_MAPS_OFF_REFERENCE, cigarTypeName + " M operator maps off end of reference", rec.getReadName(), recordNumber)); break; @@ -940,7 +946,7 @@ public static int getMateUnclippedEnd(final SAMRecord rec) { * Run all validations of the mate's CIGAR. These include validation that the CIGAR makes sense independent of * placement, plus validation that CIGAR + placement yields all bases with M operator within the range of the reference. * - * @param rec the SAM record + * @param rec the SAM record * @param recordNumber For error reporting. -1 if not known. * @return List of errors, or null if no errors. */ @@ -954,7 +960,7 @@ public static int getMateUnclippedEnd(final SAMRecord rec) { } } else { if (getMateCigarString(rec) != null) { - ret = new ArrayList(); + ret = new ArrayList<>(); if (!rec.getReadPairedFlag()) { // If the read is not paired, and the Mate Cigar String (MC Attribute) exists, that is a validation error ret.add(new SAMValidationError(SAMValidationError.Type.MATE_CIGAR_STRING_INVALID_PRESENCE, @@ -984,11 +990,11 @@ public static boolean hasMateCigar(SAMRecord rec) { } /** - * Returns a string that is the the read group ID and read name separated by a colon. This is meant to cannonically + * Returns a string that is the the read group ID and read name separated by a colon. This is meant to canonically * identify a given record within a set of records. * - * @param record - * @return + * @param record SAMRecord for which "canonical" read name is requested + * @return The record's readgroup-id (if non-null) and the read name, separated by a colon, ':' */ public static String getCanonicalRecordName(final SAMRecord record) { String name = record.getStringAttribute(ReservedTagConstants.READ_GROUP_ID); @@ -1002,7 +1008,7 @@ public static String getCanonicalRecordName(final SAMRecord record) { * or the given record's start position is greater than its mate's start position, zero is automatically returned. * NB: This method assumes that the record's mate is not contained within the given record's alignment. * - * @param rec + * @param rec SAMRecord that needs clipping due to overlapping pairs. * @return the number of bases at the end of the read that need to be clipped such that there would be no overlapping bases with its mate. * Read bases include only those from insertion, match, or mismatch Cigar operators. */ @@ -1013,7 +1019,8 @@ public static int getNumOverlappingAlignedBasesToClip(final SAMRecord rec) { // Only clip records that are left-most in genomic order and overlapping. if (rec.getMateAlignmentStart() < rec.getAlignmentStart()) return 0; // right-most, so ignore. - else if (rec.getMateAlignmentStart() == rec.getAlignmentStart() && rec.getFirstOfPairFlag()) return 0; // same start, so pick the first end + else if (rec.getMateAlignmentStart() == rec.getAlignmentStart() && rec.getFirstOfPairFlag()) + return 0; // same start, so pick the first end // Find the number of read bases after the given mate's alignment start. int numBasesToClip = 0; @@ -1026,12 +1033,11 @@ public static int getNumOverlappingAlignedBasesToClip(final SAMRecord rec) { if (refStartPos <= refPos + refBasesLength - 1) { // add to clipped bases if (operator == CigarOperator.MATCH_OR_MISMATCH) { // M if (refStartPos < refPos) numBasesToClip += refBasesLength; // use all of the bases - else numBasesToClip += (refPos + refBasesLength) - refStartPos; // since the mate's alignment start can be in the middle of a cigar element - } - else if (operator == CigarOperator.SOFT_CLIP || operator == CigarOperator.HARD_CLIP || operator == CigarOperator.PADDING || operator == CigarOperator.SKIPPED_REGION) { + else + numBasesToClip += (refPos + refBasesLength) - refStartPos; // since the mate's alignment start can be in the middle of a cigar element + } else if (operator == CigarOperator.SOFT_CLIP || operator == CigarOperator.HARD_CLIP || operator == CigarOperator.PADDING || operator == CigarOperator.SKIPPED_REGION) { // ignore - } - else { // ID + } else { // ID numBasesToClip += operator.consumesReadBases() ? el.getLength() : 0; // clip all the bases in the read from this operator } } @@ -1044,14 +1050,14 @@ else if (operator == CigarOperator.SOFT_CLIP || operator == CigarOperator.HARD_C } /** - * Returns a (possibly new) record that has been clipped if isa mapped paired and has overlapping bases with its mate. + * Returns a (possibly new) record that has been clipped if input is a mapped paired and has overlapping bases with its mate. * See {@link #getNumOverlappingAlignedBasesToClip(SAMRecord)} for how the number of overlapping bases is computed. * NB: this does not properly consider a cigar like: 100M20S10H. * NB: This method assumes that the record's mate is not contained within the given record's alignment. * - * @param record the record from which to clip bases. + * @param record the record from which to clip bases. * @param noSideEffects if true a modified clone of the original record is returned, otherwise we modify the record directly. - * @return + * @return a (possibly new) record that has been clipped */ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, final boolean noSideEffects) { return clipOverlappingAlignedBases(record, getNumOverlappingAlignedBasesToClip(record), noSideEffects); @@ -1063,18 +1069,20 @@ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, fina * NB: this does not properly consider a cigar like: 100M20S10H. * NB: This method assumes that the record's mate is not contained within the given record's alignment. * - * @param record the record from which to clip bases. + * @param record the record from which to clip bases. * @param numOverlappingBasesToClip the number of bases to clip at the end of the read. - * @param noSideEffects if true a modified clone of the original record is returned, otherwise we modify the record directly. - * @return + * @param noSideEffects if true a modified clone of the original record is returned, otherwise we modify the record directly. + * @return Returns a (possibly new) SAMRecord with the given number of bases soft-clipped */ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, final int numOverlappingBasesToClip, final boolean noSideEffects) { // NB: ignores how to handle supplemental records when present for both ends by just using the mate information in the record. - if (numOverlappingBasesToClip <= 0 || record.getReadUnmappedFlag() || record.getMateUnmappedFlag()) return record; + if (numOverlappingBasesToClip <= 0 || record.getReadUnmappedFlag() || record.getMateUnmappedFlag()) { + return record; + } try { - final SAMRecord rec = noSideEffects ? ((SAMRecord)record.clone()) : record; + final SAMRecord rec = noSideEffects ? ((SAMRecord) record.clone()) : record; // watch out for when the second read overlaps all of the first read if (rec.getMateAlignmentStart() <= rec.getAlignmentStart()) { // make it unmapped @@ -1085,7 +1093,7 @@ public static SAMRecord clipOverlappingAlignedBases(final SAMRecord record, fina // 1-based index of first base in read to clip. int clipFrom = rec.getReadLength() - numOverlappingBasesToClip + 1; // we have to check if the last cigar element is soft-clipping, so we can subtract that from clipFrom - final CigarElement cigarElement = rec.getCigar().getCigarElement(rec.getCigarLength()-1); + final CigarElement cigarElement = rec.getCigar().getCigarElement(rec.getCigarLength() - 1); if (CigarOperator.SOFT_CLIP == cigarElement.getOperator()) clipFrom -= cigarElement.getLength(); // FIXME: does not properly consider a cigar like: 100M20S10H @@ -1111,100 +1119,102 @@ public static boolean isValidUnsignedIntegerAttribute(long value) { * Extract a List of 'other canonical alignments' from a SAM record. Those alignments are stored as a string in the 'SA' tag as defined * in the SAM specification. * The name, sequence and qualities, mate data are copied from the original record. + * * @param record must be non null and must have a non-null associated header. * @return a list of 'other canonical alignments' SAMRecords. The list is empty if the 'SA' attribute is missing. */ public static List getOtherCanonicalAlignments(final SAMRecord record) { - if( record == null ) throw new IllegalArgumentException("record is null"); - if( record.getHeader() == null ) throw new IllegalArgumentException("record.getHeader() is null"); + if (record == null) throw new IllegalArgumentException("record is null"); + if (record.getHeader() == null) throw new IllegalArgumentException("record.getHeader() is null"); /* extract value of SA tag */ - final Object saValue = record.getAttribute( SAMTagUtil.getSingleton().SA ); - if( saValue == null ) return Collections.emptyList(); - if( ! (saValue instanceof String) ) throw new SAMException( - "Expected a String for attribute 'SA' but got " + saValue.getClass() ); + final Object saValue = record.getAttribute(SAMTagUtil.getSingleton().SA); + if (saValue == null) return Collections.emptyList(); + if (!(saValue instanceof String)) throw new SAMException( + "Expected a String for attribute 'SA' but got " + saValue.getClass() + ". Record: " + record); final SAMRecordFactory samReaderFactory = new DefaultSAMRecordFactory(); /* the spec says: "Other canonical alignments in a chimeric alignment, formatted as a * semicolon-delimited list: (rname,pos,strand,CIGAR,mapQ,NM;)+. * Each element in the list represents a part of the chimeric alignment. - * Conventionally, at a supplementary line, the 1rst element points to the primary line. + * Conventionally, at a supplementary line, the 1st element points to the primary line. */ /* break string using semicolon */ - final String semiColonStrs[] = SEMICOLON_PAT.split((String)saValue); + final String semiColonStrs[] = SEMICOLON_PAT.split((String) saValue); /* the result list */ - final List alignments = new ArrayList<>( semiColonStrs.length ); + final List alignments = new ArrayList<>(semiColonStrs.length); /* base SAM flag */ - int record_flag = record.getFlags() ; + int record_flag = record.getFlags(); record_flag &= ~SAMFlag.PROPER_PAIR.flag; record_flag &= ~SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; record_flag &= ~SAMFlag.READ_REVERSE_STRAND.flag; - - for(int i=0; i< semiColonStrs.length;++i ) { + for (int i = 0; i < semiColonStrs.length; ++i) { final String semiColonStr = semiColonStrs[i]; /* ignore empty string */ - if( semiColonStr.isEmpty() ) continue; + if (semiColonStr.isEmpty()) continue; /* break string using comma */ final String commaStrs[] = COMMA_PAT.split(semiColonStr); - if( commaStrs.length != 6 ) throw new SAMException("Bad 'SA' attribute in " + semiColonStr); + if (commaStrs.length != 6) + throw new SAMException("Bad 'SA' attribute in " + semiColonStr + ". Record: " + record); /* create the new record */ - final SAMRecord otherRec = samReaderFactory.createSAMRecord( record.getHeader() ); + final SAMRecord otherRec = samReaderFactory.createSAMRecord(record.getHeader()); /* copy fields from the original record */ - otherRec.setReadName( record.getReadName() ); - otherRec.setReadBases( record.getReadBases() ); - otherRec.setBaseQualities( record.getBaseQualities() ); - if( record.getReadPairedFlag() && !record.getMateUnmappedFlag()) { - otherRec.setMateReferenceIndex( record.getMateReferenceIndex() ); - otherRec.setMateAlignmentStart( record.getMateAlignmentStart() ); + otherRec.setReadName(record.getReadName()); + otherRec.setReadBases(record.getReadBases()); + otherRec.setBaseQualities(record.getBaseQualities()); + if (record.getReadPairedFlag() && !record.getMateUnmappedFlag()) { + otherRec.setMateReferenceIndex(record.getMateReferenceIndex()); + otherRec.setMateAlignmentStart(record.getMateAlignmentStart()); } /* get reference sequence */ - final int tid = record.getHeader().getSequenceIndex( commaStrs[0] ); - if( tid == -1 ) throw new SAMException("Unknown contig in " + semiColonStr); - otherRec.setReferenceIndex( tid ); + final int tid = record.getHeader().getSequenceIndex(commaStrs[0]); + if (tid == -1) + throw new SAMException("Unknown contig in " + semiColonStr + ". Record: " + record); + otherRec.setReferenceIndex(tid); /* fill POS */ final int alignStart; try { alignStart = Integer.parseInt(commaStrs[1]); - } catch( final NumberFormatException err ) { - throw new SAMException("bad POS in "+semiColonStr, err); + } catch (final NumberFormatException err) { + throw new SAMException("bad POS in " + semiColonStr + ". Record: " + record, err); } - otherRec.setAlignmentStart( alignStart ); + otherRec.setAlignmentStart(alignStart); /* set TLEN */ - if( record.getReadPairedFlag() && - !record.getMateUnmappedFlag() && - record.getMateReferenceIndex() == tid ) { - otherRec.setInferredInsertSize( record.getMateAlignmentStart() - alignStart ); + if (record.getReadPairedFlag() && + !record.getMateUnmappedFlag() && + record.getMateReferenceIndex() == tid) { + otherRec.setInferredInsertSize(record.getMateAlignmentStart() - alignStart); } /* set FLAG */ - int other_flag = record_flag; - other_flag |= (commaStrs[2].equals("+") ? 0 : SAMFlag.READ_REVERSE_STRAND.flag) ; + int other_flag = record_flag; + other_flag |= (commaStrs[2].equals("+") ? 0 : SAMFlag.READ_REVERSE_STRAND.flag); /* spec: Conventionally, at a supplementary line, the 1st element points to the primary line */ - if( !( record.getSupplementaryAlignmentFlag() && i==0 ) ) { - other_flag |= SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; - } - otherRec.setFlags(other_flag); + if (!(record.getSupplementaryAlignmentFlag() && i == 0)) { + other_flag |= SAMFlag.SUPPLEMENTARY_ALIGNMENT.flag; + } + otherRec.setFlags(other_flag); /* set CIGAR */ - otherRec.setCigar( TextCigarCodec.decode( commaStrs[3] ) ); + otherRec.setCigar(TextCigarCodec.decode(commaStrs[3])); /* set MAPQ */ try { - otherRec.setMappingQuality( Integer.parseInt(commaStrs[4]) ); + otherRec.setMappingQuality(Integer.parseInt(commaStrs[4])); } catch (final NumberFormatException err) { - throw new SAMException("bad MAPQ in "+semiColonStr, err); + throw new SAMException("bad MAPQ in " + semiColonStr + ". Record: " + record, err); } /* fill NM */ @@ -1213,16 +1223,16 @@ public static boolean isValidUnsignedIntegerAttribute(long value) { otherRec.setAttribute(SAMTagUtil.getSingleton().NM, Integer.parseInt(commaStrs[5])); } } catch (final NumberFormatException err) { - throw new SAMException("bad NM in "+semiColonStr, err); + throw new SAMException("bad NM in " + semiColonStr + ". Record: " + record, err); } /* if strand is not the same: reverse-complement */ - if( otherRec.getReadNegativeStrandFlag() != record.getReadNegativeStrandFlag() ) { - SAMRecordUtil.reverseComplement(otherRec); + if (otherRec.getReadNegativeStrandFlag() != record.getReadNegativeStrandFlag()) { + otherRec.reverseComplement(true); } /* add the alignment */ - alignments.add( otherRec ); + alignments.add(otherRec); } return alignments; } diff --git a/src/main/java/htsjdk/samtools/filter/FilteringIterator.java b/src/main/java/htsjdk/samtools/filter/FilteringIterator.java index 3ce9f96ce..4cdaebe89 100644 --- a/src/main/java/htsjdk/samtools/filter/FilteringIterator.java +++ b/src/main/java/htsjdk/samtools/filter/FilteringIterator.java @@ -36,7 +36,7 @@ * * @author Kathleen Tibbetts * - * use {@link FilteringSamIterator} instead + * @deprecated use {@link FilteringSamIterator} instead */ @Deprecated /** use {@link FilteringSamIterator} instead **/ diff --git a/src/main/java/htsjdk/tribble/util/HTTPHelper.java b/src/main/java/htsjdk/tribble/util/HTTPHelper.java index 1e89bc26b..cdd6b277e 100644 --- a/src/main/java/htsjdk/tribble/util/HTTPHelper.java +++ b/src/main/java/htsjdk/tribble/util/HTTPHelper.java @@ -101,6 +101,9 @@ public InputStream openInputStream() throws IOException { * @param end end of range ni bytes * @return * @throws IOException + * + * @deprecated since 12/10/14 Will be removed in a future release, as is somewhat fragile + * and not used. */ @Override @Deprecated diff --git a/src/main/java/htsjdk/variant/variantcontext/GenotypeLikelihoods.java b/src/main/java/htsjdk/variant/variantcontext/GenotypeLikelihoods.java index ee3e08d47..605f2985f 100644 --- a/src/main/java/htsjdk/variant/variantcontext/GenotypeLikelihoods.java +++ b/src/main/java/htsjdk/variant/variantcontext/GenotypeLikelihoods.java @@ -183,6 +183,10 @@ public String getAsString() { * If you know you're biallelic, use getGQLog10FromLikelihoods directly. * @param genotype - actually a genotype type (no call, hom ref, het, hom var) * @return an unsafe quantity that could be negative. In the bi-allelic case, the GQ resulting from best minus next best (if the type is the best). + * + * @deprecated since 2/5/13 use + * {@link GenotypeLikelihoods#getLog10GQ(Genotype, VariantContext)} or + * {@link GenotypeLikelihoods#getLog10GQ(Genotype, List)} */ @Deprecated public double getLog10GQ(GenotypeType genotype){ @@ -554,6 +558,8 @@ public static synchronized void initializeAnyploidPLIndexToAlleleIndices(final i * * @param PLindex the PL index * @return the allele index pair + * + * @deprecated since 2/5/13 */ @Deprecated public static GenotypeLikelihoodsAllelePair getAllelePairUsingDeprecatedOrdering(final int PLindex) { diff --git a/src/main/java/htsjdk/variant/variantcontext/filter/FilteringIterator.java b/src/main/java/htsjdk/variant/variantcontext/filter/FilteringIterator.java index 04609a89b..44362d6c1 100644 --- a/src/main/java/htsjdk/variant/variantcontext/filter/FilteringIterator.java +++ b/src/main/java/htsjdk/variant/variantcontext/filter/FilteringIterator.java @@ -36,7 +36,7 @@ * * @author Yossi Farjoun * - * use {@link FilteringVariantContextIterator} instead + * @deprecated since 2/29/16 use {@link FilteringVariantContextIterator} instead */ @Deprecated diff --git a/src/main/java/htsjdk/variant/vcf/VCFEncoder.java b/src/main/java/htsjdk/variant/vcf/VCFEncoder.java index a90906684..0605b73b9 100644 --- a/src/main/java/htsjdk/variant/vcf/VCFEncoder.java +++ b/src/main/java/htsjdk/variant/vcf/VCFEncoder.java @@ -22,361 +22,362 @@ */ public class VCFEncoder { - /** - * The encoding used for VCF files: ISO-8859-1 - */ - public static final Charset VCF_CHARSET = Charset.forName("ISO-8859-1"); - private static final String QUAL_FORMAT_STRING = "%.2f"; - private static final String QUAL_FORMAT_EXTENSION_TO_TRIM = ".00"; - - private final IntGenotypeFieldAccessors GENOTYPE_FIELD_ACCESSORS = new IntGenotypeFieldAccessors(); - - private VCFHeader header; - - private boolean allowMissingFieldsInHeader = false; - - private boolean outputTrailingFormatFields = false; - - /** - * Prepare a VCFEncoder that will encode records appropriate to the given VCF header, optionally - * allowing missing fields in the header. - */ - public VCFEncoder(final VCFHeader header, final boolean allowMissingFieldsInHeader, final boolean outputTrailingFormatFields) { - if (header == null) throw new NullPointerException("The VCF header must not be null."); - this.header = header; - this.allowMissingFieldsInHeader = allowMissingFieldsInHeader; - this.outputTrailingFormatFields = outputTrailingFormatFields; - } - - /** - * Please see the notes in the default constructor - */ - @Deprecated - public void setVCFHeader(final VCFHeader header) { - this.header = header; - } - - /** - * Please see the notes in the default constructor - */ - @Deprecated - public void setAllowMissingFieldsInHeader(final boolean allow) { - this.allowMissingFieldsInHeader = allow; - } - - public String encode(final VariantContext context) { - if (this.header == null) { - throw new NullPointerException("The header field must be set on the VCFEncoder before encoding records."); - } - - final StringBuilder stringBuilder = new StringBuilder(); - - // CHROM - stringBuilder.append(context.getContig()).append(VCFConstants.FIELD_SEPARATOR) - // POS - .append(String.valueOf(context.getStart())).append(VCFConstants.FIELD_SEPARATOR) - // ID - .append(context.getID()).append(VCFConstants.FIELD_SEPARATOR) - // REF - .append(context.getReference().getDisplayString()).append(VCFConstants.FIELD_SEPARATOR); - - // ALT - if ( context.isVariant() ) { - Allele altAllele = context.getAlternateAllele(0); - String alt = altAllele.getDisplayString(); - stringBuilder.append(alt); - - for (int i = 1; i < context.getAlternateAlleles().size(); i++) { - altAllele = context.getAlternateAllele(i); - alt = altAllele.getDisplayString(); - stringBuilder.append(','); - stringBuilder.append(alt); - } - } else { - stringBuilder.append(VCFConstants.EMPTY_ALTERNATE_ALLELE_FIELD); - } - - stringBuilder.append(VCFConstants.FIELD_SEPARATOR); - - // QUAL - if ( ! context.hasLog10PError()) stringBuilder.append(VCFConstants.MISSING_VALUE_v4); - else stringBuilder.append(formatQualValue(context.getPhredScaledQual())); - stringBuilder.append(VCFConstants.FIELD_SEPARATOR) - // FILTER - .append(getFilterString(context)).append(VCFConstants.FIELD_SEPARATOR); - - // INFO - final Map infoFields = new TreeMap(); - for (final Map.Entry field : context.getAttributes().entrySet() ) { - if ( ! this.header.hasInfoLine(field.getKey())) fieldIsMissingFromHeaderError(context, field.getKey(), "INFO"); - - final String outputValue = formatVCFField(field.getValue()); - if (outputValue != null) infoFields.put(field.getKey(), outputValue); - } - writeInfoString(infoFields, stringBuilder); - - // FORMAT - final GenotypesContext gc = context.getGenotypes(); - if (gc.isLazyWithData() && ((LazyGenotypesContext) gc).getUnparsedGenotypeData() instanceof String) { - stringBuilder.append(VCFConstants.FIELD_SEPARATOR); - stringBuilder.append(((LazyGenotypesContext) gc).getUnparsedGenotypeData().toString()); - } else { - final List genotypeAttributeKeys = context.calcVCFGenotypeKeys(this.header); - if ( ! genotypeAttributeKeys.isEmpty()) { - for (final String format : genotypeAttributeKeys) - if ( ! this.header.hasFormatLine(format)) - fieldIsMissingFromHeaderError(context, format, "FORMAT"); - - final String genotypeFormatString = ParsingUtils.join(VCFConstants.GENOTYPE_FIELD_SEPARATOR, genotypeAttributeKeys); - - stringBuilder.append(VCFConstants.FIELD_SEPARATOR); - stringBuilder.append(genotypeFormatString); - - final Map alleleStrings = buildAlleleStrings(context); - addGenotypeData(context, alleleStrings, genotypeAttributeKeys, stringBuilder); - } - } - - return stringBuilder.toString(); - } - - VCFHeader getVCFHeader() { - return this.header; - } - - boolean getAllowMissingFieldsInHeader() { - return this.allowMissingFieldsInHeader; - } - - private String getFilterString(final VariantContext vc) { - if (vc.isFiltered()) { - for (final String filter : vc.getFilters()) { - if ( ! this.header.hasFilterLine(filter)) fieldIsMissingFromHeaderError(vc, filter, "FILTER"); - } - - return ParsingUtils.join(";", ParsingUtils.sortList(vc.getFilters())); - } - else if (vc.filtersWereApplied()) return VCFConstants.PASSES_FILTERS_v4; - else return VCFConstants.UNFILTERED; - } - - private String formatQualValue(final double qual) { - String s = String.format(QUAL_FORMAT_STRING, qual); - if ( s.endsWith(QUAL_FORMAT_EXTENSION_TO_TRIM) ) - s = s.substring(0, s.length() - QUAL_FORMAT_EXTENSION_TO_TRIM.length()); - return s; - } - - private void fieldIsMissingFromHeaderError(final VariantContext vc, final String id, final String field) { - if ( ! allowMissingFieldsInHeader) - throw new IllegalStateException("Key " + id + " found in VariantContext field " + field - + " at " + vc.getContig() + ":" + vc.getStart() - + " but this key isn't defined in the VCFHeader. We require all VCFs to have" - + " complete VCF headers by default."); - } - - String formatVCFField(final Object val) { - final String result; - if ( val == null ) - result = VCFConstants.MISSING_VALUE_v4; - else if ( val instanceof Double ) - result = formatVCFDouble((Double) val); - else if ( val instanceof Boolean ) - result = (Boolean)val ? "" : null; // empty string for true, null for false - else if ( val instanceof List ) { - result = formatVCFField(((List)val).toArray()); - } else if ( val.getClass().isArray() ) { - final int length = Array.getLength(val); - if ( length == 0 ) - return formatVCFField(null); - final StringBuilder sb = new StringBuilder(formatVCFField(Array.get(val, 0))); - for ( int i = 1; i < length; i++) { - sb.append(','); - sb.append(formatVCFField(Array.get(val, i))); - } - result = sb.toString(); - } else - result = val.toString(); - - return result; - } - - /** - * Takes a double value and pretty prints it to a String for display - * - * Large doubles => gets %.2f style formatting - * Doubles < 1 / 10 but > 1/100 => get %.3f style formatting - * Double < 1/100 => %.3e formatting - * @param d - * @return - */ - public static String formatVCFDouble(final double d) { - final String format; - if ( d < 1 ) { - if ( d < 0.01 ) { - if ( Math.abs(d) >= 1e-20 ) - format = "%.3e"; - else { - // return a zero format - return "0.00"; - } - } else { - format = "%.3f"; - } - } else { - format = "%.2f"; - } - - return String.format(format, d); - } - - static int countOccurrences(final char c, final String s) { - int count = 0; - for (int i = 0; i < s.length(); i++) { - count += s.charAt(i) == c ? 1 : 0; - } - return count; - } - - static boolean isMissingValue(final String s) { - // we need to deal with the case that it's a list of missing values - return (countOccurrences(VCFConstants.MISSING_VALUE_v4.charAt(0), s) + countOccurrences(',', s) == s.length()); - } - - /* - * Add the genotype data - */ - public void addGenotypeData(final VariantContext vc, final Map alleleMap, final List genotypeFormatKeys, final StringBuilder builder) { - final int ploidy = vc.getMaxPloidy(2); - - for (final String sample : this.header.getGenotypeSamples()) { - builder.append(VCFConstants.FIELD_SEPARATOR); - - Genotype g = vc.getGenotype(sample); - if (g == null) g = GenotypeBuilder.createMissing(sample, ploidy); - - final List attrs = new ArrayList(genotypeFormatKeys.size()); - for (final String field : genotypeFormatKeys) { - if (field.equals(VCFConstants.GENOTYPE_KEY)) { - if ( ! g.isAvailable()) { - throw new IllegalStateException("GTs cannot be missing for some samples if they are available for others in the record"); - } - - writeAllele(g.getAllele(0), alleleMap, builder); - for (int i = 1; i < g.getPloidy(); i++) { - builder.append(g.isPhased() ? VCFConstants.PHASED : VCFConstants.UNPHASED); - writeAllele(g.getAllele(i), alleleMap, builder); - } - continue; - - } else { - final String outputValue; - if ( field.equals(VCFConstants.GENOTYPE_FILTER_KEY ) ) { - outputValue = g.isFiltered() ? g.getFilters() : VCFConstants.PASSES_FILTERS_v4; - } else { - final IntGenotypeFieldAccessors.Accessor accessor = GENOTYPE_FIELD_ACCESSORS.getAccessor(field); - if ( accessor != null ) { - final int[] intValues = accessor.getValues(g); - if ( intValues == null ) - outputValue = VCFConstants.MISSING_VALUE_v4; - else if ( intValues.length == 1 ) // fast path - outputValue = Integer.toString(intValues[0]); - else { - final StringBuilder sb = new StringBuilder(); - sb.append(intValues[0]); - for ( int i = 1; i < intValues.length; i++) { - sb.append(','); - sb.append(intValues[i]); - } - outputValue = sb.toString(); - } - } else { - Object val = g.hasExtendedAttribute(field) ? g.getExtendedAttribute(field) : VCFConstants.MISSING_VALUE_v4; - - final VCFFormatHeaderLine metaData = this.header.getFormatHeaderLine(field); - if ( metaData != null ) { - final int numInFormatField = metaData.getCount(vc); - if ( numInFormatField > 1 && val.equals(VCFConstants.MISSING_VALUE_v4) ) { - // If we have a missing field but multiple values are expected, we need to construct a new string with all fields. - // For example, if Number=2, the string has to be ".,." - final StringBuilder sb = new StringBuilder(VCFConstants.MISSING_VALUE_v4); - for ( int i = 1; i < numInFormatField; i++ ) { - sb.append(','); - sb.append(VCFConstants.MISSING_VALUE_v4); - } - val = sb.toString(); - } - } - - // assume that if key is absent, then the given string encoding suffices - outputValue = formatVCFField(val); - } - } - - if ( outputValue != null ) - attrs.add(outputValue); - } - } - - // strip off trailing missing values - if (!outputTrailingFormatFields) { - for (int i = attrs.size() - 1; i >= 0; i--) { - if (isMissingValue(attrs.get(i))) attrs.remove(i); - else break; - } - } - - for (int i = 0; i < attrs.size(); i++) { - if ( i > 0 || genotypeFormatKeys.contains(VCFConstants.GENOTYPE_KEY)) { - builder.append(VCFConstants.GENOTYPE_FIELD_SEPARATOR); - } - builder.append(attrs.get(i)); - } - } - } - - /* - * Create the info string; assumes that no values are null - */ - private void writeInfoString(final Map infoFields, final StringBuilder builder) { - if ( infoFields.isEmpty() ) { - builder.append(VCFConstants.EMPTY_INFO_FIELD); - return; - } - - boolean isFirst = true; - for (final Map.Entry entry : infoFields.entrySet()) { - if (isFirst) isFirst = false; - else builder.append(VCFConstants.INFO_FIELD_SEPARATOR); - - builder.append(entry.getKey()); - - if ( ! entry.getValue().equals("")) { - final VCFInfoHeaderLine metaData = this.header.getInfoHeaderLine(entry.getKey()); - if ( metaData == null || metaData.getCountType() != VCFHeaderLineCount.INTEGER || metaData.getCount() != 0 ) { - builder.append('='); - builder.append(entry.getValue()); - } - } - } - } - - public Map buildAlleleStrings(final VariantContext vc) { - final Map alleleMap = new HashMap(vc.getAlleles().size()+1); - alleleMap.put(Allele.NO_CALL, VCFConstants.EMPTY_ALLELE); // convenience for lookup - - final List alleles = vc.getAlleles(); - for ( int i = 0; i < alleles.size(); i++ ) { - alleleMap.put(alleles.get(i), String.valueOf(i)); - } - - return alleleMap; - } - - private void writeAllele(final Allele allele, final Map alleleMap, final StringBuilder builder) { - final String encoding = alleleMap.get(allele); - if ( encoding == null ) - throw new RuntimeException("Allele " + allele + " is not an allele in the variant context"); - builder.append(encoding); - } + /** + * The encoding used for VCF files: ISO-8859-1 + */ + public static final Charset VCF_CHARSET = Charset.forName("ISO-8859-1"); + private static final String QUAL_FORMAT_STRING = "%.2f"; + private static final String QUAL_FORMAT_EXTENSION_TO_TRIM = ".00"; + + private final IntGenotypeFieldAccessors GENOTYPE_FIELD_ACCESSORS = new IntGenotypeFieldAccessors(); + + private VCFHeader header; + + private boolean allowMissingFieldsInHeader = false; + + private boolean outputTrailingFormatFields = false; + + /** + * Prepare a VCFEncoder that will encode records appropriate to the given VCF header, optionally + * allowing missing fields in the header. + */ + public VCFEncoder(final VCFHeader header, final boolean allowMissingFieldsInHeader, final boolean outputTrailingFormatFields) { + if (header == null) throw new NullPointerException("The VCF header must not be null."); + this.header = header; + this.allowMissingFieldsInHeader = allowMissingFieldsInHeader; + this.outputTrailingFormatFields = outputTrailingFormatFields; + } + + /** + * @deprecated since 10/24/13 use the constructor + */ + @Deprecated + public void setVCFHeader(final VCFHeader header) { + this.header = header; + } + + /** + * @deprecated since 10/24/13 use the constructor + */ + @Deprecated + public void setAllowMissingFieldsInHeader(final boolean allow) { + this.allowMissingFieldsInHeader = allow; + } + + public String encode(final VariantContext context) { + if (this.header == null) { + throw new NullPointerException("The header field must be set on the VCFEncoder before encoding records."); + } + + final StringBuilder stringBuilder = new StringBuilder(); + + // CHROM + stringBuilder.append(context.getContig()).append(VCFConstants.FIELD_SEPARATOR) + // POS + .append(String.valueOf(context.getStart())).append(VCFConstants.FIELD_SEPARATOR) + // ID + .append(context.getID()).append(VCFConstants.FIELD_SEPARATOR) + // REF + .append(context.getReference().getDisplayString()).append(VCFConstants.FIELD_SEPARATOR); + + // ALT + if (context.isVariant()) { + Allele altAllele = context.getAlternateAllele(0); + String alt = altAllele.getDisplayString(); + stringBuilder.append(alt); + + for (int i = 1; i < context.getAlternateAlleles().size(); i++) { + altAllele = context.getAlternateAllele(i); + alt = altAllele.getDisplayString(); + stringBuilder.append(','); + stringBuilder.append(alt); + } + } else { + stringBuilder.append(VCFConstants.EMPTY_ALTERNATE_ALLELE_FIELD); + } + + stringBuilder.append(VCFConstants.FIELD_SEPARATOR); + + // QUAL + if (!context.hasLog10PError()) stringBuilder.append(VCFConstants.MISSING_VALUE_v4); + else stringBuilder.append(formatQualValue(context.getPhredScaledQual())); + stringBuilder.append(VCFConstants.FIELD_SEPARATOR) + // FILTER + .append(getFilterString(context)).append(VCFConstants.FIELD_SEPARATOR); + + // INFO + final Map infoFields = new TreeMap<>(); + for (final Map.Entry field : context.getAttributes().entrySet()) { + if (!this.header.hasInfoLine(field.getKey())) + fieldIsMissingFromHeaderError(context, field.getKey(), "INFO"); + + final String outputValue = formatVCFField(field.getValue()); + if (outputValue != null) infoFields.put(field.getKey(), outputValue); + } + writeInfoString(infoFields, stringBuilder); + + // FORMAT + final GenotypesContext gc = context.getGenotypes(); + if (gc.isLazyWithData() && ((LazyGenotypesContext) gc).getUnparsedGenotypeData() instanceof String) { + stringBuilder.append(VCFConstants.FIELD_SEPARATOR); + stringBuilder.append(((LazyGenotypesContext) gc).getUnparsedGenotypeData().toString()); + } else { + final List genotypeAttributeKeys = context.calcVCFGenotypeKeys(this.header); + if (!genotypeAttributeKeys.isEmpty()) { + for (final String format : genotypeAttributeKeys) + if (!this.header.hasFormatLine(format)) + fieldIsMissingFromHeaderError(context, format, "FORMAT"); + + final String genotypeFormatString = ParsingUtils.join(VCFConstants.GENOTYPE_FIELD_SEPARATOR, genotypeAttributeKeys); + + stringBuilder.append(VCFConstants.FIELD_SEPARATOR); + stringBuilder.append(genotypeFormatString); + + final Map alleleStrings = buildAlleleStrings(context); + addGenotypeData(context, alleleStrings, genotypeAttributeKeys, stringBuilder); + } + } + + return stringBuilder.toString(); + } + + VCFHeader getVCFHeader() { + return this.header; + } + + boolean getAllowMissingFieldsInHeader() { + return this.allowMissingFieldsInHeader; + } + + private String getFilterString(final VariantContext vc) { + if (vc.isFiltered()) { + for (final String filter : vc.getFilters()) { + if (!this.header.hasFilterLine(filter)) fieldIsMissingFromHeaderError(vc, filter, "FILTER"); + } + + return ParsingUtils.join(";", ParsingUtils.sortList(vc.getFilters())); + } else if (vc.filtersWereApplied()) return VCFConstants.PASSES_FILTERS_v4; + else return VCFConstants.UNFILTERED; + } + + private String formatQualValue(final double qual) { + String s = String.format(QUAL_FORMAT_STRING, qual); + if (s.endsWith(QUAL_FORMAT_EXTENSION_TO_TRIM)) + s = s.substring(0, s.length() - QUAL_FORMAT_EXTENSION_TO_TRIM.length()); + return s; + } + + private void fieldIsMissingFromHeaderError(final VariantContext vc, final String id, final String field) { + if (!allowMissingFieldsInHeader) + throw new IllegalStateException("Key " + id + " found in VariantContext field " + field + + " at " + vc.getContig() + ":" + vc.getStart() + + " but this key isn't defined in the VCFHeader. We require all VCFs to have" + + " complete VCF headers by default."); + } + + String formatVCFField(final Object val) { + final String result; + if (val == null) + result = VCFConstants.MISSING_VALUE_v4; + else if (val instanceof Double) + result = formatVCFDouble((Double) val); + else if (val instanceof Boolean) + result = (Boolean) val ? "" : null; // empty string for true, null for false + else if (val instanceof List) { + result = formatVCFField(((List) val).toArray()); + } else if (val.getClass().isArray()) { + final int length = Array.getLength(val); + if (length == 0) + return formatVCFField(null); + final StringBuilder sb = new StringBuilder(formatVCFField(Array.get(val, 0))); + for (int i = 1; i < length; i++) { + sb.append(','); + sb.append(formatVCFField(Array.get(val, i))); + } + result = sb.toString(); + } else + result = val.toString(); + + return result; + } + + /** + * Takes a double value and pretty prints it to a String for display + *

+ * Large doubles => gets %.2f style formatting + * Doubles < 1 / 10 but > 1/100 => get %.3f style formatting + * Double < 1/100 => %.3e formatting + * + * @param d + * @return + */ + public static String formatVCFDouble(final double d) { + final String format; + if (d < 1) { + if (d < 0.01) { + if (Math.abs(d) >= 1e-20) + format = "%.3e"; + else { + // return a zero format + return "0.00"; + } + } else { + format = "%.3f"; + } + } else { + format = "%.2f"; + } + + return String.format(format, d); + } + + static int countOccurrences(final char c, final String s) { + int count = 0; + for (int i = 0; i < s.length(); i++) { + count += s.charAt(i) == c ? 1 : 0; + } + return count; + } + + static boolean isMissingValue(final String s) { + // we need to deal with the case that it's a list of missing values + return (countOccurrences(VCFConstants.MISSING_VALUE_v4.charAt(0), s) + countOccurrences(',', s) == s.length()); + } + + /* + * Add the genotype data + */ + public void addGenotypeData(final VariantContext vc, final Map alleleMap, final List genotypeFormatKeys, final StringBuilder builder) { + final int ploidy = vc.getMaxPloidy(2); + + for (final String sample : this.header.getGenotypeSamples()) { + builder.append(VCFConstants.FIELD_SEPARATOR); + + Genotype g = vc.getGenotype(sample); + if (g == null) g = GenotypeBuilder.createMissing(sample, ploidy); + + final List attrs = new ArrayList(genotypeFormatKeys.size()); + for (final String field : genotypeFormatKeys) { + if (field.equals(VCFConstants.GENOTYPE_KEY)) { + if (!g.isAvailable()) { + throw new IllegalStateException("GTs cannot be missing for some samples if they are available for others in the record"); + } + + writeAllele(g.getAllele(0), alleleMap, builder); + for (int i = 1; i < g.getPloidy(); i++) { + builder.append(g.isPhased() ? VCFConstants.PHASED : VCFConstants.UNPHASED); + writeAllele(g.getAllele(i), alleleMap, builder); + } + continue; + + } else { + final String outputValue; + if (field.equals(VCFConstants.GENOTYPE_FILTER_KEY)) { + outputValue = g.isFiltered() ? g.getFilters() : VCFConstants.PASSES_FILTERS_v4; + } else { + final IntGenotypeFieldAccessors.Accessor accessor = GENOTYPE_FIELD_ACCESSORS.getAccessor(field); + if (accessor != null) { + final int[] intValues = accessor.getValues(g); + if (intValues == null) + outputValue = VCFConstants.MISSING_VALUE_v4; + else if (intValues.length == 1) // fast path + outputValue = Integer.toString(intValues[0]); + else { + final StringBuilder sb = new StringBuilder(); + sb.append(intValues[0]); + for (int i = 1; i < intValues.length; i++) { + sb.append(','); + sb.append(intValues[i]); + } + outputValue = sb.toString(); + } + } else { + Object val = g.hasExtendedAttribute(field) ? g.getExtendedAttribute(field) : VCFConstants.MISSING_VALUE_v4; + + final VCFFormatHeaderLine metaData = this.header.getFormatHeaderLine(field); + if (metaData != null) { + final int numInFormatField = metaData.getCount(vc); + if (numInFormatField > 1 && val.equals(VCFConstants.MISSING_VALUE_v4)) { + // If we have a missing field but multiple values are expected, we need to construct a new string with all fields. + // For example, if Number=2, the string has to be ".,." + final StringBuilder sb = new StringBuilder(VCFConstants.MISSING_VALUE_v4); + for (int i = 1; i < numInFormatField; i++) { + sb.append(','); + sb.append(VCFConstants.MISSING_VALUE_v4); + } + val = sb.toString(); + } + } + + // assume that if key is absent, then the given string encoding suffices + outputValue = formatVCFField(val); + } + } + + if (outputValue != null) + attrs.add(outputValue); + } + } + + // strip off trailing missing values + if (!outputTrailingFormatFields) { + for (int i = attrs.size() - 1; i >= 0; i--) { + if (isMissingValue(attrs.get(i))) attrs.remove(i); + else break; + } + } + + for (int i = 0; i < attrs.size(); i++) { + if (i > 0 || genotypeFormatKeys.contains(VCFConstants.GENOTYPE_KEY)) { + builder.append(VCFConstants.GENOTYPE_FIELD_SEPARATOR); + } + builder.append(attrs.get(i)); + } + } + } + + /* + * Create the info string; assumes that no values are null + */ + private void writeInfoString(final Map infoFields, final StringBuilder builder) { + if (infoFields.isEmpty()) { + builder.append(VCFConstants.EMPTY_INFO_FIELD); + return; + } + + boolean isFirst = true; + for (final Map.Entry entry : infoFields.entrySet()) { + if (isFirst) isFirst = false; + else builder.append(VCFConstants.INFO_FIELD_SEPARATOR); + + builder.append(entry.getKey()); + + if (!entry.getValue().equals("")) { + final VCFInfoHeaderLine metaData = this.header.getInfoHeaderLine(entry.getKey()); + if (metaData == null || metaData.getCountType() != VCFHeaderLineCount.INTEGER || metaData.getCount() != 0) { + builder.append('='); + builder.append(entry.getValue()); + } + } + } + } + + public Map buildAlleleStrings(final VariantContext vc) { + final Map alleleMap = new HashMap(vc.getAlleles().size() + 1); + alleleMap.put(Allele.NO_CALL, VCFConstants.EMPTY_ALLELE); // convenience for lookup + + final List alleles = vc.getAlleles(); + for (int i = 0; i < alleles.size(); i++) { + alleleMap.put(alleles.get(i), String.valueOf(i)); + } + + return alleleMap; + } + + private void writeAllele(final Allele allele, final Map alleleMap, final StringBuilder builder) { + final String encoding = alleleMap.get(allele); + if (encoding == null) + throw new RuntimeException("Allele " + allele + " is not an allele in the variant context"); + builder.append(encoding); + } } From 4626fe66c680c4fc65696b462391d695a8e65e71 Mon Sep 17 00:00:00 2001 From: Louis Bergelson Date: Fri, 9 Jun 2017 13:24:19 -0400 Subject: [PATCH 119/137] updating artifactory link to point to new artifactory (#892) * the Broad artifactory moved from a self hosted repository at artifactory.broadinstitute.org to a web based artifactory at broadinstitute.jfrog.io/broadinstitute/ this was causing our snapshot uploads to fail * updating our snapshot repository to point to the new link * the old artifactory has a redirect in place to the new artifactory so artifacts relying on the old url should still resolve. For unknown reasons this redirect doesn't work with gradle uploads, probably a gradle bug. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a60afe5f5..96811bbeb 100644 --- a/build.gradle +++ b/build.gradle @@ -169,7 +169,7 @@ uploadArchives { authentication(userName: project.findProperty("sonatypeUsername"), password: project.findProperty("sonatypePassword")) } - snapshotRepository(url: "https://artifactory.broadinstitute.org/artifactory/libs-snapshot-local/") { + snapshotRepository(url: "https://broadinstitute.jfrog.io/broadinstitute/libs-snapshot-local/") { authentication(userName: System.env.ARTIFACTORY_USERNAME, password: System.env.ARTIFACTORY_PASSWORD) } From 6383b25e76bc05a29852965472ab0091421365bb Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Fri, 9 Jun 2017 14:14:11 -0400 Subject: [PATCH 120/137] Fixed flipped sign when comparing read paired flags (#897) Fixed flipped sign when comparing read paired flags --- .../htsjdk/samtools/DuplicateScoringStrategy.java | 4 ++-- .../samtools/DuplicateScoringStrategyTest.java | 25 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java diff --git a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java index 292ab0654..26c83a584 100644 --- a/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java +++ b/src/main/java/htsjdk/samtools/DuplicateScoringStrategy.java @@ -36,7 +36,7 @@ public enum ScoringStrategy { SUM_OF_BASE_QUALITIES, TOTAL_MAPPED_REFERENCE_LENGTH, - RANDOM, + RANDOM } /** Hash used for the RANDOM scoring strategy. */ @@ -128,7 +128,7 @@ public static int compare(final SAMRecord rec1, final SAMRecord rec2, final Scor int cmp; // always prefer paired over non-paired - if (rec1.getReadPairedFlag() != rec2.getReadPairedFlag()) return rec1.getReadPairedFlag() ? 1 : -1; + if (rec1.getReadPairedFlag() != rec2.getReadPairedFlag()) return rec1.getReadPairedFlag() ? -1 : 1; cmp = computeDuplicateScore(rec2, scoringStrategy, assumeMateCigar) - computeDuplicateScore(rec1, scoringStrategy, assumeMateCigar); diff --git a/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java b/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java new file mode 100644 index 000000000..5943535af --- /dev/null +++ b/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java @@ -0,0 +1,25 @@ +package htsjdk.samtools; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class DuplicateScoringStrategyTest { + + @DataProvider + public Object [][] compareData() { + return new Object[][]{ + {SAMFlag.READ_PAIRED.flag, 0, true, DuplicateScoringStrategy.ScoringStrategy.RANDOM, -1}, + {0, SAMFlag.READ_PAIRED.flag, true, DuplicateScoringStrategy.ScoringStrategy.RANDOM, 1}, + }; + } + + @Test(dataProvider = "compareData") + public static void testCompare(final int samFlag1, final int samFlag2, final boolean assumeMateCigar, final DuplicateScoringStrategy.ScoringStrategy strategy, final int expected) { + final SAMRecord rec1 = new SAMRecordSetBuilder().addFrag("test", 0, 1, false, false, "36M", null, 2); + rec1.setFlags(samFlag1); + final SAMRecord rec2 = new SAMRecordSetBuilder().addFrag("test", 0, 1, true, false, "36M", null, 3); + rec2.setFlags(samFlag2); + Assert.assertEquals(DuplicateScoringStrategy.compare(rec1, rec2, strategy, assumeMateCigar), expected); + } +} \ No newline at end of file From 9f1f19111aaa9502211510b7bc6ddc5a2039f6fa Mon Sep 17 00:00:00 2001 From: Nils Homer Date: Fri, 9 Jun 2017 11:18:40 -0700 Subject: [PATCH 121/137] Standardizes mate info computation in SAMRecordSetBuilder. (#692) --- src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java index 60aae473a..b55265f71 100644 --- a/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java +++ b/src/main/java/htsjdk/samtools/SAMRecordSetBuilder.java @@ -363,13 +363,8 @@ public void addPair(final String name, final int contig, final int start1, final end1.setMappingQuality(255); end1.setReadPairedFlag(true); end1.setProperPairFlag(true); - end1.setMateReferenceIndex(contig); - end1.setAttribute(SAMTag.MC.name(), readLength + "M"); - end1.setMateAlignmentStart(start2); - end1.setMateNegativeStrandFlag(true); end1.setFirstOfPairFlag(end1IsFirstOfPair); end1.setSecondOfPairFlag(!end1IsFirstOfPair); - end1.setInferredInsertSize((int) CoordMath.getLength(start1, CoordMath.getEnd(start2, this.readLength))); end1.setAttribute(SAMTag.RG.name(), READ_GROUP_ID); if (programRecord != null) { end1.setAttribute(SAMTag.PG.name(), programRecord.getProgramGroupId()); @@ -388,13 +383,8 @@ public void addPair(final String name, final int contig, final int start1, final end2.setMappingQuality(255); end2.setReadPairedFlag(true); end2.setProperPairFlag(true); - end2.setMateReferenceIndex(contig); - end2.setAttribute(SAMTag.MC.name(), readLength + "M"); - end2.setMateAlignmentStart(start1); - end2.setMateNegativeStrandFlag(false); end2.setFirstOfPairFlag(!end1IsFirstOfPair); end2.setSecondOfPairFlag(end1IsFirstOfPair); - end2.setInferredInsertSize(end1.getInferredInsertSize()); end2.setAttribute(SAMTag.RG.name(), READ_GROUP_ID); if (programRecord != null) { end2.setAttribute(SAMTag.PG.name(), programRecord.getProgramGroupId()); @@ -404,6 +394,9 @@ public void addPair(final String name, final int contig, final int start1, final } fillInBasesAndQualities(end2); + // set mate info + SamPairUtil.setMateInfo(end1, end2, true); + this.records.add(end1); this.records.add(end2); } @@ -492,7 +485,7 @@ public void addUnmappedPair(final String name) { end1.setAttribute(SAMTag.PG.name(), programRecord.getProgramGroupId()); } if (this.unmappedHasBasesAndQualities) { - fillInBasesAndQualities(end1); + fillInBasesAndQualities(end1); } end2.setReadName(name); @@ -508,7 +501,7 @@ public void addUnmappedPair(final String name) { end2.setAttribute(SAMTag.PG.name(), programRecord.getProgramGroupId()); } if (this.unmappedHasBasesAndQualities) { - fillInBasesAndQualities(end2); + fillInBasesAndQualities(end2); } this.records.add(end1); From cc538e2832310d9a7e1cdada61c3540b92ac275f Mon Sep 17 00:00:00 2001 From: Yossi Farjoun Date: Sat, 10 Jun 2017 13:26:23 -0400 Subject: [PATCH 122/137] Typekpb patch 1 (replacing #725) (#883) * seek() checks for negative position argument value * tests --- .../seekablestream/ByteArraySeekableStream.java | 23 ++-- .../ByteArraySeekableStreamTest.java | 116 +++++++++++++++++++++ .../java/htsjdk/samtools/sra/AbstractSRATest.java | 4 +- 3 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/seekablestream/ByteArraySeekableStreamTest.java diff --git a/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java b/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java index 4f8c322c5..bb3b95af0 100644 --- a/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java +++ b/src/main/java/htsjdk/samtools/seekablestream/ByteArraySeekableStream.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2016 The Broad Institute + * Copyright (c) 2015 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,13 +24,11 @@ package htsjdk.samtools.seekablestream; -import htsjdk.samtools.seekablestream.SeekableStream; - import java.io.IOException; /** -* Created by vadim on 23/03/2015. -*/ + * Created by vadim on 23/03/2015. + */ public class ByteArraySeekableStream extends SeekableStream { private byte[] bytes; private long position = 0; @@ -51,21 +49,27 @@ public long position() throws IOException { @Override public void seek(long position) throws IOException { - this.position = position; + if (position < 0) { + throw new IllegalArgumentException("Cannot seek to a negative position, position=" + position + "."); + } else { + this.position = position; + } } @Override public int read() throws IOException { - if (position < bytes.length) + if (position < bytes.length) { return 0xFF & bytes[((int) position++)]; - else return -1; + } else { + return -1; + } } @Override public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { + } else if (off < 0 || len < 0 || len + off > b.length) { throw new IndexOutOfBoundsException(); } if (position >= bytes.length) { @@ -85,6 +89,7 @@ public int read(byte[] b, int off, int len) throws IOException { @Override public void close() throws IOException { bytes = null; + position = -1; } @Override diff --git a/src/test/java/htsjdk/samtools/seekablestream/ByteArraySeekableStreamTest.java b/src/test/java/htsjdk/samtools/seekablestream/ByteArraySeekableStreamTest.java new file mode 100644 index 000000000..04a228f94 --- /dev/null +++ b/src/test/java/htsjdk/samtools/seekablestream/ByteArraySeekableStreamTest.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright (c) 2017 The Broad Institute + * + * 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 + */ + +package htsjdk.samtools.seekablestream; + +import htsjdk.HtsjdkTest; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +/** + * Created by farjoun on 5/27/17. + */ +public class ByteArraySeekableStreamTest extends HtsjdkTest { + private final byte[] bytes = "ABCDE12345".getBytes(); + + @Test + public void testNormalBehavior() throws IOException { + ByteArraySeekableStream byteArraySeekableStream = new ByteArraySeekableStream(bytes); + + Assert.assertEquals(byteArraySeekableStream.length(), 10); + for (int i = 0; i < 10; i++) { + Assert.assertFalse(byteArraySeekableStream.eof()); + Assert.assertEquals(byteArraySeekableStream.position(), i); + Assert.assertEquals(byteArraySeekableStream.read(), bytes[i]); + } + + Assert.assertTrue(byteArraySeekableStream.eof()); + Assert.assertEquals(byteArraySeekableStream.position(), 10); + Assert.assertEquals(byteArraySeekableStream.read(), -1); + + final long i = 0; + byteArraySeekableStream.seek(i); + + Assert.assertEquals(byteArraySeekableStream.position(), i); + Assert.assertEquals(byteArraySeekableStream.read(), bytes[(int) i]); + + byte[] copy = new byte[10]; + + Assert.assertEquals(byteArraySeekableStream.read(copy), 9); + Assert.assertEquals(byteArraySeekableStream.position(), 10); + + byteArraySeekableStream.seek(0L); + + Assert.assertEquals(byteArraySeekableStream.read(copy), 10); + Assert.assertEquals(byteArraySeekableStream.position(), 10); + + Assert.assertEquals(copy, bytes); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testCantSeekNegative() throws IOException { + + ByteArraySeekableStream byteArraySeekableStream = new ByteArraySeekableStream(bytes); + + byteArraySeekableStream.seek(-1L); + + // if allowed to seek, this will throw OutOfBounds + final int f = byteArraySeekableStream.read(); + } + + @Test + public void testCantReadPostEof() throws IOException { + + ByteArraySeekableStream byteArraySeekableStream = new ByteArraySeekableStream(bytes); + byte[] copy = new byte[10]; + + byteArraySeekableStream.seek(10); + Assert.assertEquals(byteArraySeekableStream.read(copy), -1); + Assert.assertEquals(byteArraySeekableStream.read(), -1); + } + + @DataProvider(name = "abnormalReadRequests") + public Object[][] abnormalReadRequestsProvider() { + return new Object[][]{ + {new byte[10], -1, 0}, + {new byte[10], -1, -1}, + {new byte[10], 0, -1}, + {new byte[10], 0, -1}, + {new byte[10], 0, 11}, + {new byte[10], 6, 6}, + {new byte[10], 11, 0}, + }; + } + + @Test(dataProvider = "abnormalReadRequests", expectedExceptions = IndexOutOfBoundsException.class) + public void testAbnormalReadRequest(final byte[] b, final int off, final int length) throws IOException { + + ByteArraySeekableStream byteArraySeekableStream = new ByteArraySeekableStream(bytes); + int i = byteArraySeekableStream.read(b, off, length); + + Assert.assertEquals(i, -2); ///impossible + } +} diff --git a/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java b/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java index 1776a9f91..eeba1d2ea 100644 --- a/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java +++ b/src/test/java/htsjdk/samtools/sra/AbstractSRATest.java @@ -25,14 +25,14 @@ public final void checkIfCanResolve() { canResolveNetworkAccession = SRAAccession.isValid(checkAccession); } - @BeforeMethod + @BeforeMethod(groups = "sra") public final void assertSRAIsSupported() { if(SRAAccession.checkIfInitialized() != null){ throw new SkipException("Skipping SRA Test because SRA native code is unavailable."); } } - @BeforeMethod + @BeforeMethod(groups = "sra") public final void skipIfCantResolve(Method method, Object[] params) { String accession = null; From 04fd13e75a180573c58818f37037b78a49717657 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Fri, 16 Jun 2017 17:25:01 -0400 Subject: [PATCH 123/137] A little more caution when checking terminator blocks on close of BlockCompressedOutputStream. (#901) --- src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java index 4e9a59487..a1fc6c80a 100644 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedOutputStream.java @@ -28,6 +28,7 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; import java.util.zip.CRC32; import java.util.zip.Deflater; @@ -282,7 +283,7 @@ public void close() throws IOException { codec.writeBytes(BlockCompressedStreamConstants.EMPTY_GZIP_BLOCK); codec.close(); // Can't re-open something that is not a regular file, e.g. a named pipe or an output stream - if (this.file == null || !this.file.isFile()) return; + if (this.file == null || !this.file.isFile() || !Files.isRegularFile(this.file.toPath())) return; if (BlockCompressedInputStream.checkTermination(this.file) != BlockCompressedInputStream.FileTermination.HAS_TERMINATOR_BLOCK) { throw new IOException("Terminator block not found after closing BGZF file " + this.file); From 8f89e27588416b8057470d875b87ccbde7313337 Mon Sep 17 00:00:00 2001 From: Nils Homer Date: Wed, 21 Jun 2017 13:44:59 -0700 Subject: [PATCH 124/137] Sort and group order not updated when using setAttribute. (#905) --- .../htsjdk/samtools/AbstractSAMHeaderRecord.java | 3 +- src/main/java/htsjdk/samtools/SAMFileHeader.java | 39 ++++++++++++- .../java/htsjdk/samtools/SAMFileHeaderTest.java | 64 ++++++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/SAMFileHeaderTest.java diff --git a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java index 7078bf1dc..0c3d48420 100644 --- a/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java +++ b/src/main/java/htsjdk/samtools/AbstractSAMHeaderRecord.java @@ -59,8 +59,6 @@ public void setAttribute(final String key, final Object value) { /** * Set the given value for the attribute named 'key'. Replaces an existing value, if any. * If value is null, the attribute is removed. - * Supported types are Character, Integer, Float and String. Byte and Short may also be - * passed in but they will be converted to Integer. * @param key attribute name * @param value attribute value */ @@ -71,6 +69,7 @@ public void setAttribute(final String key, final String value) { mAttributes.put(key, value); } } + /** * Returns the Set of attributes. */ diff --git a/src/main/java/htsjdk/samtools/SAMFileHeader.java b/src/main/java/htsjdk/samtools/SAMFileHeader.java index f2750d4cc..eff595341 100644 --- a/src/main/java/htsjdk/samtools/SAMFileHeader.java +++ b/src/main/java/htsjdk/samtools/SAMFileHeader.java @@ -270,7 +270,7 @@ public SortOrder getSortOrder() { public void setSortOrder(final SortOrder so) { sortOrder = so; - setAttribute(SORT_ORDER_TAG, so.name()); + super.setAttribute(SORT_ORDER_TAG, so.name()); } public GroupOrder getGroupOrder() { @@ -292,7 +292,42 @@ public GroupOrder getGroupOrder() { public void setGroupOrder(final GroupOrder go) { groupOrder = go; - setAttribute(GROUP_ORDER_TAG, go.name()); + super.setAttribute(GROUP_ORDER_TAG, go.name()); + } + + + /** + * Set the given value for the attribute named 'key'. Replaces an existing value, if any. + * If value is null, the attribute is removed. + * Otherwise, the value will be converted to a String with toString. + * @param key attribute name + * @param value attribute value + * @deprecated Use {@link #setAttribute(String, String) instead + */ + @Deprecated + @Override + public void setAttribute(final String key, final Object value) { + if (key.equals(SORT_ORDER_TAG) || key.equals(GROUP_ORDER_TAG)) { + this.setAttribute(key, value.toString()); + } else { + super.setAttribute(key, value); + } + } + + /** + * Set the given value for the attribute named 'key'. Replaces an existing value, if any. + * If value is null, the attribute is removed. + * @param key attribute name + * @param value attribute value + */ + @Override + public void setAttribute(final String key, final String value) { + if (key.equals(SORT_ORDER_TAG)) { + this.sortOrder = null; + } else if (key.equals(GROUP_ORDER_TAG)) { + this.groupOrder = null; + } + super.setAttribute(key, value); } /** diff --git a/src/test/java/htsjdk/samtools/SAMFileHeaderTest.java b/src/test/java/htsjdk/samtools/SAMFileHeaderTest.java new file mode 100644 index 000000000..0723ed9e4 --- /dev/null +++ b/src/test/java/htsjdk/samtools/SAMFileHeaderTest.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (c) 2017 Nils Homer + * + * 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 + */ +package htsjdk.samtools; + +import htsjdk.HtsjdkTest; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SAMFileHeaderTest extends HtsjdkTest { + + @Test + public void testSortOrder() { + final SAMFileHeader header = new SAMFileHeader(); + + header.setSortOrder(SAMFileHeader.SortOrder.coordinate); + Assert.assertEquals(header.getSortOrder(), SAMFileHeader.SortOrder.coordinate); + Assert.assertEquals(header.getAttribute(SAMFileHeader.SORT_ORDER_TAG), SAMFileHeader.SortOrder.coordinate.name()); + + header.setAttribute(SAMFileHeader.SORT_ORDER_TAG, SAMFileHeader.SortOrder.queryname.name()); + Assert.assertEquals(header.getSortOrder(), SAMFileHeader.SortOrder.queryname); + Assert.assertEquals(header.getAttribute(SAMFileHeader.SORT_ORDER_TAG), SAMFileHeader.SortOrder.queryname.name()); + + header.setAttribute(SAMFileHeader.SORT_ORDER_TAG, SAMFileHeader.SortOrder.coordinate); + Assert.assertEquals(header.getSortOrder(), SAMFileHeader.SortOrder.coordinate); + Assert.assertEquals(header.getAttribute(SAMFileHeader.SORT_ORDER_TAG), SAMFileHeader.SortOrder.coordinate.name()); + } + + @Test + public void testGroupOrder() { + final SAMFileHeader header = new SAMFileHeader(); + + header.setGroupOrder(SAMFileHeader.GroupOrder.query); + Assert.assertEquals(header.getGroupOrder(), SAMFileHeader.GroupOrder.query); + Assert.assertEquals(header.getAttribute(SAMFileHeader.GROUP_ORDER_TAG), SAMFileHeader.GroupOrder.query.name()); + + header.setAttribute(SAMFileHeader.GROUP_ORDER_TAG, SAMFileHeader.GroupOrder.reference.name()); + Assert.assertEquals(header.getGroupOrder(), SAMFileHeader.GroupOrder.reference); + Assert.assertEquals(header.getAttribute(SAMFileHeader.GROUP_ORDER_TAG), SAMFileHeader.GroupOrder.reference.name()); + + header.setAttribute(SAMFileHeader.GROUP_ORDER_TAG, SAMFileHeader.GroupOrder.query); + Assert.assertEquals(header.getGroupOrder(), SAMFileHeader.GroupOrder.query); + Assert.assertEquals(header.getAttribute(SAMFileHeader.GROUP_ORDER_TAG), SAMFileHeader.GroupOrder.query.name()); + } +} From c20e0edd361189c3f2bc3718b018dac2ec90530b Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Sun, 25 Jun 2017 22:35:24 -0400 Subject: [PATCH 125/137] Add more checks to SamFileValidator (#907) --- src/main/java/htsjdk/samtools/SAMRecord.java | 85 +++++++++++++++------- .../htsjdk/samtools/SAMSequenceDictionary.java | 50 ++++++++++--- src/main/java/htsjdk/samtools/SAMTestUtil.java | 50 ++++--------- .../java/htsjdk/samtools/SAMValidationError.java | 17 ++++- .../java/htsjdk/samtools/SamFileValidator.java | 42 ++++++----- .../java/htsjdk/samtools/SAMRecordUnitTest.java | 2 +- .../htsjdk/samtools/SAMSequenceDictionaryTest.java | 24 ++++++ .../htsjdk/samtools/SAMSequenceRecordTest.java | 45 +++++++++++- .../java/htsjdk/samtools/ValidateSamFileTest.java | 25 +++++-- .../cram/lossy/QualityScorePreservationTest.java | 2 +- .../htsjdk/samtools/util/SequenceUtilTest.java | 4 +- .../SequenceUtil/upper_and_lowercase_read.sam | 2 +- .../ValidateSamFileTest/seq_qual_len_mismatch.sam | 21 ++++++ 13 files changed, 261 insertions(+), 108 deletions(-) create mode 100644 src/test/resources/htsjdk/samtools/ValidateSamFileTest/seq_qual_len_mismatch.sam diff --git a/src/main/java/htsjdk/samtools/SAMRecord.java b/src/main/java/htsjdk/samtools/SAMRecord.java index f93b2d72c..ec394ca17 100644 --- a/src/main/java/htsjdk/samtools/SAMRecord.java +++ b/src/main/java/htsjdk/samtools/SAMRecord.java @@ -1519,7 +1519,7 @@ public SAMTagAndValue(final String tag, final Object value) { */ public List getAttributes() { SAMBinaryTagAndValue binaryAttributes = getBinaryAttributes(); - final List ret = new ArrayList(); + final List ret = new ArrayList<>(); while (binaryAttributes != null) { ret.add(new SAMTagAndValue(SAMTagUtil.getSingleton().makeStringTag(binaryAttributes.tag), binaryAttributes.value)); @@ -1769,7 +1769,7 @@ protected void eagerDecode() { /** * Run all validations of CIGAR. These include validation that the CIGAR makes sense independent of * placement, plus validation that CIGAR + placement yields all bases with M operator within the range of the reference. - * @param recordNumber For error reporting. -1 if not known. + * @param recordNumber For error reporting, the record number in the SAM/BAM file. -1 if not known. * @return List of errors, or null if no errors. */ public List validateCigar(final long recordNumber) { @@ -1878,35 +1878,40 @@ public int hashCode() { ArrayList ret = null; if (!getReadPairedFlag()) { if (getProperPairFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_PROPER_PAIR, "Proper pair flag should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } if (getMateUnmappedFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_MATE_UNMAPPED, "Mate unmapped flag should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } if (getMateNegativeStrandFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_MATE_NEG_STRAND, "Mate negative strand flag should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } if (getFirstOfPairFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_FIRST_OF_PAIR, "First of pair flag should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } if (getSecondOfPairFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_SECOND_OF_PAIR, "Second of pair flag should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } if (null != getHeader() && getMateReferenceIndex() != NO_ALIGNMENT_REFERENCE_INDEX) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_MATE_REF_INDEX, "MRNM should not be set for unpaired read.", getReadName())); if (firstOnly) return ret; } + if (!getMateReferenceName().equals(SAMRecord.NO_ALIGNMENT_REFERENCE_NAME)) { + if (ret == null) ret = new ArrayList<>(); + ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_UNPAIRED_MATE_REFERENCE, "Unpaired read mate reference is " + getMateReferenceName() + " not " + SAMRecord.NO_ALIGNMENT_REFERENCE_NAME + " for unpaired read", getReadName())); + if (firstOnly) return ret; + } } else { final List errors = isValidReferenceIndexAndPosition(mMateReferenceIndex, mMateReferenceName, getMateAlignmentStart(), true, firstOnly); @@ -1937,23 +1942,23 @@ public int hashCode() { */ } if (getInferredInsertSize() > MAX_INSERT_SIZE || getInferredInsertSize() < -MAX_INSERT_SIZE) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_INSERT_SIZE, "Insert size out of range", getReadName())); if (firstOnly) return ret; } if (getReadUnmappedFlag()) { if (getNotPrimaryAlignmentFlag()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_NOT_PRIM_ALIGNMENT, "Not primary alignment flag should not be set for unmapped read.", getReadName())); if (firstOnly) return ret; } if (getSupplementaryAlignmentFlag()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_SUPPLEMENTARY_ALIGNMENT, "Supplementary alignment flag should not be set for unmapped read.", getReadName())); if (firstOnly) return ret; } if (getMappingQuality() != 0) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_MAPPING_QUALITY, "MAPQ should be 0 for unmapped read.", getReadName())); if (firstOnly) return ret; } @@ -1962,22 +1967,22 @@ public int hashCode() { TODO: PIC-97 This validation should be enabled, but probably at this point there are too many BAM files that have the proper pair flag set when read or mate is unmapped. if (getProperPairFlagUnchecked()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_PROPER_PAIR, "Proper pair flag should not be set for unmapped read.", getReadName())); } */ } else { if (getMappingQuality() >= 256) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_MAPPING_QUALITY, "MAPQ should be < 256.", getReadName())); if (firstOnly) return ret; } if (getCigarLength() == 0) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_CIGAR, "CIGAR should have > zero elements for mapped read.", getReadName())); /* todo - will uncomment once unit tests are added } else if (getCigar().getReadLength() != getReadLength()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_CIGAR, "CIGAR read length " + getCigar().getReadLength() + " doesn't match read length " + getReadLength(), getReadName())); */ if (firstOnly) return ret; @@ -1988,7 +1993,7 @@ public int hashCode() { if (firstOnly) return ret; } if (!hasReferenceName()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_FLAG_READ_UNMAPPED, "Mapped read should have valid reference name", getReadName())); if (firstOnly) return ret; } @@ -2006,14 +2011,14 @@ public int hashCode() { // Validate the RG ID is found in header final String rgId = (String)getAttribute(SAMTagUtil.getSingleton().RG); if (rgId != null && getHeader() != null && getHeader().getReadGroup(rgId) == null) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.READ_GROUP_NOT_FOUND, "RG ID on SAMRecord not found in header: " + rgId, getReadName())); if (firstOnly) return ret; } final List errors = isValidReferenceIndexAndPosition(mReferenceIndex, mReferenceName, getAlignmentStart(), false); if (errors != null) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.addAll(errors); if (firstOnly) return ret; } @@ -2024,7 +2029,7 @@ public int hashCode() { final String cq = (String)getAttribute(SAMTagUtil.getSingleton().CQ); final String cs = (String)getAttribute(SAMTagUtil.getSingleton().CS); if (cq == null || cq.isEmpty() || cs == null || cs.isEmpty()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.EMPTY_READ, "Zero-length read without FZ, CS or CQ tag", getReadName())); if (firstOnly) return ret; @@ -2038,7 +2043,7 @@ public int hashCode() { } } if (!hasIndel) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.EMPTY_READ, "Colorspace read with zero-length bases but no indel", getReadName())); if (firstOnly) return ret; @@ -2047,7 +2052,7 @@ public int hashCode() { } } if (this.getReadLength() != getBaseQualities().length && !Arrays.equals(getBaseQualities(), NULL_QUALS)) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.MISMATCH_READ_LENGTH_AND_QUALS_LENGTH, "Read length does not match quals length", getReadName())); if (firstOnly) return ret; @@ -2055,13 +2060,39 @@ public int hashCode() { if (this.getAlignmentStart() != NO_ALIGNMENT_START && this.getIndexingBin() != null && this.computeIndexingBin() != this.getIndexingBin()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_INDEXING_BIN, "bin field of BAM record does not equal value computed based on alignment start and end, and length of sequence to which read is aligned", getReadName())); if (firstOnly) return ret; } + if (getMateReferenceName().equals(SAMRecord.NO_ALIGNMENT_REFERENCE_NAME) && + getMateAlignmentStart() != SAMRecord.NO_ALIGNMENT_START) { + if (ret == null) ret = new ArrayList<>(); + ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_UNALIGNED_MATE_START, + "The unaligned mate start position is " + getAlignmentStart() + ", should be " + SAMRecord.NO_ALIGNMENT_START, + getReadName())); + if (firstOnly) return ret; + } + + if (getCigar().getReadLength() != 0 && getCigar().getReadLength() != getReadLength()) { + if (ret == null) ret = new ArrayList<>(); + ret.add(new SAMValidationError(SAMValidationError.Type.MISMATCH_CIGAR_SEQ_LENGTH, + "CIGAR covers " + getCigar().getReadLength() + " bases but the sequence is " + getReadLength() + " read bases ", + getReadName())); + if (firstOnly) return ret; + } + + if (getBaseQualities().length != 0 && getReadLength() != getBaseQualities().length) { + if (ret == null) ret = new ArrayList<>(); + ret.add(new SAMValidationError( + SAMValidationError.Type.MISMATCH_SEQ_QUAL_LENGTH, + "Read length is " + getReadLength() + " bases but have " + mBaseQualities.length + " qualities ", + getReadName())); + if (firstOnly) return ret; + } + if (ret == null || ret.isEmpty()) { return null; } @@ -2099,13 +2130,13 @@ protected void setFileSource(final SAMFileSource fileSource) { ArrayList ret = null; if (!hasReference) { if (alignmentStart != 0) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_ALIGNMENT_START, buildMessage("Alignment start should be 0 because reference name = *.", isMate), getReadName())); if (firstOnly) return ret; } } else { if (alignmentStart == 0) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_ALIGNMENT_START, buildMessage("Alignment start should != 0 because reference name != *.", isMate), getReadName())); if (firstOnly) return ret; } @@ -2113,12 +2144,12 @@ protected void setFileSource(final SAMFileSource fileSource) { final SAMSequenceRecord sequence = (referenceIndex != null? getHeader().getSequence(referenceIndex): getHeader().getSequence(referenceName)); if (sequence == null) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_REFERENCE_INDEX, buildMessage("Reference sequence not found in sequence dictionary.", isMate), getReadName())); if (firstOnly) return ret; } else { if (alignmentStart > sequence.getSequenceLength()) { - if (ret == null) ret = new ArrayList(); + if (ret == null) ret = new ArrayList<>(); ret.add(new SAMValidationError(SAMValidationError.Type.INVALID_ALIGNMENT_START, buildMessage("Alignment start (" + alignmentStart + ") must be <= reference sequence length (" + sequence.getSequenceLength() + ") on reference " + sequence.getSequenceName(), isMate), getReadName())); if (firstOnly) return ret; diff --git a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java index b7744d796..86ffa6c9f 100644 --- a/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java +++ b/src/main/java/htsjdk/samtools/SAMSequenceDictionary.java @@ -29,7 +29,6 @@ import java.math.BigInteger; import java.security.MessageDigest; import java.util.*; -import java.util.stream.Collector; import java.util.stream.Collectors; import javax.xml.bind.annotation.XmlElement; @@ -50,8 +49,8 @@ getter because the later wraps the list into an unmodifiable List see http://tech.joshuacummings.com/2010/10/problems-with-defensive-collection.html */ @XmlElement(name="Reference") - private List mSequences = new ArrayList(); - private final Map mSequenceMap = new HashMap(); + private List mSequences = new ArrayList<>(); + private final Map mSequenceMap = new HashMap<>(); public SAMSequenceDictionary() { } @@ -150,7 +149,7 @@ public boolean isEmpty() { private static String DICT_MISMATCH_TEMPLATE = "SAM dictionaries are not the same: %s."; /** * Non-comprehensive {@link #equals(Object)}-assertion: instead of calling {@link SAMSequenceRecord#equals(Object)} on constituent - * {@link SAMSequenceRecord}s in this dictionary against its pair in the target dictionary, in order, call + * {@link SAMSequenceRecord}s in this dictionary against its pair in the target dictionary, in order, call * {@link SAMSequenceRecord#isSameSequence(SAMSequenceRecord)}. * Aliases are ignored. * @@ -161,20 +160,49 @@ public void assertSameDictionary(final SAMSequenceDictionary that) { final Iterator thatSequences = that.mSequences.iterator(); for (final SAMSequenceRecord thisSequence : mSequences) { - if (!thatSequences.hasNext()) + if (!thatSequences.hasNext()) { throw new AssertionError(String.format(DICT_MISMATCH_TEMPLATE, thisSequence + " is present in only one dictionary")); - else { + } else { final SAMSequenceRecord thatSequence = thatSequences.next(); - if(!thatSequence.isSameSequence(thisSequence)) + if(!thatSequence.isSameSequence(thisSequence)) { throw new AssertionError( String.format(DICT_MISMATCH_TEMPLATE, thatSequence + " was found when " + thisSequence + " was expected") ); + } } } if (thatSequences.hasNext()) throw new AssertionError(String.format(DICT_MISMATCH_TEMPLATE, thatSequences.next() + " is present in only one dictionary")); } + /** + * Non-comprehensive {@link #equals(Object)}-validation: instead of calling {@link SAMSequenceRecord#equals(Object)} on constituent + * {@link SAMSequenceRecord}s in this dictionary against its pair in the target dictionary, in order, call + * {@link SAMSequenceRecord#isSameSequence(SAMSequenceRecord)}. + * + * @param that {@link SAMSequenceDictionary} to compare against + * @return true if the dictionaries are the same, false otherwise + * + */ + public boolean isSameDictionary(final SAMSequenceDictionary that) { + if (that == null || that.mSequences == null) return false; + if (this == that) return true; + + final Iterator thatSequences = that.mSequences.iterator(); + for (final SAMSequenceRecord thisSequence : mSequences) { + if (!thatSequences.hasNext()) { + return false; + } else { + final SAMSequenceRecord thatSequence = thatSequences.next(); + if (!thatSequence.isSameSequence(thisSequence)) { + return false; + } + } + } + + return !thatSequences.hasNext(); + } + /** returns true if the two dictionaries are the same, aliases are NOT considered */ @Override public boolean equals(Object o) { @@ -183,9 +211,7 @@ public boolean equals(Object o) { SAMSequenceDictionary that = (SAMSequenceDictionary) o; - if (!mSequences.equals(that.mSequences)) return false; - - return true; + return mSequences.equals(that.mSequences); } /** @@ -318,8 +344,8 @@ static public SAMSequenceDictionary mergeDictionaries(final SAMSequenceDictionar finalDict.addSequence(sMerged); final Set allTags = new HashSet<>(); - s1.getAttributes().stream().forEach(a -> allTags.add(a.getKey())); - s2.getAttributes().stream().forEach(a -> allTags.add(a.getKey())); + s1.getAttributes().forEach(a -> allTags.add(a.getKey())); + s2.getAttributes().forEach(a -> allTags.add(a.getKey())); for (final String tag : allTags) { final String value1 = s1.getAttribute(tag); diff --git a/src/main/java/htsjdk/samtools/SAMTestUtil.java b/src/main/java/htsjdk/samtools/SAMTestUtil.java index 83766f367..ec85ce2da 100644 --- a/src/main/java/htsjdk/samtools/SAMTestUtil.java +++ b/src/main/java/htsjdk/samtools/SAMTestUtil.java @@ -23,6 +23,8 @@ */ package htsjdk.samtools; +import java.util.List; + /** * Misc methods for SAM-related unit tests. These are in the src tree rather than the tests tree * so that they will be included in sam.jar, and therefore can be used by tests outside of htsjdk.samtools. @@ -55,47 +57,21 @@ public void assertPairValid(final SAMRecord firstEnd, final SAMRecord secondEnd) } /** - * Basic sanity check for a SAMRecord. - * @throws SanityCheckFailedException if the sanity check failed + * Basic sanity check for a SAMRecord. Print errors to screen. + * @param read SAM record + * @throws IllegalArgumentException if read is null + * @throws SanityCheckFailedException if errors */ - public void assertReadValid(final SAMRecord read) throws SanityCheckFailedException { - assertEquals(read.getReadBases().length, read.getBaseQualities().length); - // Note that it is possible to have an unmapped read that has a coordinate - if (read.getReferenceName().equals(SAMRecord.NO_ALIGNMENT_REFERENCE_NAME)) { - assertEquals(read.getAlignmentStart(), SAMRecord.NO_ALIGNMENT_START); - assertTrue(read.getReadUnmappedFlag()); - } else { - assertNotSame(read.getAlignmentStart(), SAMRecord.NO_ALIGNMENT_START); - } - if (read.getReadUnmappedFlag()) { - assertEquals(read.getMappingQuality(), SAMRecord.NO_MAPPING_QUALITY); - assertEquals(read.getCigar().getCigarElements().size(), 0); - } else { - assertNotSame(read.getCigar().getCigarElements(), 0); + public static void assertReadValid(final SAMRecord read) throws SanityCheckFailedException { + if (read == null) { + throw new IllegalArgumentException("SAMRecord is null"); } - if (read.getReadPairedFlag()) { - if (read.getMateReferenceName().equals(SAMRecord.NO_ALIGNMENT_REFERENCE_NAME)) { - assertEquals(read.getMateAlignmentStart(), SAMRecord.NO_ALIGNMENT_START); - assertTrue(read.getMateUnmappedFlag()); - } else { - // Even if the mate is unmapped, if it has a reference name, it should have a position. - assertNotSame(read.getMateAlignmentStart(), SAMRecord.NO_ALIGNMENT_START); - } - if (read.getReadUnmappedFlag() || read.getMateUnmappedFlag() || - !read.getReferenceName().equals(read.getMateReferenceName())) { - assertEquals(read.getInferredInsertSize(), 0); - } else { - assertNotSame(read.getInferredInsertSize(), 0); - } - if (!read.getReadUnmappedFlag() && !read.getMateUnmappedFlag()) { - assertNotSame(read.getReadNegativeStrandFlag(), read.getMateNegativeStrandFlag()); - assertNotSame(read.getMateNegativeStrandFlag(), - read.getReadName()); - } - } else { - assertEquals(read.getInferredInsertSize(), 0); + final List errors = read.isValid(false); + if ( errors != null) { + errors.forEach(v -> System.out.println(v.toString())); } + assertTrue(errors.isEmpty()); } private static void assertEquals(T a, T b) { diff --git a/src/main/java/htsjdk/samtools/SAMValidationError.java b/src/main/java/htsjdk/samtools/SAMValidationError.java index 452e92cf5..edd49c13c 100644 --- a/src/main/java/htsjdk/samtools/SAMValidationError.java +++ b/src/main/java/htsjdk/samtools/SAMValidationError.java @@ -208,7 +208,22 @@ MISMATCH_MATE_CIGAR_STRING, /** There is a Cigar String (stored in the MC Tag) for a read whose mate is NOT mapped. */ - MATE_CIGAR_STRING_INVALID_PRESENCE; + MATE_CIGAR_STRING_INVALID_PRESENCE, + + /** The mate reference of the unpaired read should be "*" */ + INVALID_UNPAIRED_MATE_REFERENCE, + + /** The unaligned mate read start position should be 0 */ + INVALID_UNALIGNED_MATE_START, + + /** Mismatch between the number of bases covered by the CIGAR and sequence */ + MISMATCH_CIGAR_SEQ_LENGTH, + + /** Mismatch between the sequence and quality length */ + MISMATCH_SEQ_QUAL_LENGTH, + + /** Mismatch between file and sequence dictionaries */ + MISMATCH_FILE_SEQ_DICT; public final Severity severity; diff --git a/src/main/java/htsjdk/samtools/SamFileValidator.java b/src/main/java/htsjdk/samtools/SamFileValidator.java index d0b745e7f..3e316a235 100644 --- a/src/main/java/htsjdk/samtools/SamFileValidator.java +++ b/src/main/java/htsjdk/samtools/SamFileValidator.java @@ -88,6 +88,7 @@ private Histogram errorsByType; private PairEndInfoMap pairEndInfoByName; private ReferenceSequenceFileWalker refFileWalker; + private SAMSequenceDictionary samSequenceDictionary; private boolean verbose; private int maxVerboseOutput; private SAMSortOrderChecker orderChecker; @@ -154,7 +155,7 @@ public boolean validateSamFileSummary(final SamReader samReader, final Reference for (final Histogram.Bin bin : errorsByType.values()) { errorsAndWarningsByType.increment(bin.getId().getHistogramString(), bin.getValue()); } - final MetricsFile metricsFile = new MetricsFile(); + final MetricsFile metricsFile = new MetricsFile<>(); errorsByType.setBinLabel("Error Type"); errorsByType.setValueLabel("Count"); metricsFile.setHistogram(errorsAndWarningsByType); @@ -180,7 +181,7 @@ public boolean validateSamFileVerbose(final SamReader samReader, final Reference } catch (MaxOutputExceededException e) { out.println("Maximum output of [" + maxVerboseOutput + "] errors reached."); } - boolean result = errorsByType.isEmpty(); + final boolean result = errorsByType.isEmpty(); cleanup(); return result; } @@ -249,13 +250,13 @@ private void validateUnmatchedPairs() { // For the coordinate-sorted map, need to detect mate pairs in which the mateReferenceIndex on one end // does not match the readReference index on the other end, so the pairs weren't united and validated. inMemoryPairMap = new InMemoryPairEndInfoMap(); - CloseableIterator> it = ((CoordinateSortedPairEndInfoMap) pairEndInfoByName).iterator(); + final CloseableIterator> it = pairEndInfoByName.iterator(); while (it.hasNext()) { - Map.Entry entry = it.next(); - PairEndInfo pei = inMemoryPairMap.remove(entry.getValue().readReferenceIndex, entry.getKey()); + final Map.Entry entry = it.next(); + final PairEndInfo pei = inMemoryPairMap.remove(entry.getValue().readReferenceIndex, entry.getKey()); if (pei != null) { // Found a mismatch btw read.mateReferenceIndex and mate.readReferenceIndex - List errors = pei.validateMates(entry.getValue(), entry.getKey()); + final List errors = pei.validateMates(entry.getValue(), entry.getKey()); for (final SAMValidationError error : errors) { addError(error); } @@ -405,10 +406,7 @@ private void validateSecondaryBaseCalls(final SAMRecord record, final long recor } private boolean validateCigar(final SAMRecord record, final long recordNumber) { - if (record.getReadUnmappedFlag()) { - return true; - } - return validateCigar(record, recordNumber, true); + return record.getReadUnmappedFlag() || validateCigar(record, recordNumber, true); } private boolean validateMateCigar(final SAMRecord record, final long recordNumber) { @@ -458,6 +456,7 @@ private void init(final ReferenceSequenceFile reference, final SAMFileHeader hea } if (reference != null) { this.refFileWalker = new ReferenceSequenceFileWalker(reference); + this.samSequenceDictionary = reference.getSequenceDictionary(); } } @@ -525,6 +524,12 @@ private void validateHeader(final SAMFileHeader fileHeader) { } if (fileHeader.getSequenceDictionary().isEmpty()) { sequenceDictionaryEmptyAndNoWarningEmitted = true; + } else { + if (samSequenceDictionary != null) { + if (!fileHeader.getSequenceDictionary().isSameDictionary(samSequenceDictionary)) { + addError(new SAMValidationError(Type.MISMATCH_FILE_SEQ_DICT, "Mismatch between file and sequence dictionary", null)); + } + } } if (fileHeader.getReadGroups().isEmpty()) { addError(new SAMValidationError(Type.MISSING_READ_GROUP, "Read groups is empty", null)); @@ -540,7 +545,7 @@ private void validateHeader(final SAMFileHeader fileHeader) { } final List rgs = fileHeader.getReadGroups(); - final Set readGroupIDs = new HashSet(); + final Set readGroupIDs = new HashSet<>(); for (final SAMReadGroupRecord record : rgs) { final String readGroupID = record.getReadGroupId(); @@ -692,11 +697,10 @@ public PairEndInfo(final SAMRecord record, final long recordNumber) { this.firstOfPairFlag = record.getFirstOfPairFlag(); } - private PairEndInfo(int readAlignmentStart, int readReferenceIndex, boolean readNegStrandFlag, boolean readUnmappedFlag, - String readCigarString, - int mateAlignmentStart, int mateReferenceIndex, boolean mateNegStrandFlag, boolean mateUnmappedFlag, - String mateCigarString, - boolean firstOfPairFlag, long recordNumber) { + private PairEndInfo(final int readAlignmentStart, final int readReferenceIndex, final boolean readNegStrandFlag, final boolean readUnmappedFlag, + final String readCigarString, + final int mateAlignmentStart, final int mateReferenceIndex, final boolean mateNegStrandFlag, final boolean mateUnmappedFlag, + final String mateCigarString, final boolean firstOfPairFlag, final long recordNumber) { this.readAlignmentStart = readAlignmentStart; this.readReferenceIndex = readReferenceIndex; this.readNegStrandFlag = readNegStrandFlag; @@ -712,7 +716,7 @@ private PairEndInfo(int readAlignmentStart, int readReferenceIndex, boolean read } public List validateMates(final PairEndInfo mate, final String readName) { - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); validateMateFields(this, mate, readName, errors); validateMateFields(mate, this, readName, errors); // Validations that should not be repeated on both ends @@ -789,7 +793,7 @@ private void validateMateFields(final PairEndInfo end1, final PairEndInfo end2, private class CoordinateSortedPairEndInfoMap implements PairEndInfoMap { private final CoordinateSortedPairInfoMap onDiskMap = - new CoordinateSortedPairInfoMap(maxTempFiles, new Codec()); + new CoordinateSortedPairInfoMap<>(maxTempFiles, new Codec()); @Override public void put(int mateReferenceIndex, String key, PairEndInfo value) { @@ -877,7 +881,7 @@ public void encode(final String key, final PairEndInfo record) { } private static class InMemoryPairEndInfoMap implements PairEndInfoMap { - private final Map map = new HashMap(); + private final Map map = new HashMap<>(); @Override public void put(int mateReferenceIndex, String key, PairEndInfo value) { diff --git a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java index 1bfe26345..5fa35f3e9 100644 --- a/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java +++ b/src/test/java/htsjdk/samtools/SAMRecordUnitTest.java @@ -463,7 +463,7 @@ public void test_setAttribute_null_removes_tag() { } private SAMRecord createTestRecordHelper() { - return new SAMRecordSetBuilder().addFrag("test", 0, 1, false, false, "3S9M", null, 2); + return new SAMRecordSetBuilder().addFrag("test", 0, 1, false, false, "3S33M", null, 2); } @Test diff --git a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java index 8b1763067..a8e60ed50 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceDictionaryTest.java @@ -39,6 +39,7 @@ import java.io.StringWriter; import java.util.Arrays; import java.util.Collections; +import java.util.List; public class SAMSequenceDictionaryTest extends HtsjdkTest { @Test @@ -142,4 +143,27 @@ public void testMergeDictionaries(final SAMSequenceRecord rec1, final SAMSequenc throw new Exception("Expected to not be able to merge dictionaries, but was able"); } } + + @DataProvider + public Object[][] testIsSameDictionaryData() { + + final SAMSequenceRecord rec1, rec2; + rec1 = new SAMSequenceRecord("chr1", 100); + rec2 = new SAMSequenceRecord("chr2", 101); + + return new Object[][]{ + new Object[]{Arrays.asList(rec1), Arrays.asList(rec1), true}, + new Object[]{Arrays.asList(rec1), Arrays.asList(rec2), false}, + new Object[]{Arrays.asList(rec1, rec2), Arrays.asList(rec1), false} + }; + } + + @Test(dataProvider = "testIsSameDictionaryData") + public void testIsSameDictionary(final List recs1, final List recs2, final boolean isSameDictionary) { + + final SAMSequenceDictionary dict1 = new SAMSequenceDictionary(recs1); + final SAMSequenceDictionary dict2 = new SAMSequenceDictionary(recs2); + + Assert.assertEquals(dict1.isSameDictionary(dict2), isSameDictionary); + } } diff --git a/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java index 1035d1b10..e0c73d502 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java @@ -24,8 +24,11 @@ package htsjdk.samtools; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.Arrays; + /** * Test for SAMReadGroupRecordTest */ @@ -33,10 +36,50 @@ @Test public void testGetSAMString() { - SAMSequenceRecord r = new SAMSequenceRecord("chr5_but_without_a_prefix", 271828); + final SAMSequenceRecord r = new SAMSequenceRecord("chr5_but_without_a_prefix", 271828); r.setSpecies("Psephophorus terrypratchetti"); r.setAssembly("GRCt01"); r.setMd5("7a6dd3d307de916b477e7bf304ac22bc"); Assert.assertEquals("@SQ\tSN:chr5_but_without_a_prefix\tLN:271828\tSP:Psephophorus terrypratchetti\tAS:GRCt01\tM5:7a6dd3d307de916b477e7bf304ac22bc", r.getSAMString()); } + + @DataProvider + public Object[][] testIsSameSequenceData() { + final SAMSequenceRecord rec1 = new SAMSequenceRecord("chr1", 100); + final SAMSequenceRecord rec2 = new SAMSequenceRecord("chr2", 101); + final SAMSequenceRecord rec3 = new SAMSequenceRecord("chr3", 0); + final SAMSequenceRecord rec4 = new SAMSequenceRecord("chr1", 100); + + final String md5One = "1"; + final String md5Two = "2"; + final int index1 = 1; + final int index2 = 2; + + return new Object[][]{ + new Object[]{rec1, rec1, md5One, md5One, index1, index1, true}, + new Object[]{rec1, null, md5One, md5One, index1, index1, false}, + new Object[]{rec1, rec4, md5One, md5One, index1, index1, true}, + new Object[]{rec1, rec4, md5One, md5One, index1, index2, false}, + new Object[]{rec1, rec3, md5One, md5Two, index1, index1, false}, + new Object[]{rec1, rec2, md5One, md5Two, index1, index1, false}, + new Object[]{rec1, rec4, md5One, null, index1, index1, true}, + new Object[]{rec1, rec4, null, md5One, index1, index1, true}, + new Object[]{rec1, rec4, md5One, md5One, index1, index2, false} + }; + } + + @Test(dataProvider = "testIsSameSequenceData") + public void testIsSameSequence(final SAMSequenceRecord rec1 , final SAMSequenceRecord rec2, final String md5One, final String md5Two, + final int index1, final int index2, final boolean isSame) { + if (rec2 != null) { + rec2.setMd5(md5Two); + rec2.setSequenceIndex(index2); + } + + if (rec1 != null) { + rec1.setMd5(md5One); + rec1.setSequenceIndex(index1); + Assert.assertEquals(rec1.isSameSequence(rec2), isSame); + } + } } diff --git a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java index 292758b8c..8aac6e2e3 100644 --- a/src/test/java/htsjdk/samtools/ValidateSamFileTest.java +++ b/src/test/java/htsjdk/samtools/ValidateSamFileTest.java @@ -147,6 +147,7 @@ public void testUnpairedRecords() throws IOException { Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_FLAG_FIRST_OF_PAIR.getHistogramString()).getValue(), 1.0); Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_FLAG_SECOND_OF_PAIR.getHistogramString()).getValue(), 1.0); Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_MATE_REF_INDEX.getHistogramString()).getValue(), 1.0); + Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_UNPAIRED_MATE_REFERENCE.getHistogramString()).getValue(), 1.0); } @Test @@ -173,6 +174,7 @@ public void testPairedRecords() throws IOException { Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_FLAG_MATE_UNMAPPED.getHistogramString()).getValue(), 1.0); Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_MATE_ALIGNMENT_START.getHistogramString()).getValue(), 2.0); Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_MATE_REF_INDEX.getHistogramString()).getValue(), 2.0); + Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_UNALIGNED_MATE_START.getHistogramString()).getValue(), 1.0); } @Test(dataProvider = "missingMateTestCases") @@ -232,6 +234,7 @@ public void testMappedRecords() throws IOException { Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_CIGAR.getHistogramString()).getValue(), 1.0); Assert.assertEquals(results.get(SAMValidationError.Type.INVALID_FLAG_READ_UNMAPPED.getHistogramString()).getValue(), 1.0); Assert.assertEquals(results.get(SAMValidationError.Type.MISSING_TAG_NM.getHistogramString()).getValue(), 1.0); + Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_CIGAR_SEQ_LENGTH.getHistogramString()).getValue(), 1.0); } @Test @@ -300,11 +303,10 @@ public void testMateCigarScenarios(final String scenario, final String inputFile throws Exception { final SamReader reader = SamReaderFactory.makeDefault().open(new File(TEST_DATA_DIR, inputFile)); final Histogram results = executeValidation(reader, null, IndexValidationStringency.EXHAUSTIVE); - Assert.assertNotNull(results.get(expectedError.getHistogramString())); - Assert.assertEquals(results.get(expectedError.getHistogramString()).getValue(), 1.0); + Assert.assertNotNull(results.get(expectedError.getHistogramString()), scenario); + Assert.assertEquals(results.get(expectedError.getHistogramString()).getValue(), 1.0, scenario); } - @DataProvider(name = "testMateCigarScenarios") public Object[][] testMateCigarScenarios() { return new Object[][]{ @@ -318,8 +320,8 @@ public void testTruncated(final String scenario, final String inputFile, final S throws Exception { final SamReader reader = SamReaderFactory.makeDefault().validationStringency(ValidationStringency.SILENT).open(new File(TEST_DATA_DIR, inputFile)); final Histogram results = executeValidation(reader, null, IndexValidationStringency.EXHAUSTIVE); - Assert.assertNotNull(results.get(expectedError.getHistogramString())); - Assert.assertEquals(results.get(expectedError.getHistogramString()).getValue(), 1.0); + Assert.assertNotNull(results.get(expectedError.getHistogramString()), scenario); + Assert.assertEquals(results.get(expectedError.getHistogramString()).getValue(), 1.0, scenario); } @DataProvider(name = "testTruncatedScenarios") @@ -400,9 +402,20 @@ public void testRedundantTags() throws Exception { public void testHeaderValidation() throws Exception { final SamReader samReader = SamReaderFactory.makeDefault().validationStringency(ValidationStringency.SILENT) .open(new File(TEST_DATA_DIR, "buggyHeader.sam")); - final Histogram results = executeValidation(samReader, null, IndexValidationStringency.EXHAUSTIVE); + final File referenceFile = new File(TEST_DATA_DIR, "../hg19mini.fasta"); + final ReferenceSequenceFile reference = new FastaSequenceFile(referenceFile, false); + final Histogram results = executeValidation(samReader, reference, IndexValidationStringency.EXHAUSTIVE); Assert.assertEquals(results.get(SAMValidationError.Type.UNRECOGNIZED_HEADER_TYPE.getHistogramString()).getValue(), 3.0); Assert.assertEquals(results.get(SAMValidationError.Type.HEADER_TAG_MULTIPLY_DEFINED.getHistogramString()).getValue(), 1.0); + Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_FILE_SEQ_DICT.getHistogramString()).getValue(), 1.0); + } + + @Test + public void testSeqQualMismatch() throws Exception { + final SamReader samReader = SamReaderFactory.makeDefault().validationStringency(ValidationStringency.SILENT) + .open(new File(TEST_DATA_DIR, "seq_qual_len_mismatch.sam")); + final Histogram results = executeValidation(samReader, null, IndexValidationStringency.EXHAUSTIVE); + Assert.assertEquals(results.get(SAMValidationError.Type.MISMATCH_SEQ_QUAL_LENGTH.getHistogramString()).getValue(), 8.0); } @Test diff --git a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java index a33766762..73859a46a 100644 --- a/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java +++ b/src/test/java/htsjdk/samtools/cram/lossy/QualityScorePreservationTest.java @@ -119,7 +119,7 @@ private SAMRecord buildSAMRecord(String seqName, String line) { @Test public void test3() { - String line1 = "98573 0 20 1 10 40M * 0 0 AAAAAAAAAA !!!!!!!!!!"; + String line1 = "98573 0 20 1 10 10M * 0 0 AAAAAAAAAA !!!!!!!!!!"; String seqName = "20"; byte[] ref = new byte[40]; diff --git a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java index e57b8fd08..6a115db9b 100644 --- a/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java +++ b/src/test/java/htsjdk/samtools/util/SequenceUtilTest.java @@ -262,11 +262,11 @@ public void testCountInsertedAndDeletedBases(final String cigarString, final int @Test(dataProvider = "testKmerGenerationTestCases") public void testKmerGeneration(final int length, final String[] expectedKmers) { - final Set actualSet = new HashSet(); + final Set actualSet = new HashSet<>(); for (final byte[] kmer : SequenceUtil.generateAllKmers(length)) { actualSet.add(StringUtil.bytesToString(kmer)); } - final Set expectedSet = new HashSet(Arrays.asList(expectedKmers)); + final Set expectedSet = new HashSet<>(Arrays.asList(expectedKmers)); Assert.assertTrue(actualSet.equals(expectedSet)); } diff --git a/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam index 82efe858e..335d8159c 100644 --- a/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam +++ b/src/test/resources/htsjdk/samtools/SequenceUtil/upper_and_lowercase_read.sam @@ -7,4 +7,4 @@ read1 0 chr1 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:0 read2 0 chr1 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:0 read3 0 chr2 1 0 16M * 0 0 AcGtAcGTaCGtAcGt AAAAAAAAAAAAAAAA NM:i:8 MD:Z:0T2A0T2A0t2a0t2a0 read4 0 chr2 1 0 8M * 0 0 TCGATCGA AAAAAAAA NM:i:0 -read5 0 chr2 1 0 4M1D2M1S * 0 0 TCGACGAA AAAAAAAA NM:i:1 MD:Z:4^T2 +read5 0 chr2 1 0 4M1D2M2S * 0 0 TCGACGAA AAAAAAAA NM:i:1 MD:Z:4^T2 diff --git a/src/test/resources/htsjdk/samtools/ValidateSamFileTest/seq_qual_len_mismatch.sam b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/seq_qual_len_mismatch.sam new file mode 100644 index 000000000..3c689b135 --- /dev/null +++ b/src/test/resources/htsjdk/samtools/ValidateSamFileTest/seq_qual_len_mismatch.sam @@ -0,0 +1,21 @@ +@HD VN:1.0 SO:coordinate +@SQ SN:chr1 LN:101 +@SQ SN:chr2 LN:101 +@SQ SN:chr3 LN:101 +@SQ SN:chr4 LN:101 +@SQ SN:chr5 LN:101 +@SQ SN:chr6 LN:101 +@SQ SN:chr7 LN:404 +@SQ SN:chr8 LN:202 +@RG ID:0 SM:Hi,Mom! LB:my-library PL:ILLUMINA +@RG ID:1 SM:Hi,Mom! LB:my-library PL:ILLUMINA +@RG ID:2 SM:Hi,Mom! LB:my-library PL:Illumina +@PG ID:1 PN:Hey! VN:2.0 +both_reads_align_clip_marked 1107 chr7 1 255 101M = 302 201 CAACAGAAGCNGGNATCTGTGTTTGTGTTTCGGATTTCCTGCTGAANNGNTTNTCGNNTCNNNNNNNNATCCCGATTTCNTTCCGCAGCTNACCTCCCAAN )'.*.+2,))&&'&*/)-&*-)&.-)&)&),/-&&..)./.,.).*&&,&.&&-)&&&0*&&&&&&&&/32/,01460&&/6/*0*/2/283//36868/ RG:Z:0 PG:Z:1 NM:i:0 MQ:i:255 XT:Z:foo OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +both_reads_present_only_first_aligns 89 chr7 1 255 101M * 0 0 CAACAGAAGCNGGNATCTGTGTTTGTGTTTCGGATTTCCTGCTGAANNGNTTNTCGNNTCNNNNNNNNATCCCGATTTCNTTCCGCAGCTNACCTCCCAAN )'.*.+2,))&&'&*/)-&*-)&.-)&)&),/-&&..)./.,.).*&&,&.&&-)&&&0*&&&&&&&&/32/,01460&&/6/*0*/2/283//36868/ RG:Z:1 PG:Z:1 NM:i:3 MQ:i:255 XT:Z:foo OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +read_2_too_many_gaps 83 chr7 1 255 101M = 302 201 CAACAGAAGCNGGNATCTGTGTTTGTGTTTCGGATTTCCTGCTGAANNGNTTNTCGNNTCNNNNNNNNATCCCGATTTCNTTCCGCAGCTNACCTCCCAAN )'.*.+2,))&&'&*/)-&*-)&.-)&)&),/-&&..)./.,.).*&&,&.&&-)&&&0*&&&&&&&&/32/,01460&&/6/*0*/2/283//36868/ RG:Z:2 PG:Z:1 NM:i:8 MQ:i:255 XT:Z:foo2 OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +both_reads_align_clip_adapter 147 chr7 16 255 101M = 21 -96 CAACAGAAGCNGGNATCTGTGTTTGTGTTTCGGATTTCCTGCTGAANNGNTTNTCGNNTCNNNNNNNNATCCCGATTTCNTTCCGCAGCTNACCTCCCAAN )'.*.+2,))&&'&*/)-&*-)&.-)&)&),/-&&..)./.,.).*&&,&.&&-)&&&0*&&&&&&&&/32/,01460&&/6/*0*/2/283//36868/ RG:Z:1 PG:Z:1 NM:i:1 MQ:i:255 XT:Z:foo2 OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +both_reads_align_clip_adapter 99 chr7 21 255 101M = 16 96 CAACAGAAGCNGGNATCTGTGTTTGTGTTTCGGATTTCCTGCTGAANNGNTTNTCGNNTCNNNNNNNNATCCCGATTTCNTTCCGCAGCTNACCTCCCAAN )'.*.+2,))&&'&*/)-&*-)&.-)&)&),/-&&..)./.,.).*&&,&.&&-)&&&0*&&&&&&&&/32/,01460&&/6/*0*/2/283//36868/ RG:Z:1 PG:Z:1 NM:i:1 MQ:i:255 XT:Z:foo2 OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +both_reads_align_clip_marked 163 chr7 302 255 101M = 1 -201 NCGCGGCATCNCGATTTCTTTCCGCAGCTAACCTCCCGACAGATCGGCAGCGCGTCGTGTAGGTTATTATGGTACATCTTGTCGTGCGGCNAGAGCATACA &/15445666651/566666553+2/14/&/555512+3/)-'/-&-'*+))*''13+3)'//++''/'))/3+&*5++)&'2+&+/*&-&&*)&-./1' RG:Z:0 PG:Z:1 NM:i:5 MQ:i:255 OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +read_2_too_many_gaps 163 chr7 302 255 10M1D10M5I76M = 1 -201 NCGCGGCATCNCGATTTCTTTCCGCAGCTAACCTCCCGACAGATCGGCAGCGCGTCGTGTAGGTTATTATGGTACATCTTGTCGTGCGGCNAGAGCATACA &/15445666651/566666553+2/14/&/555512+3/)-'/-&-'*+))*''13+3)'//++''/'))/3+&*5++)&'2+&+/*&-&&*)&-./1' RG:Z:2 PG:Z:1 NM:i:6 MQ:i:255 OQ:Z:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +both_reads_present_only_first_aligns 165 * 0 0 * chr7 1 0 NCGCGGCATCNCGATTTCTTTCCGCAGCTAACCTCCCGACAGATCGGCAGCGCGTCGTGTAGGTTATTATGGTACATCTTGTCGTGCGGCNAGAGCATACA &/15445666651/566666553+2/14/&/555512+3/)-'/-&-'*+))*''13+3)'//++''/'))/3+&*5++)&'2+&+/*&-&&*)&-./1' RG:Z:1 PG:Z:1 From 7d31bc2a5c4f7ad79de7fa804898289d6fd556de Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Mon, 26 Jun 2017 16:14:08 -0400 Subject: [PATCH 126/137] Adding the gitter badge. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1981fd182..c2af30592 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.samtools/htsjdk/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.samtools%22%20AND%20a%3A%22htsjdk%22) [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/samtools/htsjdk) [![Language](http://img.shields.io/badge/language-java-brightgreen.svg)](https://www.java.com/) +[![Join the chat at https://gitter.im/samtools/htsjdk](https://badges.gitter.im/samtools/htsjdk.svg)](https://gitter.im/samtools/htsjdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Status of downstream projects automatically built on top of the current htsjdk master branch. See [gatk-jenkins](https://gatk-jenkins.broadinstitute.org/view/HTSJDK%20Release%20Tests/) for detailed logs. Failure may indicate problems in htsjdk, but may also be due to expected incompatibilities between versions, or unrelated failures in downstream projects. - [Picard](https://github.com/broadinstitute/picard): [![Build Status](https://gatk-jenkins.broadinstitute.org/buildStatus/icon?job=picard-on-htsjdk-master)](https://gatk-jenkins.broadinstitute.org/job/picard-on-htsjdk-master/) From 1cd23dacecd6b837a54ba801d4f08a48006d3215 Mon Sep 17 00:00:00 2001 From: Ron Levine Date: Wed, 5 Jul 2017 19:51:35 -0400 Subject: [PATCH 127/137] Make stream seek error message informative (#927) --- .../samtools/util/BlockCompressedInputStream.java | 17 +++++++++++++++-- .../util/BlockCompressedOutputStreamTest.java | 20 +++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java index e108d1bb3..622ca67ac 100755 --- a/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java +++ b/src/main/java/htsjdk/samtools/util/BlockCompressedInputStream.java @@ -57,10 +57,12 @@ public final static String INCORRECT_HEADER_SIZE_MSG = "Incorrect header size for file: "; public final static String UNEXPECTED_BLOCK_LENGTH_MSG = "Unexpected compressed block length: "; public final static String PREMATURE_END_MSG = "Premature end of file: "; - public final static String CANNOT_SEEK_STREAM_MSG = "Cannot seek on stream based file "; + public final static String CANNOT_SEEK_STREAM_MSG = "Cannot seek a position for a non-file stream"; + public final static String CANNOT_SEEK_CLOSED_STREAM_MSG = "Cannot seek a position for a closed stream"; public final static String INVALID_FILE_PTR_MSG = "Invalid file pointer: "; private InputStream mStream = null; + private boolean mIsClosed = false; private SeekableStream mFile = null; private byte[] mFileBuffer = null; private DecompressedBlock mCurrentBlock = null; @@ -222,6 +224,9 @@ public void close() throws IOException { // Encourage garbage collection mFileBuffer = null; mCurrentBlock = null; + + // Mark as closed + mIsClosed = true; } /** @@ -344,12 +349,20 @@ public int read(final byte[] buffer, int offset, int length) throws IOException * Seek to the given position in the file. Note that pos is a special virtual file pointer, * not an actual byte offset. * - * @param pos virtual file pointer + * @param pos virtual file pointer position + * @throws IOException if stream is closed or not a file based stream */ public void seek(final long pos) throws IOException { + // Must be before the mFile == null check because mFile == null for closed files and streams + if (mIsClosed) { + throw new IOException(CANNOT_SEEK_CLOSED_STREAM_MSG); + } + + // Cannot seek on streams that are not file based if (mFile == null) { throw new IOException(CANNOT_SEEK_STREAM_MSG); } + // Decode virtual file pointer // Upper 48 bits is the byte offset into the compressed stream of a // block. diff --git a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java index a678c8dca..35175cd1d 100644 --- a/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java +++ b/src/test/java/htsjdk/samtools/util/BlockCompressedOutputStreamTest.java @@ -81,6 +81,7 @@ public void testBasic() throws Exception { Assert.assertEquals(bcis2.read(buffer), available, "Should read to end of block"); Assert.assertTrue(bcis2.endOfBlock(), "Should be at end of block"); bcis2.close(); + Assert.assertEquals(bcis2.read(buffer), -1, "Should be end of file"); } @DataProvider(name = "seekReadExceptionsData") @@ -89,24 +90,32 @@ public void testBasic() throws Exception { return new Object[][]{ {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.gz", FileTruncatedException.class, BlockCompressedInputStream.PREMATURE_END_MSG + System.getProperty("user.dir") + "/" + - HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.gz", true, false, 0}, + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.gz", true, false, false, 0}, {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.hdr.gz", IOException.class, BlockCompressedInputStream.INCORRECT_HEADER_SIZE_MSG + System.getProperty("user.dir") + "/" + - HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.hdr.gz", true, false, 0}, + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.truncated.hdr.gz", true, false, false, 0}, {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", IOException.class, - BlockCompressedInputStream.CANNOT_SEEK_STREAM_MSG, false, true, 0}, + BlockCompressedInputStream.CANNOT_SEEK_STREAM_MSG, false, true, false, 0}, + {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", IOException.class, + BlockCompressedInputStream.CANNOT_SEEK_CLOSED_STREAM_MSG, false, true, true, 0}, {HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", IOException.class, BlockCompressedInputStream.INVALID_FILE_PTR_MSG + 1000 + " for " + System.getProperty("user.dir") + "/" + - HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", true, true, 1000 } + HTSJDK_TRIBBLE_RESOURCES + "vcfexample.vcf.gz", true, true, false, 1000 } }; } @Test(dataProvider = "seekReadExceptionsData") - public void testSeekReadExceptions(final String filePath, final Class c, final String msg, final boolean isFile, final boolean isSeek, final int pos) throws Exception { + public void testSeekReadExceptions(final String filePath, final Class c, final String msg, final boolean isFile, final boolean isSeek, final boolean isClosed, + final int pos) throws Exception { final BlockCompressedInputStream bcis = isFile ? new BlockCompressedInputStream(new File(filePath)) : new BlockCompressedInputStream(new FileInputStream(filePath)); + + if ( isClosed ) { + bcis.close(); + } + boolean haveException = false; try { if ( isSeek ) { @@ -212,5 +221,6 @@ public Deflater makeDeflater(final int compressionLevel, final boolean gzipCompa } bcis.close(); Assert.assertEquals(deflateCalls[0], 3, "deflate calls"); + Assert.assertEquals(reader.readLine(), null); } } From 8d207ae878f9f5136cb6e6ae628659eddef047d6 Mon Sep 17 00:00:00 2001 From: Tim Fennell Date: Wed, 12 Jul 2017 08:14:04 -0400 Subject: [PATCH 128/137] Updates to readme about documentation, communication & license. (#916) --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c2af30592..afe901e05 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,19 @@ common file formats, such as [SAM][1] and [VCF][2], used for high-throughput sequencing data. There are also an number of useful utilities for manipulating HTS data. -Please see the [HTSJDK Documentation](http://samtools.github.io/htsjdk) for more information. - > **NOTE: _HTSJDK does not currently support the latest Variant Call Format Specification (VCFv4.3 and BCFv2.2)._** -#### Building HTSJDK +### Documentation & Getting Help + +API documentation for all versions of HTSJDK since `1.128` are available through [javadoc.io](http://www.javadoc.io/doc/com.github.samtools/htsjdk). + +If you believe you have found a bug or have an issue with the library please a) search the open and recently closed issues to ensure it has not already been reported, then b) log an issue. + +The project has a [gitter chat room](https://gitter.im/samtools/htsjdk) if you would like to chat with the developers and others involved in the project. + +To receive announcements of releases and other significant project news please subscribe to the [htsjdk-announce](https://groups.google.com/forum/#!forum/htsjdk-announce) google group. + +### Building HTSJDK HTSJDK is now built using [gradle](http://gradle.org/). @@ -74,7 +82,7 @@ Example gradle usage from the htsjdk root directory: ./gradlew tasks ``` -#### Create an HTSJDK project in IntelliJ +### Create an HTSJDK project in IntelliJ To create a project in IntelliJ IDE for htsjdk do the following: 1. Select fom the menu: `File -> New -> Project from Existing Sources` @@ -83,13 +91,17 @@ To create a project in IntelliJ IDE for htsjdk do the following: From time to time if dependencies change in htsjdk you may need to refresh the project from the `View -> Gradle` menu. -#### Licensing Information +### Licensing Information -Not all sub-packages of htsjdk are subject to the same license, so a license notice is included in each source file or sub-package as appropriate. Please check the relevant license notice whenever you start working with a part of htsjdk that you have not previously worked with to avoid any surprises. +Not all sub-packages of htsjdk are subject to the same license, so a license notice is included in each source file or sub-package as appropriate. +Please check the relevant license notice whenever you start working with a part of htsjdk that you have not previously worked with to avoid any surprises. +Broadly speaking the majority of the code is covered under the MIT license with the following notable exceptions: -#### Java Minimum Version Support Policy +* Much of the CRAM code is under the Apache License, Version 2 +* Core `tribble` code (underlying VCF reading/writing amongst other things) is under LGPL +* Code supporting the reading/writing of SRA format is uncopyrighted & public domain -> **NOTE: _Effective November 24th 2015, HTSJDK has ended support of Java 7 and previous versions. Java 8 is now required_.** +### Java Minimum Version Support Policy We will support all Java SE versions supported by Oracle until at least six months after Oracle's Public Updates period has ended ([see this link](http://www.oracle.com/technetwork/java/eol-135779.html)). @@ -97,9 +109,8 @@ Java SE Major Release | End of Java SE Oracle Public Updates | Proposed End of S ---- | ---- | ---- | ---- 6 | Feb 2013 | Aug 2013 | Oct 2015 7 | Apr 2015 | Oct 2015 | Oct 2015 -8* | Mar 2017 | Sep 2017 | Sep 2017 +8 | Jul 2018 | Jul 2018 | TBD -* to be finalized HTSJDK is migrating to semantic versioning (http://semver.org/). We will eventually adhere to it strictly and bump our major version whenever there are breaking changes to our API, but until we more clearly define what constitutes our official API, clients should assume that every release potentially contains at least minor changes to public methods. From 624a3bf314575769a9e50c04d43bd7a41fd8a81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Wed, 19 Jul 2017 16:41:02 +0200 Subject: [PATCH 129/137] Add missing HtsjdkTest marker to test classes (#936) Some tests were being skipped because they weren't marked as HtsjdkTest --- src/test/java/htsjdk/samtools/BAMFileSpanTest.java | 4 +++- src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java | 3 ++- src/test/java/htsjdk/samtools/PathInputResourceTest.java | 4 +++- src/test/java/htsjdk/samtools/SAMProgramRecordTest.java | 3 ++- src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java | 3 ++- src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java | 3 ++- src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java | 3 ++- src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java | 3 ++- 8 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/test/java/htsjdk/samtools/BAMFileSpanTest.java b/src/test/java/htsjdk/samtools/BAMFileSpanTest.java index 4fc39b294..06d1bc9ab 100644 --- a/src/test/java/htsjdk/samtools/BAMFileSpanTest.java +++ b/src/test/java/htsjdk/samtools/BAMFileSpanTest.java @@ -1,11 +1,13 @@ package htsjdk.samtools; import java.util.Arrays; + +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class BAMFileSpanTest { +public class BAMFileSpanTest extends HtsjdkTest { @Test(dataProvider = "testRemoveContentsBeforeProvider") public void testRemoveContentsBefore(BAMFileSpan originalSpan, BAMFileSpan cutoff, BAMFileSpan expectedSpan) { diff --git a/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java b/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java index 5943535af..d86b697a5 100644 --- a/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java +++ b/src/test/java/htsjdk/samtools/DuplicateScoringStrategyTest.java @@ -1,10 +1,11 @@ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -public class DuplicateScoringStrategyTest { +public class DuplicateScoringStrategyTest extends HtsjdkTest { @DataProvider public Object [][] compareData() { diff --git a/src/test/java/htsjdk/samtools/PathInputResourceTest.java b/src/test/java/htsjdk/samtools/PathInputResourceTest.java index 1bc2aa161..f82b9a627 100644 --- a/src/test/java/htsjdk/samtools/PathInputResourceTest.java +++ b/src/test/java/htsjdk/samtools/PathInputResourceTest.java @@ -5,10 +5,12 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.function.Function; + +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; -public class PathInputResourceTest { +public class PathInputResourceTest extends HtsjdkTest { final String localBam = "src/test/resources/htsjdk/samtools/BAMFileIndexTest/index_test.bam"; @Test diff --git a/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java b/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java index 5d9a36893..99a26cc38 100644 --- a/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java +++ b/src/test/java/htsjdk/samtools/SAMProgramRecordTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Test for SAMReadGroupRecordTest */ -public class SAMProgramRecordTest { +public class SAMProgramRecordTest extends HtsjdkTest { @Test public void testGetSAMString() { diff --git a/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java b/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java index c3a7423d3..0801f52a5 100644 --- a/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java +++ b/src/test/java/htsjdk/samtools/SAMReadGroupRecordTest.java @@ -23,13 +23,14 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Test for SAMReadGroupRecordTest */ -public class SAMReadGroupRecordTest { +public class SAMReadGroupRecordTest extends HtsjdkTest { @Test public void testGetSAMString() { diff --git a/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java index e0c73d502..89e6121d2 100644 --- a/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java +++ b/src/test/java/htsjdk/samtools/SAMSequenceRecordTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools; +import htsjdk.HtsjdkTest; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -32,7 +33,7 @@ /** * Test for SAMReadGroupRecordTest */ -public class SAMSequenceRecordTest { +public class SAMSequenceRecordTest extends HtsjdkTest { @Test public void testGetSAMString() { diff --git a/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java b/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java index 72e59cff7..c367397a3 100644 --- a/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java +++ b/src/test/java/htsjdk/samtools/fastq/FastqEncoderTest.java @@ -23,6 +23,7 @@ */ package htsjdk.samtools.fastq; +import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordSetBuilder; import org.testng.Assert; @@ -31,7 +32,7 @@ /** * @author Daniel Gomez-Sanchez (magicDGS) */ -public class FastqEncoderTest { +public class FastqEncoderTest extends HtsjdkTest { @Test public void testAsFastqRecord() throws Exception { diff --git a/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java b/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java index 54cfdedfc..d9b3bf84a 100644 --- a/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java +++ b/src/test/java/htsjdk/samtools/util/CigarElementUnitTest.java @@ -1,11 +1,12 @@ package htsjdk.samtools.util; +import htsjdk.HtsjdkTest; import htsjdk.samtools.CigarElement; import htsjdk.samtools.CigarOperator; import org.testng.annotations.Test; -public class CigarElementUnitTest { +public class CigarElementUnitTest extends HtsjdkTest { @Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeLengthCheck(){ From cfbbe4b8e0e5ad4f011d6aacec2640eab8ff49a0 Mon Sep 17 00:00:00 2001 From: Vadim Zalunin Date: Thu, 20 Jul 2017 13:21:07 +0100 Subject: [PATCH 130/137] fix for ambiguity and lc match/mismatch bases in CRAM (#889) --- .../htsjdk/samtools/CRAMContainerStreamWriter.java | 9 +- .../htsjdk/samtools/cram/build/CramNormalizer.java | 11 +- .../samtools/cram/build/Sam2CramRecordFactory.java | 136 +++++++-------------- .../cram/encoding/readfeatures/Substitution.java | 15 ++- .../htsjdk/samtools/cram/ref/ReferenceSource.java | 9 +- .../java/htsjdk/samtools/util/SequenceUtil.java | 54 +++++++- .../java/htsjdk/samtools/BAMFileWriterTest.java | 39 ++++++ .../java/htsjdk/samtools/CRAMComplianceTest.java | 13 +- .../java/htsjdk/samtools/CRAMSliceMD5Test.java | 136 +++++++++++++++++++++ .../cram/build/Sam2CramRecordFactoryTest.java | 109 +++++++++++++++++ .../samtools/cram/ref/ReferenceSourceTest.java | 33 +++++ .../htsjdk/samtools/util/SequenceUtilTest.java | 57 +++++++++ .../htsjdk/samtools/cram/amb#amb.2.1.cram | Bin 0 -> 11045 bytes .../htsjdk/samtools/cram/amb#amb.3.0.cram | Bin 0 -> 1210 bytes .../resources/htsjdk/samtools/cram/amb#amb.sam | 57 +++++++++ src/test/resources/htsjdk/samtools/cram/amb.fa | 2 + src/test/resources/htsjdk/samtools/cram/amb.fa.fai | 1 + .../htsjdk/samtools/cram/ambiguityCodes.fasta | 2 + .../htsjdk/samtools/cram/ambiguityCodes.fasta.fai | 1 + .../samtoolsSliceMD5WithAmbiguityCodesTest.cram | Bin 0 -> 1826 bytes 20 files changed, 577 insertions(+), 107 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/CRAMSliceMD5Test.java create mode 100644 src/test/java/htsjdk/samtools/cram/build/Sam2CramRecordFactoryTest.java create mode 100644 src/test/java/htsjdk/samtools/cram/ref/ReferenceSourceTest.java create mode 100644 src/test/resources/htsjdk/samtools/cram/amb#amb.2.1.cram create mode 100644 src/test/resources/htsjdk/samtools/cram/amb#amb.3.0.cram create mode 100644 src/test/resources/htsjdk/samtools/cram/amb#amb.sam create mode 100644 src/test/resources/htsjdk/samtools/cram/amb.fa create mode 100644 src/test/resources/htsjdk/samtools/cram/amb.fa.fai create mode 100644 src/test/resources/htsjdk/samtools/cram/ambiguityCodes.fasta create mode 100644 src/test/resources/htsjdk/samtools/cram/ambiguityCodes.fasta.fai create mode 100644 src/test/resources/htsjdk/samtools/cram/samtoolsSliceMD5WithAmbiguityCodesTest.cram diff --git a/src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java b/src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java index 4707b7bcc..c588bdb46 100644 --- a/src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java +++ b/src/main/java/htsjdk/samtools/CRAMContainerStreamWriter.java @@ -17,6 +17,7 @@ import htsjdk.samtools.cram.structure.Slice; import htsjdk.samtools.util.Log; import htsjdk.samtools.util.RuntimeIOException; +import htsjdk.samtools.util.SequenceUtil; import java.io.IOException; import java.io.OutputStream; @@ -437,7 +438,13 @@ protected void flushContainer() throws IllegalArgumentException, IllegalAccessEx final SAMRecord restoredSamRecord = f.create(cramRecords.get(i)); assert (restoredSamRecord.getAlignmentStart() == samRecords.get(i).getAlignmentStart()); assert (restoredSamRecord.getReferenceName().equals(samRecords.get(i).getReferenceName())); - assert (restoredSamRecord.getReadString().equals(samRecords.get(i).getReadString())); + + if (!restoredSamRecord.getReadString().equals(samRecords.get(i).getReadString())) { + // try to fix the original read bases by normalizing them to BAM set: + final byte[] originalReadBases = samRecords.get(i).getReadString().getBytes(); + final String originalReadBasesUpperCaseIupacNoDot = new String(SequenceUtil.toBamReadBasesInPlace(originalReadBases)); + assert (restoredSamRecord.getReadString().equals(originalReadBasesUpperCaseIupacNoDot)); + } assert (restoredSamRecord.getBaseQualityString().equals(samRecords.get(i).getBaseQualityString())); } } diff --git a/src/main/java/htsjdk/samtools/cram/build/CramNormalizer.java b/src/main/java/htsjdk/samtools/cram/build/CramNormalizer.java index 1be1aa546..b2dd67c44 100644 --- a/src/main/java/htsjdk/samtools/cram/build/CramNormalizer.java +++ b/src/main/java/htsjdk/samtools/cram/build/CramNormalizer.java @@ -32,6 +32,7 @@ import htsjdk.samtools.cram.structure.CramCompressionRecord; import htsjdk.samtools.cram.structure.SubstitutionMatrix; import htsjdk.samtools.util.Log; +import htsjdk.samtools.util.SequenceUtil; import java.util.ArrayList; import java.util.Arrays; @@ -242,7 +243,8 @@ public static void restoreQualityScores(final byte defaultQualityScore, } else System.arraycopy(ref, alignmentStart - refOffsetZeroBased, bases, 0, bases.length); - return bases; + + return SequenceUtil.toBamReadBasesInPlace(bases); } final List variations = record.readFeatures; for (final ReadFeature variation : variations) { @@ -256,6 +258,7 @@ public static void restoreQualityScores(final byte defaultQualityScore, final Substitution substitution = (Substitution) variation; byte refBase = getByteOrDefault(ref, alignmentStart + posInSeq - refOffsetZeroBased, (byte) 'N'); + // substitution requires ACGTN only: refBase = Utils.normalizeBase(refBase); final byte base = substitutionMatrix.base(refBase, substitution.getCode()); substitution.setBase(base); @@ -304,11 +307,7 @@ public static void restoreQualityScores(final byte defaultQualityScore, } } - for (int i = 0; i < bases.length; i++) { - bases[i] = Utils.normalizeBase(bases[i]); - } - - return bases; + return SequenceUtil.toBamReadBasesInPlace(bases); } private static byte getByteOrDefault(final byte[] array, final int pos, diff --git a/src/main/java/htsjdk/samtools/cram/build/Sam2CramRecordFactory.java b/src/main/java/htsjdk/samtools/cram/build/Sam2CramRecordFactory.java index b7ffcb13f..6c59e13fd 100644 --- a/src/main/java/htsjdk/samtools/cram/build/Sam2CramRecordFactory.java +++ b/src/main/java/htsjdk/samtools/cram/build/Sam2CramRecordFactory.java @@ -17,50 +17,21 @@ */ package htsjdk.samtools.cram.build; -import htsjdk.samtools.CigarElement; -import htsjdk.samtools.CigarOperator; -import htsjdk.samtools.SAMFileHeader; -import htsjdk.samtools.SAMReadGroupRecord; -import htsjdk.samtools.SAMRecord; +import htsjdk.samtools.*; import htsjdk.samtools.SAMRecord.SAMTagAndValue; -import htsjdk.samtools.SAMTag; import htsjdk.samtools.cram.common.CramVersions; import htsjdk.samtools.cram.common.Version; -import htsjdk.samtools.cram.encoding.readfeatures.BaseQualityScore; -import htsjdk.samtools.cram.encoding.readfeatures.Deletion; -import htsjdk.samtools.cram.encoding.readfeatures.HardClip; -import htsjdk.samtools.cram.encoding.readfeatures.InsertBase; -import htsjdk.samtools.cram.encoding.readfeatures.Padding; -import htsjdk.samtools.cram.encoding.readfeatures.ReadFeature; -import htsjdk.samtools.cram.encoding.readfeatures.RefSkip; -import htsjdk.samtools.cram.encoding.readfeatures.SoftClip; -import htsjdk.samtools.cram.encoding.readfeatures.Substitution; +import htsjdk.samtools.cram.encoding.readfeatures.*; import htsjdk.samtools.cram.structure.CramCompressionRecord; import htsjdk.samtools.cram.structure.ReadTag; import htsjdk.samtools.util.Log; +import htsjdk.samtools.util.SequenceUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; public class Sam2CramRecordFactory { - - public static final String UNKNOWN_READ_GROUP_ID = "UNKNOWN"; - public static final String UNKNOWN_READ_GROUP_SAMPLE = "UNKNOWN"; - - private final static byte QS_asciiOffset = 33; - public final static byte unsetQualityScore = 32; - public final static byte ignorePositionsWithQualityScore = -1; - private byte[] refBases; private final Version version; - private byte[] refSNPs; final private SAMFileHeader header; @@ -68,9 +39,6 @@ private final Map readGroupMap = new HashMap(); - private long landedRefMaskScores = 0; - private long landedTotalScores = 0; - public boolean captureAllTags = false; public boolean preserveReadNames = false; public final Set captureTags = new TreeSet(); @@ -151,8 +119,14 @@ public CramCompressionRecord createCramRecord(final SAMRecord record) { } else cramRecord.readFeatures = Collections.emptyList(); cramRecord.readBases = record.getReadBases(); + + /** + * CRAM read bases are limited to ACGTN, see https://github.com/samtools/hts-specs/blob/master/CRAMv3.pdf passage 10.2 on read bases. + * However, BAM format allows upper case IUPAC codes without a dot, so we follow the same approach to reproduce the behaviour of samtools. + */ + // copy read bases to avoid changing the original record: + cramRecord.readBases = SequenceUtil.toBamReadBasesInPlace(Arrays.copyOf(record.getReadBases(), record.getReadLength())); cramRecord.qualityScores = record.getBaseQualities(); - landedTotalScores += cramRecord.readLength; if (version.compatibleWith(CramVersions.CRAM_v3)) cramRecord.setUnknownBases(record.getReadBases() == SAMRecord.NULL_SEQUENCE); @@ -187,7 +161,7 @@ public CramCompressionRecord createCramRecord(final SAMRecord record) { * A wrapper method to provide better diagnostics for ArrayIndexOutOfBoundsException. * * @param cramRecord CRAM record - * @param samRecord SAM record + * @param samRecord SAM record * @return a list of read features created for the given {@link htsjdk.samtools.SAMRecord} */ private List checkedCreateVariations(final CramCompressionRecord cramRecord, final SAMRecord samRecord) { @@ -247,7 +221,7 @@ public CramCompressionRecord createCramRecord(final SAMRecord record) { case M: case X: case EQ: - addSubstitutionsAndMaskedBases(cramRecord, features, zeroBasedPositionInRead, alignmentStartOffset, + addMismatchReadFeatures(cramRecord.alignmentStart, features, zeroBasedPositionInRead, alignmentStartOffset, cigarElementLength, bases, qualityScore); break; default: @@ -291,57 +265,47 @@ private void addInsertion(final List features, final int zeroBasedP } } - private void addSubstitutionsAndMaskedBases(final CramCompressionRecord cramRecord, final List features, final int fromPosInRead, final int + /** + * Processes a stretch of read bases marked as match or mismatch and emits appropriate read features. + * Briefly the algorithm is: + *

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