All Downloads are FREE. Search and download functionalities are using the official Maven repository.

htsjdk.variant.variantcontext.VariantContextBuilder Maven / Gradle / Ivy

There is a newer version: 4.1.3
Show newest version
/*
* 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;

import htsjdk.variant.vcf.VCFConstants;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
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.List;
import java.util.Map;
import java.util.Set;

/**
 * 

Builder class for VariantContext.

* *

Some basic assumptions here:

*
    *
  1. data isn't protectively copied. If you provide an attribute map to * the build, and modify it later, the builder will see this and so will any * resulting variant contexts. It's best not to modify collections provided * to a builder.
  2. * *
  3. the system uses the standard builder model, allowing the simple construction idiom: *
    * builder.source("a").genotypes(gc).id("x").make() => VariantContext *
  4. *
  5. The best way to copy a VariantContext is: *
    * new VariantContextBuilder(vc).make() => a copy of VC *
    *
  6. validation of arguments is done at the during the final make() call, so a * VariantContextBuilder can exist in an inconsistent state as long as those issues * are resolved before the call to make() is issued. *
* @author depristo */ public class VariantContextBuilder { // required fields private boolean fullyDecoded = false; private String source = null; private String contig = null; private long start = -1; private long stop = -1; private Collection alleles = null; // optional -> these are set to the appropriate default value private String ID = VCFConstants.EMPTY_ID_FIELD; private GenotypesContext genotypes = GenotypesContext.NO_GENOTYPES; private double log10PError = VariantContext.NO_LOG10_PERROR; private Set filters = null; private Map attributes = null; private boolean attributesCanBeModified = false; private boolean filtersCanBeModified = false; /** enum of what must be validated */ final private EnumSet toValidate = EnumSet.noneOf(VariantContext.Validation.class); /** * Create an empty VariantContextBuilder where all values adopt their default values. Note that * source, chr, start, stop, and alleles must eventually be filled in, or the resulting VariantContext * will throw an error. */ public VariantContextBuilder() {} /** * Create an empty VariantContextBuilder where all values adopt their default values, but the bare min. * of info (source, chr, start, stop, and alleles) have been provided to start. */ public VariantContextBuilder(final String source, final String contig, final long start, final long stop, final Collection alleles) { this.source = source; this.contig = contig; this.start = start; this.stop = stop; this.alleles = alleles; this.attributes = Collections.emptyMap(); // immutable toValidate.add(VariantContext.Validation.ALLELES); } /** * Getter for contig * @return the current contig */ public String getContig() { return contig; } /** * Getter for start position * @return the current start position */ public long getStart() { return start; } /** * Getter for stop position * @return the current stop position */ public long getStop() { return stop; } /** * Getter for id of variant * @return the current variant id */ public String getID() { return ID; } /** * Getter for genotypes (DANGEROUS!!! DOES NOT MAKE A COPY!!!) * @return the current GenotypeContext */ public GenotypesContext getGenotypes() { return genotypes; } /** * Getter for filters (DANGEROUS!!! DOES NOT MAKE A COPY!!!) * @return the current set of filters */ public Set getFilters() { return filters; } /** * Getter for attributes (DANGEROUS!!! DOES NOT MAKE A COPY!!!) * @return the current map of attributes */ public Map getAttributes() { return attributes; } /** * Returns a new builder based on parent -- the new VC will have all fields initialized * to their corresponding values in parent. This is the best way to create a derived VariantContext * * @param parent Cannot be null */ public VariantContextBuilder(final VariantContext parent) { if (parent == null) { throw new IllegalArgumentException("BUG: VariantContextBuilder parent argument cannot be null in VariantContextBuilder"); } this.alleles = parent.getAlleles(); this.contig = parent.getContig(); this.genotypes = parent.getGenotypes(); this.ID = parent.getID(); this.log10PError = parent.getLog10PError(); this.source = parent.getSource(); this.start = parent.getStart(); this.stop = parent.getEnd(); this.fullyDecoded = parent.isFullyDecoded(); this.attributes(parent.getAttributes()); if (parent.filtersWereApplied()) { this.filters(parent.getFilters()); } else { this.unfiltered(); } } public VariantContextBuilder(final VariantContextBuilder parent) { if ( parent == null ) throw new IllegalArgumentException("BUG: VariantContext parent argument cannot be null in VariantContextBuilder"); this.attributesCanBeModified = false; this.filtersCanBeModified = false; this.alleles = parent.alleles; this.contig = parent.contig; this.genotypes = parent.genotypes; this.ID = parent.ID; this.log10PError = parent.log10PError; this.source = parent.source; this.start = parent.start; this.stop = parent.stop; this.fullyDecoded = parent.fullyDecoded; this.attributes(parent.attributes); this.filters(parent.filters); } public VariantContextBuilder copy() { return new VariantContextBuilder(this); } /** * Tells this builder to use this collection of alleles for the resulting VariantContext * * @param alleles a Collection of alleles to set as the alleles of this builder * @return this builder */ public VariantContextBuilder alleles(final Collection alleles) { this.alleles = alleles; toValidate.add(VariantContext.Validation.ALLELES); return this; } public VariantContextBuilder alleles(final List alleleStrings) { final List alleles = new ArrayList<>(alleleStrings.size()); for ( int i = 0; i < alleleStrings.size(); i++ ) { alleles.add(Allele.create(alleleStrings.get(i), i == 0)); } return alleles(alleles); } public VariantContextBuilder alleles(final String ... alleleStrings) { return alleles(Arrays.asList(alleleStrings)); } public List getAlleles() { return new ArrayList<>(alleles); } /** * Tells this builder to use this map of attributes for the resulting VariantContext. The * contents of the Map are copied to a new Map to ensure that modifications to the provided Map post-invocation * don't affect the VariantContext and also to ensure additional attributes can be added in case the provided * map doesn't support changes (e.g. UnmodifiableMap). * * Attributes can be null -> meaning there are no attributes. After * calling this routine the builder assumes it can modify the attributes * object here, if subsequent calls are made to set attribute values * * Value for each attribute must be of a type that implements {@link Serializable} or else * serialization will fail. * * @param attributes a Map of attributes to replace existing attributes with */ public VariantContextBuilder attributes(final Map attributes) { this.attributes = new HashMap<>(); if (attributes != null) this.attributes.putAll(attributes); this.attributesCanBeModified = true; return this; } /** * Tells this builder to put this map of attributes into the resulting VariantContext. The * contents of the Map are copied to the current Map (or a new one is created if null) * * After calling this routine the builder assumes it can modify the attributes * object here, if subsequent calls are made to set attribute values * * Value for each attribute must be of a type that implements {@link Serializable} or else * serialization will fail. * * @param attributes a Map of attributes to complement any existing attributes with, overwriting any that * share the same key. */ public VariantContextBuilder putAttributes(final Map attributes) { if (this.attributes == null) { this.attributes = new HashMap<>(); } if (attributes != null) { this.attributes.putAll(attributes); } this.attributesCanBeModified = true; return this; } /** * Puts the key -> value mapping into this builder's attributes * * @param key key for the attribute * @param value value for the attribute (must be of a type that implements {@link Serializable} or else serialization will fail) */ public VariantContextBuilder attribute(final String key, final Object value) { makeAttributesModifiable(); attributes.put(key, value); return this; } /** * Removes key if present in the attributes * * @param key key to remove * @return this builder */ public VariantContextBuilder rmAttribute(final String key) { makeAttributesModifiable(); attributes.remove(key); return this; } /** * Removes list of keys if present in the attributes * * @param keys list of keys to remove * @return this builder */ public VariantContextBuilder rmAttributes(final List keys) { makeAttributesModifiable(); for ( final String key : keys ) attributes.remove(key); return this; } /** * Makes the attributes field modifiable. In many cases attributes is just a pointer to an immutable * collection, so methods that want to add / remove records require the attributes to be copied to a */ private void makeAttributesModifiable() { if (!attributesCanBeModified) { this.attributesCanBeModified = true; final Map tempAttributes = attributes; if (tempAttributes != null) { this.attributes = new HashMap<>(tempAttributes); } else { this.attributes = new HashMap<>(); } } } /** * Makes the filters modifiable. */ private void makeFiltersModifiable() { if (!filtersCanBeModified) { this.filtersCanBeModified = true; final Set tempFilters = filters; this.filters = new HashSet<>(); if (tempFilters != null) { this.filters.addAll(tempFilters); } } } /** * This builder's filters are set to this value * * filters can be null -> meaning there are no filters * * @param filters Set of strings to set as the filters for this builder * This set will be copied so that external set can be * safely changed. * @return this builder */ public VariantContextBuilder filters(final Set filters) { if (filters == null) { unfiltered(); } else { this.filtersCanBeModified = true; filtersAsIs(new HashSet<>(filters)); } return this; } /** * This builder's filters are set to this value * * filters can be null -> meaning there are no filters * * @param filters Set of strings to set as the filters for this builder * @return this builder */ private void filtersAsIs(final Set filters) { this.filters = filters; toValidate.add(VariantContext.Validation.FILTERS); } /** * {@link #filters} * * @param filters Set of strings to set as the filters for this builder * @return this builder */ public VariantContextBuilder filters(final String ... filters) { filtersAsIs(new LinkedHashSet<>(Arrays.asList(filters))); return this; } /** Adds the given filter to the list of filters * * @param filter * @return */ public VariantContextBuilder filter(final String filter) { makeFiltersModifiable(); this.filters.add(filter); return this; } /** * Tells this builder that the resulting VariantContext should have PASS filters * * @return this builder */ public VariantContextBuilder passFilters() { filtersAsIs(VariantContext.PASSES_FILTERS); return this; } /** * Tells this builder that the resulting VariantContext be unfiltered * * @return this builder */ public VariantContextBuilder unfiltered() { this.filters = null; return this; } /** * Tells this builder that the resulting VariantContext should use this genotype's GenotypeContext. * * Note that this method will call the immutable method on the provided genotypes object * to ensure that the user will not modify it further. * Note that genotypes can be null -> meaning there are no genotypes * * @param genotypes GenotypeContext to use in this builder * @return this builder */ public VariantContextBuilder genotypes(final GenotypesContext genotypes) { this.genotypes = genotypes; if (genotypes != null) { genotypes.immutable(); toValidate.add(VariantContext.Validation.GENOTYPES); } return this; } public VariantContextBuilder genotypesNoValidation(final GenotypesContext genotypes) { this.genotypes = genotypes; return this; } /** * Tells this builder that the resulting VariantContext should use a GenotypeContext containing genotypes * * Note that genotypes can be null, meaning there are no genotypes * * @param genotypes Collection of genotypes to set as genotypes for this builder */ public VariantContextBuilder genotypes(final Collection genotypes) { return genotypes(GenotypesContext.copy(genotypes)); } /** * Tells this builder that the resulting VariantContext should use a GenotypeContext containing genotypes * @param genotypes genotypes to set as genotypes for this builder */ public VariantContextBuilder genotypes(final Genotype ... genotypes) { return genotypes(GenotypesContext.copy(Arrays.asList(genotypes))); } /** * Tells this builder that the resulting VariantContext should not contain any GenotypeContext */ public VariantContextBuilder noGenotypes() { this.genotypes = null; return this; } /** * Tells us that the resulting VariantContext should have ID * @param ID id of variant * @return this builder */ public VariantContextBuilder id(final String ID) { this.ID = ID; return this; } /** * Tells us that the resulting VariantContext should not have an ID * @return this builder */ public VariantContextBuilder noID() { return id(VCFConstants.EMPTY_ID_FIELD); } /** * Tells us that the resulting VariantContext should have log10PError * @param log10PError value of QUAL field for this builder * @return this builder */ public VariantContextBuilder log10PError(final double log10PError) { this.log10PError = log10PError; return this; } /** * Tells us that the resulting VariantContext should have source field set to source * @param source string describing the source of the variant * @return this builder */ public VariantContextBuilder source(final String source) { this.source = source; return this; } /** * Tells us that the resulting VariantContext should have the specified location * @param contig the contig the variant is on (must be in the dictionary) * @param start the start position of the variant * @param stop the end position of the variant * @return this builder */ public VariantContextBuilder loc(final String contig, final long start, final long stop) { this.contig = contig; this.start = start; this.stop = stop; toValidate.add(VariantContext.Validation.ALLELES); return this; } /** * Tells us that the resulting VariantContext should have the specified contig chr * @param contig the contig of the variant * @return this builder */ public VariantContextBuilder chr(final String contig) { this.contig = contig; return this; } /** * Tells us that the resulting VariantContext should have the specified contig start * @param start the start position of the variant * @return this builder */ public VariantContextBuilder start(final long start) { this.start = start; toValidate.add(VariantContext.Validation.ALLELES); return this; } /** * Tells us that the resulting VariantContext should have the specified contig stop * @param stop the stop position of the variant * @return this builder */ public VariantContextBuilder stop(final long stop) { this.stop = stop; return this; } /** * @see #computeEndFromAlleles(java.util.List, int, int) with endForSymbolicAlleles == -1 */ public VariantContextBuilder computeEndFromAlleles(final List alleles, final int start) { return computeEndFromAlleles(alleles, start, -1); } /** * Compute the end position for this VariantContext from the alleles themselves * * assigns this builder the stop position computed. * * @param alleles the list of alleles to consider. The reference allele must be the first one * @param start the known start position of this event * @param endForSymbolicAlleles the end position to use if any of the alleles is symbolic. Can be -1 * if no is expected but will throw an error if one is found * @return this builder */ public VariantContextBuilder computeEndFromAlleles(final List alleles, final int start, final int endForSymbolicAlleles) { stop(VariantContextUtils.computeEndFromAlleles(alleles, start, endForSymbolicAlleles)); return this; } /** * @return true if this builder contains fully decoded data * * See VariantContext for more information */ public boolean isFullyDecoded() { return fullyDecoded; } /** * Sets this builder's fully decoded state to true. * * A fully decoded builder indicates that all fields are represented by their * proper java objects (e.g., Integer(10) not "10"). * * See VariantContext for more information * * @param isFullyDecoded */ public VariantContextBuilder fullyDecoded(boolean isFullyDecoded) { this.fullyDecoded = isFullyDecoded; return this; } /** * Takes all of the builder data provided up to this point, and instantiates * a freshly allocated VariantContext with all of the builder data. This * VariantContext is validated as appropriate and if not failing QC (and * throwing an exception) is returned. * * Note that this function can be called multiple times to create multiple * VariantContexts from the same builder. */ public VariantContext make() { return make(false); } public VariantContext make(final boolean leaveModifyableAsIs) { if (!leaveModifyableAsIs) { attributesCanBeModified = false; filtersCanBeModified = false; } return new VariantContext(source, ID, contig, start, stop, alleles, genotypes, log10PError, filters, attributes, fullyDecoded, toValidate); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy