/* * 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.variant.utils.GeneralUtils; import htsjdk.variant.vcf.VCFCompoundHeaderLine; import htsjdk.variant.vcf.VCFConstants; import htsjdk.variant.vcf.VCFFormatHeaderLine; import htsjdk.variant.vcf.VCFHeader; import htsjdk.variant.vcf.VCFHeaderLineType; import htsjdk.variant.vcf.VCFInfoHeaderLine; import java.util.HashMap; import java.util.Map; /** * See #BCFWriter for documentation on this classes role in encoding BCF2 files * * @author Mark DePristo * @since 06/12 */ public class BCF2FieldWriterManager { final Map<String, BCF2FieldWriter.SiteWriter> siteWriters = new HashMap<String, BCF2FieldWriter.SiteWriter>(); final Map<String, BCF2FieldWriter.GenotypesWriter> genotypesWriters = new HashMap<String, BCF2FieldWriter.GenotypesWriter>(); final IntGenotypeFieldAccessors intGenotypeFieldAccessors = new IntGenotypeFieldAccessors(); public BCF2FieldWriterManager() { } /** * Setup the FieldWriters appropriate to each INFO and FORMAT in the VCF header * * Must be called before any of the getter methods will work * * @param header a VCFHeader containing description for every INFO and FORMAT field we'll attempt to write out to BCF * @param encoder the encoder we are going to use to write out the BCF2 data * @param stringDictionary a map from VCFHeader strings to their offsets for encoding */ public void setup(final VCFHeader header, final BCF2Encoder encoder, final Map<String, Integer> stringDictionary) { for (final VCFInfoHeaderLine line : header.getInfoHeaderLines()) { final String field = line.getID(); final BCF2FieldWriter.SiteWriter writer = createInfoWriter(header, line, encoder, stringDictionary); add(siteWriters, field, writer); } for (final VCFFormatHeaderLine line : header.getFormatHeaderLines()) { final String field = line.getID(); final BCF2FieldWriter.GenotypesWriter writer = createGenotypesWriter(header, line, encoder, stringDictionary); add(genotypesWriters, field, writer); } } private final <T> void add(final Map<String, T> map, final String field, final T writer) { if ( map.containsKey(field) ) throw new IllegalStateException("BUG: field " + field + " already seen in VCFHeader while building BCF2 field encoders"); map.put(field, writer); } // ----------------------------------------------------------------- // // Master routine to look at the header, a specific line, and // build an appropriate SiteWriter for that header element // // ----------------------------------------------------------------- private BCF2FieldWriter.SiteWriter createInfoWriter(final VCFHeader header, final VCFInfoHeaderLine line, final BCF2Encoder encoder, final Map<String, Integer> dict) { return new BCF2FieldWriter.GenericSiteWriter(header, createFieldEncoder(line, encoder, dict, false)); } private BCF2FieldEncoder createFieldEncoder(final VCFCompoundHeaderLine line, final BCF2Encoder encoder, final Map<String, Integer> dict, final boolean createGenotypesEncoders ) { if ( createGenotypesEncoders && intGenotypeFieldAccessors.getAccessor(line.getID()) != null ) { if ( GeneralUtils.DEBUG_MODE_ENABLED && line.getType() != VCFHeaderLineType.Integer ) System.err.println("Warning: field " + line.getID() + " expected to encode an integer but saw " + line.getType() + " for record " + line); return new BCF2FieldEncoder.IntArray(line, dict); } else if ( createGenotypesEncoders && line.getID().equals(VCFConstants.GENOTYPE_KEY) ) { return new BCF2FieldEncoder.GenericInts(line, dict); } else { switch ( line.getType() ) { case Character: case String: return new BCF2FieldEncoder.StringOrCharacter(line, dict); case Flag: return new BCF2FieldEncoder.Flag(line, dict); case Float: return new BCF2FieldEncoder.Float(line, dict); case Integer: if ( line.isFixedCount() && line.getCount() == 1 ) return new BCF2FieldEncoder.AtomicInt(line, dict); else return new BCF2FieldEncoder.GenericInts(line, dict); default: throw new IllegalArgumentException("Unexpected type for field " + line.getID()); } } } // ----------------------------------------------------------------- // // Master routine to look at the header, a specific line, and // build an appropriate Genotypes for that header element // // ----------------------------------------------------------------- private BCF2FieldWriter.GenotypesWriter createGenotypesWriter(final VCFHeader header, final VCFFormatHeaderLine line, final BCF2Encoder encoder, final Map<String, Integer> dict) { final String field = line.getID(); final BCF2FieldEncoder fieldEncoder = createFieldEncoder(line, encoder, dict, true); if ( field.equals(VCFConstants.GENOTYPE_KEY) ) { return new BCF2FieldWriter.GTWriter(header, fieldEncoder); } else if ( line.getID().equals(VCFConstants.GENOTYPE_FILTER_KEY) ) { return new BCF2FieldWriter.FTGenotypesWriter(header, fieldEncoder); } else if ( intGenotypeFieldAccessors.getAccessor(field) != null ) { return new BCF2FieldWriter.IGFGenotypesWriter(header, fieldEncoder, intGenotypeFieldAccessors.getAccessor(field)); } else if ( line.getType() == VCFHeaderLineType.Integer ) { return new BCF2FieldWriter.IntegerTypeGenotypesWriter(header, fieldEncoder); } else { return new BCF2FieldWriter.StaticallyTypeGenotypesWriter(header, fieldEncoder); } } // ----------------------------------------------------------------- // // Accessors to get site / genotype writers // // ----------------------------------------------------------------- /** * Get a site writer specialized to encode values for site info field * @param field key found in the VCF header INFO records * @return non-null writer if one can be found, or null if none exists for field */ public BCF2FieldWriter.SiteWriter getSiteFieldWriter(final String field) { return getWriter(field, siteWriters); } /** * Get a genotypes writer specialized to encode values for genotypes field * @param field key found in the VCF header FORMAT records * @return non-null writer if one can be found, or null if none exists for field */ public BCF2FieldWriter.GenotypesWriter getGenotypeFieldWriter(final String field) { return getWriter(field, genotypesWriters); } public <T> T getWriter(final String key, final Map<String, T> map) { return map.get(key); } }