Skip to content

Commit

Permalink
Merge pull request #239 from samtools/dr_make_variant_headers_seriali…
Browse files Browse the repository at this point in the history
…zable

Make VCFHeader Serializable
  • Loading branch information
lbergelson committed May 7, 2015
2 parents c8d5c53 + e72d53d commit 6b893e4
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 21 deletions.
30 changes: 24 additions & 6 deletions src/java/htsjdk/variant/vcf/VCFCompoundHeaderLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,34 @@ protected String toStringEncoding() {
}

/**
* returns true if we're equal to another compounder header line
* returns true if we're equal to another compound header line
* @param o a compound header line
* @return true if equal
*/
public boolean equals(Object o) {
if (!(o instanceof VCFCompoundHeaderLine))
@Override
public boolean equals(final Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() || ! super.equals(o) ) {
return false;
VCFCompoundHeaderLine other = (VCFCompoundHeaderLine) o;
return equalsExcludingDescription(other) &&
description.equals(other.description);
}

final VCFCompoundHeaderLine that = (VCFCompoundHeaderLine) o;
return equalsExcludingDescription(that) &&
description.equals(that.description);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + count;
result = 31 * result + (countType != null ? countType.hashCode() : 0); // only nullable field according to validate()
result = 31 * result + description.hashCode();
result = 31 * result + type.hashCode();
result = 31 * result + lineType.hashCode();
return result;
}

public boolean equalsExcludingDescription(VCFCompoundHeaderLine other) {
Expand Down
22 changes: 22 additions & 0 deletions src/java/htsjdk/variant/vcf/VCFContigHeaderLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
/**
* A special class representing a contig VCF header line. Knows the true contig order and sorts on that
*
* Note: this class has a natural ordering that is inconsistent with equals()
*
* @author mdepristo
*/
public class VCFContigHeaderLine extends VCFSimpleHeaderLine {
Expand Down Expand Up @@ -82,6 +84,26 @@ public SAMSequenceRecord getSAMSequenceRecord() {
return record;
}

@Override
public boolean equals(final Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() || ! super.equals(o) ) {
return false;
}

final VCFContigHeaderLine that = (VCFContigHeaderLine) o;
return contigIndex.equals(that.contigIndex);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + contigIndex.hashCode();
return result;
}

/**
* IT IS CRITICAL THAT THIS BE OVERRIDDEN SO WE SORT THE CONTIGS IN THE CORRECT ORDER
*/
Expand Down
4 changes: 3 additions & 1 deletion src/java/htsjdk/variant/vcf/VCFHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import htsjdk.variant.utils.GeneralUtils;
import htsjdk.variant.variantcontext.VariantContextComparator;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -57,7 +58,8 @@
* <p/>
* A class representing the VCF header
*/
public class VCFHeader {
public class VCFHeader implements Serializable {
public static final long serialVersionUID = 1L;

// the mandatory header fields
public enum HEADER_FIELDS {
Expand Down
26 changes: 22 additions & 4 deletions src/java/htsjdk/variant/vcf/VCFHeaderLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import htsjdk.tribble.TribbleException;

import java.io.Serializable;
import java.util.Map;


Expand All @@ -37,7 +38,9 @@
* <p/>
* A class representing a key=value entry in the VCF header
*/
public class VCFHeaderLine implements Comparable {
public class VCFHeaderLine implements Comparable, Serializable {
public static final long serialVersionUID = 1L;

protected static final boolean ALLOW_UNBOUND_DESCRIPTIONS = true;
protected static final String UNBOUND_DESCRIPTION = "Not provided in original VCF header";

Expand Down Expand Up @@ -101,10 +104,25 @@ protected String toStringEncoding() {
return mKey + "=" + mValue;
}

public boolean equals(Object o) {
if ( !(o instanceof VCFHeaderLine) )
@Override
public boolean equals(final Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
return mKey.equals(((VCFHeaderLine)o).getKey()) && mValue.equals(((VCFHeaderLine)o).getValue());
}

final VCFHeaderLine that = (VCFHeaderLine) o;
return mKey.equals(that.mKey) && // key not nullable
(mValue != null ? mValue.equals(that.mValue) : that.mValue == null); // value is nullable
}

@Override
public int hashCode() {
int result = mKey.hashCode();
result = 31 * result + (mValue != null ? mValue.hashCode() : 0);
return result;
}

public int compareTo(Object other) {
Expand Down
28 changes: 18 additions & 10 deletions src/java/htsjdk/variant/vcf/VCFSimpleHeaderLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,26 @@ protected String toStringEncoding() {
return getKey() + "=" + VCFHeaderLine.toStringEncoding(map);
}

public boolean equals(Object o) {
if ( !(o instanceof VCFSimpleHeaderLine) )
return false;
VCFSimpleHeaderLine other = (VCFSimpleHeaderLine)o;
if ( !name.equals(other.name) || genericFields.size() != other.genericFields.size() )
@Override
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() || ! super.equals(o) ) {
return false;
for ( Map.Entry<String, String> entry : genericFields.entrySet() ) {
if ( !entry.getValue().equals(other.genericFields.get(entry.getKey())) )
return false;
}

return true;

final VCFSimpleHeaderLine that = (VCFSimpleHeaderLine) o;
return name.equals(that.name) &&
genericFields.equals(that.genericFields);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + genericFields.hashCode();
return result;
}

public String getID() {
Expand Down
34 changes: 34 additions & 0 deletions src/tests/java/htsjdk/variant/vcf/VCFHeaderUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.math.BigInteger;
Expand All @@ -51,6 +55,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
* Created by IntelliJ IDEA.
Expand Down Expand Up @@ -192,6 +197,35 @@ public void testVCFHeaderAddDuplicateHeaderLine() {
Assert.assertEquals(numHeaderLinesBefore, numHeaderLinesAfter);
}

@Test
public void testVCFHeaderSerialization() throws Exception {
final VCFFileReader reader = new VCFFileReader(new File("testdata/htsjdk/variant/HiSeq.10000.vcf"), false);
final VCFHeader originalHeader = reader.getFileHeader();
reader.close();

final ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
final ObjectOutputStream out = new ObjectOutputStream(byteArrayStream);
out.writeObject(originalHeader);
out.close();

final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteArrayStream.toByteArray()));
final VCFHeader deserializedHeader = (VCFHeader)in.readObject();
in.close();

Assert.assertEquals(deserializedHeader.getMetaDataInInputOrder(), originalHeader.getMetaDataInInputOrder(), "Header metadata does not match before/after serialization");
Assert.assertEquals(deserializedHeader.getContigLines(), originalHeader.getContigLines(), "Contig header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getFilterLines(), originalHeader.getFilterLines(), "Filter header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getFormatHeaderLines(), originalHeader.getFormatHeaderLines(), "Format header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getIDHeaderLines(), originalHeader.getIDHeaderLines(), "ID header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getInfoHeaderLines(), originalHeader.getInfoHeaderLines(), "Info header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getOtherHeaderLines(), originalHeader.getOtherHeaderLines(), "Other header lines do not match before/after serialization");
Assert.assertEquals(deserializedHeader.getGenotypeSamples(), originalHeader.getGenotypeSamples(), "Genotype samples not the same before/after serialization");
Assert.assertEquals(deserializedHeader.samplesWereAlreadySorted(), originalHeader.samplesWereAlreadySorted(), "Sortedness of samples not the same before/after serialization");
Assert.assertEquals(deserializedHeader.getSampleNamesInOrder(), originalHeader.getSampleNamesInOrder(), "Sorted list of sample names in header not the same before/after serialization");
Assert.assertEquals(deserializedHeader.getSampleNameToOffset(), originalHeader.getSampleNameToOffset(), "Sample name to offset map not the same before/after serialization");
Assert.assertEquals(deserializedHeader.toString(), originalHeader.toString(), "String representation of header not the same before/after serialization");
}

/**
* a little utility function for all tests to md5sum a file
* Shameless taken from:
Expand Down

0 comments on commit 6b893e4

Please sign in to comment.