/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.core.multiGenome.data.display; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import edu.yu.einstein.genplay.core.manager.project.ProjectManager; import edu.yu.einstein.genplay.core.multiGenome.VCF.VCFFile.VCFFile; import edu.yu.einstein.genplay.core.multiGenome.data.display.content.MGFileContentManager; import edu.yu.einstein.genplay.core.multiGenome.data.display.variant.ReferenceVariant; import edu.yu.einstein.genplay.core.multiGenome.data.display.variant.Variant; import edu.yu.einstein.genplay.core.multiGenome.filter.MGFilter; import edu.yu.einstein.genplay.core.multiGenome.filter.VCFFilter; import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome; import edu.yu.einstein.genplay.dataStructure.enums.VariantType; /** * A {@link VariantDisplayList} handles the list of {@link Variant} for each allele. * There is as many {@link Variant} lists as alleles. * It also handle the display policy of each {@link Variant}. * A {@link Variant} can be: * - shown * - filtered but shown (the "hide filtered variants" feature is off) * - hidden because filtered (the "hide filtered variants" feature is on) * - hidden because it is a homozygote reference (the "hide references" feature is on) * - hidden because filtered AND because it is a homozygote reference (both feature mentionned above are on) * * The {@link VariantDisplayList} is specific of one genome and knows which {@link VariantType} it handles. * * @author Nicolas Fourel * @version 0.1 */ public class VariantDisplayList implements Serializable { /** Default serial version ID */ private static final long serialVersionUID = 2664998644351746289L; private static final int SAVED_FORMAT_VERSION_NUMBER = 1; // saved format version /** When a variant is shown */ public static byte SHOW = 1; /** When a variant is filtered but shown */ public static byte SHOW_FILTER = 2; /** When a variant is hidden because of all options */ public static byte HIDE_ALL = -1; /** When a variant is hidden because filtered variants must be hidden */ public static byte HIDE_FILTER = -2; /** When a variant is hidden because references must be hidden (case of a filtered homozygote reference) */ public static byte HIDE_REFERENCE = -3; private List<List<Variant>> variants; // The lists of variants for all alleles. private byte[][] display; // The display policy bytes for all variants within all alleles (as array of bytes for memory usage). private String genomeName; // The name of the genome. private List<VariantType> types; // The list of variant type to handle. /** * Constructor of {@link VariantDisplayList} */ public VariantDisplayList () { initialize(null, null); } /** * Generate the lists of variants */ public void generateLists () { VariantDisplayListBuilder builder = new VariantDisplayListBuilder(); variants = builder.getList(genomeName, types); builder = null; } /** * @return the display */ public byte[][] getDisplay() { return display; } /** * @return the genomeName */ public String getGenomeName() { return genomeName; } /** * Recursive function. Returns the index where the value is found or -1 if the exact value is not found. * @param list * @param value a position on the meta genome * @return the index where the start value of the window is found or -1 if the value is not found */ public int getIndex (List<Variant> list, int value) { int index = getIndex(list, value, 0, list.size() - 1); if (index >= 0) { int start = list.get(index).getStart(); int stop = list.get(index).getStop(); if ((value >= start) && (value < stop)) { return index; } } return -1; } /** * Recursive function. Returns the index where the value is found * or the index right after if the exact value is not found. * @param value value * @param indexStart start index (in the data array) * @param indexStop stop index (in the data array) * @return the index where the start value of the window is found or the index right after if the exact value is not found */ private int getIndex (List<Variant> list, int value, int indexStart, int indexStop) { if (list.isEmpty()) { return -1; } int middle = (indexStop - indexStart) / 2; if (indexStart == indexStop) { return indexStart; } else { int start = list.get(indexStart + middle).getStart(); int stop = list.get(indexStart + middle).getStop(); if ((value >= start) && (value < stop)) { return indexStart + middle; } else if (value > start) { return getIndex(list, value, indexStart + middle + 1, indexStop); } else { return getIndex(list, value, indexStart, indexStart + middle); } } } /** * @param alleleIndex the index of the allele to iterate * @return a specific iterator for {@link VariantDisplayListIterator} */ public VariantDisplayListIterator getIterator (int alleleIndex) { return new VariantDisplayListIterator(this, alleleIndex); } /** * @return the variants */ public List<List<Variant>> getVariants() { return variants; } /** * @param alleleIndex the index of an allele * @param metaGenomePosition a meta genome position * @return the list of variants at the given allele including the given meta genome position, null if not found */ public List<Variant> getVariantsInArea (int alleleIndex, int metaGenomePosition) { List<Variant> result = new ArrayList<Variant>(); int index = getIndex(variants.get(alleleIndex), metaGenomePosition); if (index > -1) { result.add(variants.get(alleleIndex).get(index)); result.addAll(lookAfterIndex(alleleIndex, index, metaGenomePosition)); result.addAll(lookBeforeIndex(alleleIndex, index, metaGenomePosition)); } return result; } /** * Initializes the {@link VariantDisplayList} input parameters * @param genomeName the name of a genome * @param types a list of {@link VariantType} */ public void initialize (String genomeName, List<VariantType> types) { this.genomeName = genomeName; this.types = types; display = null; } private void initialyzeDisplay () { display = new byte[variants.size()][]; for (int i = 0; i < variants.size(); i++) { List<Variant> currentVariantList = variants.get(i); display[i] = new byte[currentVariantList.size()]; } } /** * Look for all {@link Variant} after a specific index (within a specific allele) which include a meta genome position. * @param alleleIndex the index of the allele * @param index the index where to start * @param metaGenomePosition the meta genome position to use * @return the list of {@link Variant} including the meta genome position after the given index, an empty list otherwise */ private List<Variant> lookAfterIndex (int alleleIndex, int index, int metaGenomePosition) { List<Variant> result = new ArrayList<Variant>(); int nextIndex = index + 1; boolean includePosition = true; List<Variant> variantList = variants.get(alleleIndex); int size = variantList.size(); while (includePosition && (nextIndex < size)) { Variant current = variantList.get(nextIndex); if ((metaGenomePosition >= current.getStart()) && (metaGenomePosition < current.getStop())) { result.add(current); nextIndex++; } else { includePosition = false; } } return result; } /** * Look for all {@link Variant} before a specific index (within a specific allele) which include a meta genome position. * @param alleleIndex the index of the allele * @param index the index where to start * @param metaGenomePosition the meta genome position to use * @return the list of {@link Variant} including the meta genome position before the given index, an empty list otherwise */ private List<Variant> lookBeforeIndex (int alleleIndex, int index, int metaGenomePosition) { List<Variant> result = new ArrayList<Variant>(); int previousIndex = index - 1; boolean includePosition = true; List<Variant> variantList = variants.get(alleleIndex); while (includePosition && (previousIndex > -1)) { Variant current = variantList.get(previousIndex); if ((metaGenomePosition >= current.getStart()) && (metaGenomePosition < current.getStop())) { result.add(current); previousIndex--; } else { includePosition = false; } } return result; } /** * Method used for unserialization * @param in * @throws IOException * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { int savedVersion = in.readInt(); if (savedVersion == 0) { // after version 0 we don't load the variant list anymore, we regenerate it instead variants = (List<List<Variant>>) in.readObject(); } display = (byte[][]) in.readObject(); genomeName = (String) in.readObject(); types = (List<VariantType>) in.readObject(); if (savedVersion > 0) { // regenerate the list instead of loading it generateLists(); } } /** * Update the display policy using the list of filters * @param filters list of {@link MGFilter} * @param showFilter true if filtered {@link Variant} have to be shown, false otherwise */ public void updateDisplay (List<MGFilter> filters, boolean showFilter) { initialyzeDisplay(); updateDisplayForFilters(filters, showFilter); } /** * Updates the display policy arrays according to the filters and the filter option * @param filters the list of {@link MGFilter} * @param showFilter the filter option (true: filters are shown, false: filters are hidden) */ private void updateDisplayForFilters (List<MGFilter> filters, boolean showFilter) { MGFileContentManager contentManager = ProjectManager.getInstance().getMultiGenomeProject().getFileContentManager(); Chromosome currentChromosome = ProjectManager.getInstance().getProjectWindow().getGenomeWindow().getChromosome(); for (int i = 0; i < variants.size(); i++) { List<Variant> currentVariantList = variants.get(i); for (int j = 0; j < currentVariantList.size(); j++) { Variant currentVariant = currentVariantList.get(j); boolean valid = true; VCFFile file = contentManager.getFile(currentChromosome, currentVariant.getChromosomeContent()); for (MGFilter filter: filters) { if (filter instanceof VCFFilter) { VCFFilter currentFilter = (VCFFilter) filter; if (currentFilter.getVCFFile().equals(file)) { valid = currentFilter.isVariantValid(currentVariant.getReferencePositionIndex()); if (!valid) { break; } } } } byte display = SHOW; if (!valid) { if (showFilter) { display = SHOW_FILTER; } else { display = HIDE_FILTER; } } this.display[i][j] = display; } } } /** * Update the whole display policy array of the current list of variant to match defined settings about: * - showing the references * - showing the filtered variants * @param showReference true if the homozygote references have to be shown, false otherwise * @param showFilter true if the filtered variants have to be shown, false otherwise */ public void updateDisplayForOption (boolean showReference, boolean showFilter) { if ((variants.size() > 0) && (variants.get(0).size() > 0)) { int alleleNumber = variants.size(); int variantNumber = variants.get(0).size(); // For the reference option for (int i = 0; i < variantNumber; i++) { boolean isFullReference = true; for (int j = 0; j < alleleNumber; j++) { if (!(variants.get(j).get(i) instanceof ReferenceVariant)) { isFullReference = false; break; } } if (isFullReference) { for (int j = 0; j < alleleNumber; j++) { byte currentDisplay = display[j][i]; byte newDisplay = currentDisplay; if (showReference) { if (currentDisplay == HIDE_ALL) { newDisplay = HIDE_FILTER; } else if (currentDisplay == HIDE_REFERENCE) { newDisplay = SHOW; } } else { if (currentDisplay == SHOW) { newDisplay = HIDE_REFERENCE; } else if (currentDisplay == HIDE_FILTER) { newDisplay = HIDE_ALL; } } display[j][i] = newDisplay; } } } // For the filter option for (int allele = 0; allele < display.length; allele++) { for (int pos = 0; pos < display[allele].length; pos++) { byte currentDisplay = display[allele][pos]; byte newDisplay = currentDisplay; if (showFilter) { if (currentDisplay == HIDE_ALL) { newDisplay = HIDE_REFERENCE; } else if (currentDisplay == HIDE_FILTER) { newDisplay = SHOW_FILTER; } } else { if (currentDisplay == SHOW_FILTER) { newDisplay = HIDE_FILTER; }/* else if (currentDisplay == HIDE_REFERENCE) { newDisplay = HIDE_ALL; }*/ } display[allele][pos] = newDisplay; } } } } /** * Method used for serialization * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(SAVED_FORMAT_VERSION_NUMBER); out.writeObject(display); out.writeObject(genomeName); out.writeObject(types); } }