2727
2828import htsjdk .samtools .SAMSequenceDictionary ;
2929import htsjdk .samtools .util .IOUtil ;
30+ import htsjdk .samtools .util .Log ;
3031import htsjdk .samtools .util .RuntimeIOException ;
32+ import htsjdk .tribble .TribbleException ;
3133import htsjdk .tribble .index .IndexCreator ;
34+ import htsjdk .utils .ValidationUtils ;
3235import htsjdk .variant .variantcontext .VariantContext ;
3336import htsjdk .variant .variantcontext .VariantContextBuilder ;
3437import htsjdk .variant .vcf .VCFConstants ;
3538import htsjdk .variant .vcf .VCFEncoder ;
3639import htsjdk .variant .vcf .VCFHeader ;
3740import htsjdk .variant .vcf .VCFHeaderLine ;
3841import htsjdk .variant .vcf .VCFHeaderVersion ;
42+ import htsjdk .variant .vcf .VCFUtils ;
3943
4044import java .io .BufferedWriter ;
4145import java .io .ByteArrayOutputStream ;
4549import java .io .OutputStreamWriter ;
4650import java .io .Writer ;
4751import java .nio .file .Path ;
52+ import java .util .stream .Collectors ;
4853
4954/**
5055 * this class writes VCF files
5156 */
5257class VCFWriter extends IndexingVariantContextWriter {
58+ protected final static Log logger = Log .getInstance (VCFWriter .class );
5359
54- private static final String VERSION_LINE =
55- VCFHeader .METADATA_INDICATOR + VCFHeaderVersion .VCF4_2 .getFormatString () + "=" + VCFHeaderVersion .VCF4_2 .getVersionString ();
60+ private static final String DEFAULT_VERSION_LINE = VCFHeader .DEFAULT_VCF_VERSION .toHeaderVersionLine ();
5661
5762 // Initialized when the header is written to the output stream
5863 private VCFEncoder vcfEncoder = null ;
@@ -164,7 +169,7 @@ public void writeHeader(final VCFHeader header) {
164169 }
165170
166171 public static String getVersionLine () {
167- return VERSION_LINE ;
172+ return DEFAULT_VERSION_LINE ;
168173 }
169174
170175 public static VCFHeader writeHeader (VCFHeader header ,
@@ -175,12 +180,18 @@ public static VCFHeader writeHeader(VCFHeader header,
175180 try {
176181 rejectVCFV43Headers (header );
177182
178- // the file format field needs to be written first
183+ // Validate that the file version we're writing is version-compatible this header's version.
184+ validateHeaderVersion (header , versionLine );
185+
186+ // The file format field needs to be written first; below any file format lines
187+ // embedded in the header will be removed
179188 writer .write (versionLine + "\n " );
180189
181190 for (final VCFHeaderLine line : header .getMetaDataInSortedOrder () ) {
182- if ( VCFHeaderVersion .isFormatString (line .getKey ()) )
191+ // Remove the fileformat header lines
192+ if ( VCFHeaderVersion .isFormatString (line .getKey ()) ) {
183193 continue ;
194+ }
184195
185196 writer .write (VCFHeader .METADATA_INDICATOR );
186197 writer .write (line .toString ());
@@ -189,14 +200,9 @@ public static VCFHeader writeHeader(VCFHeader header,
189200
190201 // write out the column line
191202 writer .write (VCFHeader .HEADER_INDICATOR );
192- boolean isFirst = true ;
193- for (final VCFHeader .HEADER_FIELDS field : header .getHeaderFields () ) {
194- if ( isFirst )
195- isFirst = false ; // don't write out a field separator
196- else
197- writer .write (VCFConstants .FIELD_SEPARATOR );
198- writer .write (field .toString ());
199- }
203+ writer .write (header .getHeaderFields ().stream ()
204+ .map (f -> f .name ())
205+ .collect (Collectors .joining (VCFConstants .FIELD_SEPARATOR )).toString ());
200206
201207 if ( header .hasGenotypingData () ) {
202208 writer .write (VCFConstants .FIELD_SEPARATOR );
@@ -217,6 +223,33 @@ public static VCFHeader writeHeader(VCFHeader header,
217223 return header ;
218224 }
219225
226+ /**
227+ * Given a header and a requested target output version, see if the header's version is compatible with the
228+ * requested version.
229+ * @param header
230+ * @param requestedVersionLine
231+ */
232+ private static void validateHeaderVersion (final VCFHeader header , final String requestedVersionLine ) {
233+ ValidationUtils .nonNull (header );
234+ ValidationUtils .nonNull (requestedVersionLine );
235+
236+ final VCFHeaderVersion vcfCurrentVersion = header .getVCFHeaderVersion ();
237+ final VCFHeaderVersion vcfRequestedVersion = VCFHeaderVersion .getHeaderVersion (requestedVersionLine );
238+ if (!vcfCurrentVersion .equals (vcfRequestedVersion )) {
239+ final String message = String .format ("Attempting to write a %s VCF header to a %s VCFWriter" ,
240+ vcfRequestedVersion ,
241+ vcfCurrentVersion .getVersionString ());
242+ if (!VCFHeaderVersion .versionsAreCompatible (VCFHeaderVersion .getHeaderVersion (requestedVersionLine ), vcfCurrentVersion )) {
243+ if (VCFUtils .getStrictVCFVersionValidation ()) {
244+ throw new TribbleException (message );
245+ }
246+ }
247+ if (VCFUtils .getVerboseVCFLogging ()) {
248+ logger .warn (message );
249+ }
250+ }
251+ }
252+
220253 /**
221254 * attempt to close the VCF file
222255 */
0 commit comments