/*
* Copyright 2015-2016 OpenCB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opencb.opencga.storage.core.variant.io.json;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.opencb.biodata.formats.variant.io.VariantWriter;
import org.opencb.biodata.models.feature.Genotype;
import org.opencb.biodata.models.variant.StudyEntry;
import org.opencb.biodata.models.variant.Variant;
import org.opencb.biodata.models.variant.VariantSource;
import org.opencb.biodata.models.variant.VariantVcfFactory;
import org.opencb.biodata.models.variant.avro.FileEntry;
import org.opencb.biodata.models.variant.avro.VariantAnnotation;
import org.opencb.biodata.models.variant.stats.VariantStats;
import org.opencb.opencga.storage.core.variant.io.VariantReaderUtils;
import org.opencb.opencga.storage.core.variant.io.json.mixin.*;
import javax.annotation.Nullable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
/**
* @author Cristina Yenyxe Gonzalez Garcia <cyenyxe@ebi.ac.uk>
*/
public class VariantJsonWriter implements VariantWriter {
private final VariantSource source;
// Null if OutputStreams were directly provided
private final Path outdir;
protected JsonFactory factory;
protected ObjectMapper jsonObjectMapper;
protected JsonGenerator variantsGenerator;
protected JsonGenerator fileGenerator;
private OutputStream variantsStream;
private OutputStream fileStream;
private long numVariantsWritten;
private boolean includeSrc = false;
private boolean includeStats;
private boolean includeSamples = true;
private boolean closeStreams;
public VariantJsonWriter(VariantSource source, @Nullable Path outdir) {
Objects.requireNonNull(source, "VariantSource can not be null");
this.source = source;
this.outdir = (outdir != null) ? outdir : Paths.get("").toAbsolutePath();
this.factory = new JsonFactory();
this.jsonObjectMapper = new ObjectMapper(this.factory);
this.numVariantsWritten = 0;
closeStreams = true;
}
public VariantJsonWriter(OutputStream variantsStream) {
this(null, variantsStream, null);
}
public VariantJsonWriter(VariantSource source, OutputStream variantsStream, OutputStream fileStream) {
this.source = source;
this.outdir = null;
this.variantsStream = variantsStream;
this.fileStream = fileStream;
this.factory = new JsonFactory();
this.jsonObjectMapper = new ObjectMapper(this.factory);
this.numVariantsWritten = 0;
closeStreams = false;
}
@Override
public boolean open() {
try {
if (outdir != null) {
String output = Paths.get(outdir.toString(), source.getFileName()).toAbsolutePath().toString()
+ "." + VariantReaderUtils.VARIANTS_FILE + ".json.gz";
variantsStream = new GZIPOutputStream(new FileOutputStream(output));
fileStream = new GZIPOutputStream(new FileOutputStream(VariantReaderUtils.getMetaFromTransformedFile(output)));
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
return true;
}
@Override
public boolean pre() {
jsonObjectMapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, true);
jsonObjectMapper.addMixIn(StudyEntry.class, VariantSourceEntryJsonMixin.class);
jsonObjectMapper.addMixIn(Genotype.class, GenotypeJsonMixin.class);
jsonObjectMapper.addMixIn(VariantStats.class, VariantStatsJsonMixin.class);
jsonObjectMapper.addMixIn(VariantSource.class, VariantSourceJsonMixin.class);
jsonObjectMapper.addMixIn(VariantAnnotation.class, VariantAnnotationMixin.class);
try {
variantsGenerator = factory.createGenerator(variantsStream);
if (fileStream != null || source != null) {
fileGenerator = factory.createGenerator(fileStream);
}
} catch (IOException ex) {
close();
throw new UncheckedIOException(ex);
}
return true;
}
@Override
public boolean write(Variant variant) {
try {
variantsGenerator.writeObject(variant);
variantsGenerator.writeRaw('\n');
} catch (IOException ex) {
Logger.getLogger(VariantJsonWriter.class.getName()).log(Level.SEVERE, variant.getChromosome() + ":" + variant.getStart(), ex);
close();
throw new UncheckedIOException(ex);
}
return true;
}
@Override
public boolean write(List<Variant> batch) {
for (Variant variant : batch) {
try {
for (StudyEntry studyEntry : variant.getStudies()) {
if (!includeSrc) {
for (FileEntry fileEntry : studyEntry.getFiles()) {
if (fileEntry.getAttributes().containsKey(VariantVcfFactory.SRC)) {
fileEntry.getAttributes().remove(VariantVcfFactory.SRC);
}
}
}
if (!includeSamples) {
studyEntry.getSamplesData().clear();
}
if (!includeStats) {
studyEntry.setStats(Collections.emptyMap());
}
}
variantsGenerator.writeObject(variant);
variantsGenerator.writeRaw('\n');
} catch (IOException ex) {
Logger.getLogger(VariantJsonWriter.class.getName()).log(Level.SEVERE, variant.getChromosome() + ":" + variant.getStart(),
ex);
close();
throw new UncheckedIOException(ex);
}
}
numVariantsWritten += batch.size();
if (numVariantsWritten % 1000 == 0) {
Variant lastVariantInBatch = batch.get(batch.size() - 1);
Logger.getLogger(VariantJsonWriter.class.getName()).log(Level.INFO, "{0}\tvariants written upto position {1}:{2}",
new Object[]{numVariantsWritten, lastVariantInBatch.getChromosome(), lastVariantInBatch.getStart()});
}
return true;
}
@Override
public boolean post() {
try {
variantsStream.flush();
variantsGenerator.flush();
if (fileGenerator != null) {
fileGenerator.writeObject(source);
fileStream.flush();
fileGenerator.flush();
}
} catch (IOException ex) {
Logger.getLogger(VariantJsonWriter.class.getName()).log(Level.SEVERE, null, ex);
close();
throw new UncheckedIOException(ex);
}
return true;
}
@Override
public boolean close() {
try {
if (closeStreams) {
variantsGenerator.close();
fileGenerator.close();
}
} catch (IOException ex) {
Logger.getLogger(VariantJsonWriter.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
return true;
}
@Override
public void includeStats(boolean stats) {
this.includeStats = stats;
}
public void includeSrc(boolean src) {
this.includeSrc = src;
}
@Override
public void includeSamples(boolean samples) {
this.includeSamples = samples;
}
@Override
public void includeEffect(boolean effect) {
}
}