/* * BioJava development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public Licence. This should * be distributed with the code. If you do not have a copy, * see: * * http://www.gnu.org/copyleft/lesser.html * * Copyright for this code is held jointly by the individual * authors. These should be listed in @author doc comments. * * For more information on the BioJava project and its aims, * or to join the biojava-l mailing list, visit the home page * at: * * http://www.biojava.org/ * */ package org.biojava.bio.alignment; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.TreeSet; import java.util.Vector; import org.biojava.bio.BioError; import org.biojava.bio.BioException; import org.biojava.bio.symbol.Alphabet; import org.biojava.bio.symbol.AlphabetManager; import org.biojava.bio.symbol.Edit; import org.biojava.bio.symbol.GappedSymbolList; import org.biojava.bio.symbol.IllegalSymbolException; import org.biojava.bio.symbol.Location; import org.biojava.bio.symbol.RangeLocation; import org.biojava.bio.symbol.SimpleGappedSymbolList; import org.biojava.bio.symbol.Symbol; import org.biojava.bio.symbol.SymbolList; import org.biojava.utils.ChangeEvent; import org.biojava.utils.ChangeSupport; import org.biojava.utils.ChangeVetoException; /** *

FlexibleAlignment is a class which implements * UnequalLengthAlignment, ARAlignment and EditableAlignment It * places no restriction on where any sequence can be in the alignment * so there could be gaps in the alignment. You tell it where to put * the sequence, it will do it. I think I will be adding an Exception * NonContinuousAlignmentException. STILL UNDER * CONSTRUCTION. seqString does not work because there it does not * seem to support tokenization 'token' this is true for * SimpleAlignment too. * * @author David Waring * @author Matthew Pocock */ public class FlexibleAlignment extends AbstractULAlignment implements ARAlignment, EditableAlignment{ protected Map data; protected List labelOrder; protected Location alignmentRange; List alphaList = new ArrayList(); /** * construct this object with the reference sequence which can either be a gappedSymbolList or not * label in all cases refers to an object that holds the display name (generally just a String). * since more than one sequence in an alignment could have the same name this works as long as the * labels are different objects even though they may hold the same name. */ public FlexibleAlignment (List seqList)throws BioException{ data = new Hashtable(); labelOrder = new Vector(); alignmentRange = new RangeLocation(1,1); int k=0; // go through the list make sure that all seqs are GappedSymbolLists for (Iterator i = seqList.iterator();i.hasNext();){ Object element = i.next(); if (!(element instanceof AlignmentElement)){ throw new BioException("All elements of seqList must be AlignmentElements "); } AlignmentElement ae = (AlignmentElement)element; Object label = ae.getLabel(); Location loc = ae.getLoc(); SymbolList seq = ae.getSymbolList(); alphaList.add(seq.getAlphabet()); if (! (seq instanceof GappedSymbolList)){ seq = new SimpleGappedSymbolList(seq); ae = new SimpleAlignmentElement(label,seq,loc); } data.put(label,ae); labelOrder.add(label); int min = lesser(alignmentRange.getMin(),loc.getMin()); int max = greater(alignmentRange.getMax(),loc.getMax()); alignmentRange = new RangeLocation(min,max); k++; } this.alphabet = AlphabetManager.getCrossProductAlphabet(alphaList); try { resetRange(); }catch (ChangeVetoException e){ throw new BioError("Should not have a problem here"); } } private int getOrder(Object label) throws Exception{ for (int i=0; ithe EDIT is a static final variable of type ChangeType in SymbolList interface cevt = new ChangeEvent(this, ARAlignment.ADD_LABEL, label); cs = getChangeSupport(ARAlignment.ADD_LABEL); // let the listeners know what we want to do cs.firePreChangeEvent(cevt); if (! (seq instanceof GappedSymbolList)){ seq = new SimpleGappedSymbolList(seq); ae = new SimpleAlignmentElement(label,seq,loc); } data.put(label,ae); labelOrder.add(label); alphaList.add(seq.getAlphabet()); this.alphabet = AlphabetManager.getCrossProductAlphabet(alphaList); int min = lesser(alignmentRange.getMin(),loc.getMin()); int max = greater(alignmentRange.getMax(),loc.getMax()); alignmentRange = new RangeLocation(min,max); resetRange(); cs.firePostChangeEvent(cevt); } public synchronized void removeSequence(Object label) throws ChangeVetoException{ ChangeSupport cs; ChangeEvent cevt; // give the listeners a change to veto this // create a new change event ->the EDIT is a static final variable of type ChangeType in SymbolList interface cevt = new ChangeEvent(this, ARAlignment.REMOVE_LABEL, label); cs = getChangeSupport(ARAlignment.REMOVE_LABEL); // let the listeners know what we want to do cs.firePreChangeEvent(cevt); try{ alphaList.remove(getOrder(label)); this.alphabet = AlphabetManager.getCrossProductAlphabet(alphaList); } catch (Throwable e){ e.printStackTrace(); } data.remove(label); labelOrder.remove(label); resetRange(); cs.firePostChangeEvent(cevt); } ///////////////////////// // methods from Interface UnequalLengthAlignment ///////////////////////// /** * The location of an individual SymbolList relative to overall Alignment */ public Location locInAlignment(Object label)throws NoSuchElementException{ return getAE(label).getLoc(); } public List getLabelsAt(int column) throws IndexOutOfBoundsException { if (column < 1 || column > this.length()){ throw new IndexOutOfBoundsException(); } List labelList = new ArrayList(); Location loc; Object label; for (Iterator labelIterator = data.keySet().iterator();labelIterator.hasNext();){ label = labelIterator.next(); loc = getAE(label).getLoc(); if (loc.contains(column)){ labelList.add(label); } } return labelList; } ///////////////////////// // methods from Interface Alignment //////////////////////// public synchronized int length(){ return alignmentRange.getMax() - alignmentRange.getMin() + 1; } public Alphabet getAlphabet(){ return alphabet; } /** * getLabels will return a list of labels in left to right order */ public synchronized List getLabels(){ TreeSet sorted = new TreeSet(new LeftRightLocationComparator()); sorted.addAll(labelOrder); return new Vector(sorted); } /** * This gets the symbol for an individual sequence at position in the overall alignment * If the sequence is not aligned at that location it returns null */ public synchronized Symbol symbolAt(Object label, int column) throws NoSuchElementException,IndexOutOfBoundsException{ SymbolList seq = symbolListForLabel(label); int cloc = posInSeq(label,column); Symbol symbol = null; //debug (label.toString() + " " + column + ":" + cloc); if (seq == null){ // debug("seq is null"); } try { symbol = seq.symbolAt(cloc); }catch (IndexOutOfBoundsException e){ // leave symbol == null } return symbol; } public synchronized SymbolList symbolListForLabel(Object label) throws NoSuchElementException{ return getAE(label).getSymbolList(); } // methods from interface EditableAlignment public synchronized void edit(Object label,Edit edit) throws ChangeVetoException{ throw new BioError("Not implemented yet"); } /** * loc in this case is the Alignment Location */ public synchronized void shiftAtAlignmentLoc(Object label, Location loc, int offset) throws ChangeVetoException,IllegalAlignmentEditException,IndexOutOfBoundsException{ Location sourceLoc = locInSeq(label,loc); shiftAtSequenceLoc(label,sourceLoc,offset); } /** * loc in this case is the SymbolList Location */ public synchronized void shiftAtSequenceLoc(Object label, Location loc, int offset) throws ChangeVetoException,IllegalAlignmentEditException,IndexOutOfBoundsException{ ChangeSupport csgap; ChangeEvent cegap; ChangeSupport csloc; ChangeEvent celoc; celoc = new ChangeEvent(this, EditableAlignment.LOCATION, label); csloc = getChangeSupport(EditableAlignment.LOCATION); cegap = new ChangeEvent(this, EditableAlignment.GAPS, label); csgap = getChangeSupport(EditableAlignment.GAPS); int caseValue = 0; int absOffset = Math.abs(offset); Location seqLoc = locInAlignment(label); AlignmentElement ae = getAE(label); SymbolList seq = ae.getSymbolList(); Location newLoc; int min = loc.getMin(); int max = loc.getMax(); if (min < 1 || max > seq.length()){ throw new IndexOutOfBoundsException(); } if (offset == 0) { return; } if (offset > 1) caseValue += 1; if (min == 1) caseValue += 2; if (max == seq.length()) caseValue += 4; switch(caseValue){ case 0 : // internal shift to left if (! allGaps(seq,min + offset, min - 1)){ throw new IllegalAlignmentEditException (); } csgap.firePreChangeEvent(cegap); ((GappedSymbolList)seq).addGapsInView(max + 1, absOffset); removeGaps((GappedSymbolList)seq, min - absOffset, absOffset); csgap.firePostChangeEvent(cegap); break; case 1: // internal shift to right if (! allGaps(seq,max + 1 , max + offset)){ throw new IllegalAlignmentEditException (); } csgap.firePreChangeEvent(cegap); removeGaps((GappedSymbolList)seq, max + 1, offset); ((GappedSymbolList)seq).addGapsInView(min ,offset); csgap.firePostChangeEvent(cegap); break; case 2 : // left end shift to left csgap.firePreChangeEvent(cegap); csloc.firePreChangeEvent(celoc); ((GappedSymbolList)seq).addGapsInView(max + 1,absOffset); newLoc = new RangeLocation(seqLoc.getMin() - absOffset, seqLoc.getMax()); ae.setLoc(newLoc); resetRange(); csloc.firePostChangeEvent(celoc); csgap.firePostChangeEvent(cegap); break; case 3 : // left end shift to right if (! allGaps(seq,max + 1 , max + offset)){ throw new IllegalAlignmentEditException (); } csgap.firePreChangeEvent(cegap); csloc.firePreChangeEvent(celoc); removeGaps((GappedSymbolList)seq, max + 1, offset); newLoc = new RangeLocation(seqLoc.getMin() + offset,seqLoc.getMax()); ae.setLoc(newLoc); resetRange(); csloc.firePostChangeEvent(celoc); csgap.firePostChangeEvent(cegap); break; case 4 : // right end shift to left if (! allGaps(seq,min - absOffset, min - 1)){ throw new IllegalAlignmentEditException (); } csgap.firePreChangeEvent(cegap); csloc.firePreChangeEvent(celoc); removeGaps((GappedSymbolList)seq, min - absOffset , absOffset); newLoc = new RangeLocation(seqLoc.getMin(),seqLoc.getMax() + offset); ae.setLoc(newLoc); resetRange(); csloc.firePostChangeEvent(celoc); csgap.firePostChangeEvent(cegap); break; case 5 : // right end shift to right csgap.firePreChangeEvent(cegap); csloc.firePreChangeEvent(celoc); ((GappedSymbolList)seq).addGapsInView(min ,offset); newLoc = new RangeLocation(seqLoc.getMin(), seqLoc.getMax() + offset); ae.setLoc(newLoc); resetRange(); csloc.firePostChangeEvent(celoc); csgap.firePostChangeEvent(cegap); break; case 6 : // whole seq shift to left debug("Shifting all to left " + absOffset); shift (label,offset); break; case 7 : // whole seq shift to right debug("Shifting all to right " + absOffset); shift (label,offset); break; default : debug("OOOPS something is wrong " + loc.toString() + " " + absOffset); return; } } /** * because there is a bug in GappedSymbolList */ public synchronized void removeGaps(GappedSymbolList seq,int start, int length){ try{ // seq.removeGaps (start , length); // because there is a bug in GappedSymbolList we do it one at a time for (int i = 1; i <= length;i++){ seq.removeGap(start); } }catch (IllegalSymbolException e){ throw new BioError("We should have tested for this already"); } } /** * make sure that all Symbols in this range are gaps */ protected synchronized boolean allGaps(SymbolList seq,int start, int end){ Symbol gs = seq.getAlphabet().getGapSymbol(); for (int i = start; i <= end; i++){ if (!(seq.symbolAt(i).equals(gs))){ return false; } } return true; } /** * check that begining is at 1 otherwise shift everything over */ protected synchronized void resetRange()throws ChangeVetoException{ int min = 0;// just for the compiler int max = 0;// just for the compiler int lMin; int lMax; int count = 1; // get the current range from all labels for (Iterator i = getLabels().iterator();i.hasNext();){ Object label = i.next(); lMin = locInAlignment(label).getMin(); lMax = locInAlignment(label).getMax(); if (count == 1){ min = lMin; }else{ min = lesser(min , lMin); } if (count == 1){ max = lMax; }else{ max = greater(max , lMax); } count++; } alignmentRange = new RangeLocation(min,max); if (min != 1){ int offset = 1 - alignmentRange.getMin(); shiftAll(offset); alignmentRange = new RangeLocation(alignmentRange.getMin() + offset, alignmentRange.getMax() + offset); } } protected synchronized void shiftAll(int offset)throws ChangeVetoException{ List lList = getLabels(); for (Iterator i= lList.iterator();i.hasNext();){ Object label = i.next(); shift (label,offset); } } /** * moves the whole sequence */ protected synchronized void shift(Object label,int offset)throws ChangeVetoException{ ChangeSupport csloc; ChangeEvent celoc; celoc = new ChangeEvent(this, EditableAlignment.LOCATION, label); csloc = getChangeSupport(EditableAlignment.LOCATION); Location oLoc = locInAlignment(label); Location nLoc = new RangeLocation(oLoc.getMin() + offset,oLoc.getMax() + offset); csloc.firePreChangeEvent(celoc); debug("shifting " + label.toString()); getAE(label).setLoc(nLoc); resetRange(); debug("shifted " + label); csloc.firePostChangeEvent(celoc); } // utility methods protected int greater(int x, int y){ int greatest = (x > y) ? x : y; return greatest; } protected int lesser(int x, int y){ int least = (x < y) ? x : y; return least; } protected AlignmentElement getAE(Object label) throws NoSuchElementException{ if (!(data.containsKey(label))); return (AlignmentElement)data.get(label); } /** * get the position in the sequence corresponding to the postion within the alignment */ protected synchronized int posInSeq(Object label,int column)throws NoSuchElementException, IndexOutOfBoundsException { if (column < 1 || column > this.length()){ throw new IndexOutOfBoundsException(); } Location loc = locInAlignment(label); return (column - loc.getMin() + 1); } protected synchronized Location locInSeq(Object label,Location viewLoc) throws NoSuchElementException, IndexOutOfBoundsException { int min = posInSeq(label,viewLoc.getMin()); int max = posInSeq(label,viewLoc.getMax()); return new RangeLocation(min,max); } }