/******************************************************************************* * 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.gui.track.layer.variantLayer; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import edu.yu.einstein.genplay.core.comparator.VariantComparator; import edu.yu.einstein.genplay.core.multiGenome.data.display.VariantDisplayList; import edu.yu.einstein.genplay.core.multiGenome.data.display.VariantDisplayMultiListScanner; import edu.yu.einstein.genplay.core.multiGenome.data.display.variant.MixVariant; import edu.yu.einstein.genplay.core.multiGenome.data.display.variant.Variant; import edu.yu.einstein.genplay.core.multiGenome.data.display.variant.VariantDisplay; import edu.yu.einstein.genplay.dataStructure.chromosome.Chromosome; import edu.yu.einstein.genplay.dataStructure.genomeWindow.GenomeWindow; /** * @author Nicolas Fourel * @version 0.1 */ public class MultiGenomeListHandler implements Serializable { /** Generated serial version ID */ private static final long serialVersionUID = -7976375676020159462L; private static final int SAVED_FORMAT_VERSION_NUMBER = 0; // saved format version private List<VariantDisplayList> variantList; private List<List<Variant>> fullList; private List<List<VariantDisplay>> fittedList; private CacheTrack<List<List<VariantDisplay>>> cache; private Chromosome fittedChromosome; // Chromosome with the adapted data private Double fittedXRatio; // xRatio of the adapted data (ie ratio between the number of pixel and the number of base to display ) /** * Constructor of {@link MultiGenomeListHandler} */ public MultiGenomeListHandler () { cache = new CacheTrack<List<List<VariantDisplay>>>(); fittedChromosome = null; fittedXRatio = null; } /** * Recursive function. Returns the index where the start value of the window is found or the index right after if the exact value is not find. * @param list * @param value * @param indexStart * @param indexStop * @return the index where the start value of the window is found or the index right after if the exact value is not find */ private int findStart(List<VariantDisplay> list, int value, int indexStart, int indexStop) { int middle = (indexStop - indexStart) / 2; if (indexStart == indexStop) { return indexStart; } else if (value == list.get(indexStart + middle).getVariant().getStart()) { return indexStart + middle; } else if (value > list.get(indexStart + middle).getVariant().getStart()) { return findStart(list, value, indexStart + middle + 1, indexStop); } else { return findStart(list, value, indexStart, indexStart + middle); } } /** * Recursive function. Returns the index where the stop value of the window is found or the index right before if the exact value is not find. * @param list * @param value * @param indexStart * @param indexStop * @return the index where the stop value of the window is found or the index right before if the exact value is not find */ private int findStop(List<VariantDisplay> list, int value, int indexStart, int indexStop) { int middle = (indexStop - indexStart) / 2; if (indexStart == indexStop) { return indexStart; } else if (value == list.get(indexStart + middle).getVariant().getStop()) { return indexStart + middle; } else if (value > list.get(indexStart + middle).getVariant().getStop()) { return findStop(list, value, indexStart + middle + 1, indexStop); } else { return findStop(list, value, indexStart, indexStart + middle); } } /** * Merges two windows together if the gap between this two windows is not visible */ public void fitToScreen() { fittedList = new ArrayList<List<VariantDisplay>>(); fittedList.add(new ArrayList<VariantDisplay>()); fittedList.add(new ArrayList<VariantDisplay>()); fitToScreen(0); fitToScreen(1); } ///////////////////////////////////////////////////////////////////// Interface methods /** * Merges two windows together if the gap between this two windows is not visible */ private void fitToScreen (int allele) { VariantDisplayMultiListScanner scanner = new VariantDisplayMultiListScanner(variantList); scanner.initializeOneAllele(allele); scanner.setDisplayDependancy(true); if (fittedXRatio > 1) { List<Variant> firstVariants = scanner.getCurrentVariants(); if (firstVariants.size() > 0) { Variant variant = firstVariants.get(0); VariantDisplayList list = scanner.getCurrentVariantDisplayList(variant); int index = scanner.getCurrentVariantIndex(variant); fittedList.get(allele).add(new VariantDisplay(list, variant, allele, index)); } while (scanner.hasNext()) { List<Variant> variants = scanner.next(); Variant variant = variants.get(0); VariantDisplayList list = scanner.getCurrentVariantDisplayList(variant); int index = scanner.getCurrentVariantIndex(variant); fittedList.get(allele).add(new VariantDisplay(list, variant, allele, index)); } } else { // Initialize the first position int start = -1; int stop = -1; Variant currentVariant = null; int previousIndex = -1; int currentIndex = -1; VariantDisplayList previousList = null; VariantDisplayList currentList = null; // Insert first variant List<Variant> currentVariants = scanner.getCurrentVariants(); if (currentVariants.size() > 0) { currentVariant = currentVariants.get(0); } else { if (scanner.hasNext()) { currentVariant = scanner.next().get(0); // Get the next position } } if (currentVariant != null) { start = currentVariant.getStart(); stop = currentVariant.getStop(); currentIndex = scanner.getCurrentVariantIndex(currentVariant); currentList = scanner.getCurrentVariantDisplayList(currentVariant); } // Process the next positions Variant previousVariant = null; boolean hasToMerge = false; while (scanner.hasNext()) { previousIndex = currentIndex; previousList = currentList; previousVariant = currentVariant; currentVariant = scanner.next().get(0); // Get the next position currentIndex = scanner.getCurrentVariantIndex(currentVariant); currentList = scanner.getCurrentVariantDisplayList(currentVariant); double distance = (currentVariant.getStart() - stop) * fittedXRatio; // Compare its distance with the previous position if (distance < 1) { hasToMerge = true; stop = currentVariant.getStop(); } else { // Insert previous information VariantDisplay variantDisplay = null; if (hasToMerge) { hasToMerge = false; variantDisplay = new VariantDisplay(null, new MixVariant(start, stop), allele, -1); } else { variantDisplay = new VariantDisplay(previousList, previousVariant, allele, previousIndex); } if (variantDisplay != null) { fittedList.get(allele).add(variantDisplay); } // Update current information start = currentVariant.getStart(); stop = currentVariant.getStop(); } } // Handle the last case VariantDisplay variantDisplay = null; if (hasToMerge) { variantDisplay = new VariantDisplay(null, new MixVariant(start, stop), allele, -1); } else { if (currentVariant != null) { variantDisplay = new VariantDisplay(currentList, currentVariant, allele, currentIndex); } } if (variantDisplay != null) { fittedList.get(allele).add(variantDisplay); } } } /** * Forces the fitToScreen method to be performed * (Used after a change of options that didn't required the full list of {@link Variant} to change) * @param xRatio */ public void forceFitToScreen(double xRatio) { fittedXRatio = xRatio; fitToScreen(); cache.setData(fittedXRatio, fittedList); } /** * @param window the genome window * @param xRatio the x ratio * @param allele the allele index * @return the variant list that fits the screen */ public final List<VariantDisplay> getFittedData(GenomeWindow window, double xRatio, int allele) { boolean hasToFit = false; if ((fittedChromosome == null) || (!fittedChromosome.equals(window.getChromosome()))) { fittedChromosome = window.getChromosome(); if ((fittedXRatio == null) || (fittedXRatio != xRatio)) { fittedXRatio = xRatio; } hasToFit = true; } else if ((fittedXRatio == null) || (fittedXRatio != xRatio)) { fittedXRatio = xRatio; hasToFit = true; } if (hasToFit) { if (cache.hasData(xRatio)) { fittedList = cache.getData(xRatio); } else { fitToScreen(); cache.setData(fittedXRatio, fittedList); } } List<VariantDisplay> result = getFittedData(window.getStart(), window.getStop(), allele); return result; } protected List<VariantDisplay> getFittedData(int start, int stop, int allele) { if ((fittedList == null) || (fittedList.size() == 0) || (fittedList.get(allele).size() == 0)) { return null; } ArrayList<VariantDisplay> resultList = new ArrayList<VariantDisplay>(); int indexStart = findStart(fittedList.get(allele), start, 0, fittedList.get(allele).size() - 1); int indexStop = findStop(fittedList.get(allele), stop, 0, fittedList.get(allele).size() - 1); if (indexStart > 0) { VariantDisplay variant = fittedList.get(allele).get(indexStart - 1); if (variant.getVariant().getStop() >= start) { resultList.add(variant); } } for (int i = indexStart; i <= indexStop; i++) { if (i == indexStop) { VariantDisplay variant = fittedList.get(allele).get(indexStop); if (variant.getVariant().getStart() <= stop) { resultList.add(variant); } } else { resultList.add(fittedList.get(allele).get(i)); } } if ((indexStop + 1) < fittedList.get(allele).size()) { VariantDisplay variant = fittedList.get(allele).get(indexStop + 1); if (variant.getVariant().getStart() <= stop) { resultList.add(variant); } } return resultList; } /** * Initializes the list of {@link Variant} * @param variantList */ public void initialize (List<VariantDisplayList> variantList) { cache.initialize(); if (variantList == null) { variantList = new ArrayList<VariantDisplayList>(); } this.variantList = variantList; fullList = new ArrayList<List<Variant>>(); fullList.add(new ArrayList<Variant>()); fullList.add(new ArrayList<Variant>()); for (VariantDisplayList current: variantList) { fullList.get(0).addAll(current.getVariants().get(0)); fullList.get(1).addAll(current.getVariants().get(1)); } Collections.sort(fullList.get(0), new VariantComparator()); Collections.sort(fullList.get(1), new VariantComparator()); } /** * Method used for unserialization * @param in * @throws IOException * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.readInt(); variantList = (List<VariantDisplayList>) in.readObject(); fullList = (List<List<Variant>>) in.readObject(); fittedList = (List<List<VariantDisplay>>) in.readObject(); fittedChromosome = (Chromosome) in.readObject(); fittedXRatio = in.readDouble(); cache = new CacheTrack<List<List<VariantDisplay>>>(); } /** * Method used for serialization * @param out * @throws IOException */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeInt(SAVED_FORMAT_VERSION_NUMBER); out.writeObject(variantList); out.writeObject(fullList); out.writeObject(fittedList); out.writeObject(fittedChromosome); if (fittedXRatio == null) { out.writeDouble(0.0); } else { out.writeDouble(fittedXRatio); } } ///////////////////////////////////////////////////////////////////// }