X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fdatamodel%2FAlignment.java;h=75f516442113b7978dd6db4bb3d7a5865afaa770;hb=c19d2a91ca05e052e3408bf5852d88eb5d0608f1;hp=83aab4e22e56df58b420f9171a00a0a9d0bcd407;hpb=59d682209891099d46b960509907c79e3fb276fe;p=jalview.git diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 83aab4e..75f5164 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -1,23 +1,38 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8) - * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2) + * Copyright (C) 2015 The Jalview Authors * * This file is part of Jalview. * * Jalview 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. + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. * * Jalview 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 Jalview. If not, see . + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.datamodel; -import java.util.*; +import jalview.analysis.AlignmentUtils; +import jalview.io.FastaFile; +import jalview.util.MessageManager; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; /** * Data structure to hold and manipulate a multiple sequence alignment @@ -52,6 +67,8 @@ public class Alignment implements AlignmentI public Hashtable alignmentProperties; + private Set codonFrameList = new LinkedHashSet(); + private void initAlignment(SequenceI[] seqs) { int i = 0; @@ -76,6 +93,27 @@ public class Alignment implements AlignmentI } /** + * Make a 'copy' alignment - sequences have new copies of features and + * annotations, but share the original dataset sequences. + */ + public Alignment(AlignmentI al) + { + SequenceI[] seqs = al.getSequencesArray(); + for (int i = 0; i < seqs.length; i++) + { + seqs[i] = new Sequence(seqs[i]); + } + + /* + * Share the same dataset sequence mappings (if any). TODO: find a better + * place for these to live (alignment dataset?). + */ + this.codonFrameList = ((Alignment) al).codonFrameList; + + initAlignment(seqs); + } + + /** * Make an alignment from an array of Sequences. * * @param sequences @@ -109,15 +147,12 @@ public class Alignment implements AlignmentI */ public static AlignmentI createAlignment(CigarArray compactAlignment) { - throw new Error("Alignment(CigarArray) not yet implemented"); + throw new Error( + MessageManager + .getString("error.alignment_cigararray_not_implemented")); // this(compactAlignment.refCigars); } - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ @Override public List getSequences() { @@ -137,7 +172,9 @@ public class Alignment implements AlignmentI public SequenceI[] getSequencesArray() { if (sequences == null) + { return null; + } synchronized (sequences) { return sequences.toArray(new SequenceI[sequences.size()]); @@ -145,6 +182,17 @@ public class Alignment implements AlignmentI } /** + * Returns a map of lists of sequences keyed by sequence name. + * + * @return + */ + @Override + public Map> getSequencesByName() + { + return AlignmentUtils.getSequencesByName(this); + } + + /** * DOCUMENT ME! * * @param i @@ -190,8 +238,7 @@ public class Alignment implements AlignmentI } if (sequences == null) { - initAlignment(new SequenceI[] - { snew }); + initAlignment(new SequenceI[] { snew }); } else { @@ -201,7 +248,9 @@ public class Alignment implements AlignmentI } } if (hiddenSequences != null) + { hiddenSequences.adjustHeightSequenceAdded(); + } } /** @@ -212,10 +261,9 @@ public class Alignment implements AlignmentI @Override public void setSequenceAt(int i, SequenceI snew) { - SequenceI oldseq = getSequenceAt(i); - deleteSequence(i); synchronized (sequences) { + deleteSequence(i); sequences.set(i, snew); } } @@ -235,7 +283,9 @@ public class Alignment implements AlignmentI public void finalize() { if (getDataset() != null) + { getDataset().removeAlignmentRef(); + } dataset = null; sequences = null; @@ -282,8 +332,8 @@ public class Alignment implements AlignmentI synchronized (sequences) { sequences.remove(i); + hiddenSequences.adjustHeightSequenceDeleted(i); } - hiddenSequences.adjustHeightSequenceDeleted(i); } } @@ -327,14 +377,14 @@ public class Alignment implements AlignmentI for (int i = 0; i < gSize; i++) { SequenceGroup sg = groups.get(i); - if (sg == null || sg.getSequences(null) == null) + if (sg == null || sg.getSequences() == null) { this.deleteGroup(sg); gSize--; continue; } - if (sg.getSequences(null).contains(s)) + if (sg.getSequences().contains(s)) { temp.add(sg); } @@ -370,7 +420,7 @@ public class Alignment implements AlignmentI return; } } - + sg.setContext(this); groups.add(sg); } } @@ -445,6 +495,10 @@ public class Alignment implements AlignmentI { removeAnnotationForGroup(null); } + for (SequenceGroup sg : groups) + { + sg.setContext(null); + } groups.clear(); } } @@ -459,6 +513,7 @@ public class Alignment implements AlignmentI { removeAnnotationForGroup(g); groups.remove(g); + g.setContext(null); } } } @@ -699,6 +754,28 @@ public class Alignment implements AlignmentI return true; } + /** + * Delete all annotations, including auto-calculated if the flag is set true. + * Returns true if at least one annotation was deleted, else false. + * + * @param includingAutoCalculated + * @return + */ + @Override + public boolean deleteAllAnnotations(boolean includingAutoCalculated) + { + boolean result = false; + for (AlignmentAnnotation alan : getAlignmentAnnotation()) + { + if (!alan.autoCalculated || includingAutoCalculated) + { + deleteAnnotation(alan); + result = true; + } + } + return result; + } + /* * (non-Javadoc) * @@ -739,7 +816,9 @@ public class Alignment implements AlignmentI continue; } if (tIndex < temp.length) + { temp[tIndex++] = annotations[i]; + } } if (swap) @@ -935,6 +1014,27 @@ public class Alignment implements AlignmentI else if (dataset == null && data != null) { dataset = data; + for (int i = 0; i < getHeight(); i++) + { + SequenceI currentSeq = getSequenceAt(i); + SequenceI dsq = currentSeq.getDatasetSequence(); + if (dsq == null) + { + dsq = currentSeq.createDatasetSequence(); + dataset.addSequence(dsq); + } + else + { + while (dsq.getDatasetSequence() != null) + { + dsq = dsq.getDatasetSequence(); + } + if (dataset.findIndex(dsq) == -1) + { + dataset.addSequence(dsq); + } + } + } } dataset.addAlignmentRef(); } @@ -1135,7 +1235,9 @@ public class Alignment implements AlignmentI public void setProperty(Object key, Object value) { if (alignmentProperties == null) + { alignmentProperties = new Hashtable(); + } alignmentProperties.put(key, value); } @@ -1144,9 +1246,13 @@ public class Alignment implements AlignmentI public Object getProperty(Object key) { if (alignmentProperties != null) + { return alignmentProperties.get(key); + } else + { return null; + } } @Override @@ -1155,8 +1261,6 @@ public class Alignment implements AlignmentI return alignmentProperties; } - AlignedCodonFrame[] codonFrameList = null; - /* * (non-Javadoc) * @@ -1167,62 +1271,55 @@ public class Alignment implements AlignmentI @Override public void addCodonFrame(AlignedCodonFrame codons) { - if (codons == null) - return; - if (codonFrameList == null) + if (codons != null) { - codonFrameList = new AlignedCodonFrame[] - { codons }; - return; + codonFrameList.add(codons); } - AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length + 1]; - System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length); - t[codonFrameList.length] = codons; - codonFrameList = t; } /* * (non-Javadoc) * - * @see jalview.datamodel.AlignmentI#getCodonFrame(int) + * @see + * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI) */ @Override - public AlignedCodonFrame getCodonFrame(int index) + public List getCodonFrame(SequenceI seq) { - return codonFrameList[index]; + if (seq == null) + { + return null; + } + List cframes = new ArrayList(); + for (AlignedCodonFrame acf : codonFrameList) + { + if (acf.involvesSequence(seq)) + { + cframes.add(acf); + } + } + return cframes; } - /* - * (non-Javadoc) + /** + * Sets the codon frame mappings (replacing any existing mappings). * - * @see - * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI) + * @see jalview.datamodel.AlignmentI#setCodonFrames() */ @Override - public AlignedCodonFrame[] getCodonFrame(SequenceI seq) + public void setCodonFrames(Set acfs) { - if (seq == null || codonFrameList == null) - return null; - Vector cframes = new Vector(); - for (int f = 0; f < codonFrameList.length; f++) - { - if (codonFrameList[f].involvesSequence(seq)) - cframes.addElement(codonFrameList[f]); - } - if (cframes.size() == 0) - return null; - AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()]; - cframes.copyInto(cfr); - return cfr; + this.codonFrameList = acfs; } - /* - * (non-Javadoc) + /** + * Returns the set of codon frame mappings. Any changes to the returned set + * will affect the alignment. * * @see jalview.datamodel.AlignmentI#getCodonFrames() */ @Override - public AlignedCodonFrame[] getCodonFrames() + public Set getCodonFrames() { return codonFrameList; } @@ -1237,27 +1334,10 @@ public class Alignment implements AlignmentI public boolean removeCodonFrame(AlignedCodonFrame codons) { if (codons == null || codonFrameList == null) - return false; - boolean removed = false; - int i = 0, iSize = codonFrameList.length; - while (i < iSize) { - if (codonFrameList[i] == codons) - { - removed = true; - if (i + 1 < iSize) - { - System.arraycopy(codonFrameList, i + 1, codonFrameList, i, iSize - - i - 1); - } - iSize--; - } - else - { - i++; - } + return false; } - return removed; + return codonFrameList.remove(codons); } @Override @@ -1302,11 +1382,9 @@ public class Alignment implements AlignmentI { addAnnotation(alan[a]); } - AlignedCodonFrame[] acod = toappend.getCodonFrames(); - for (int a = 0; acod != null && a < acod.length; a++) - { - this.addCodonFrame(acod[a]); - } + + this.codonFrameList.addAll(toappend.getCodonFrames()); + List sg = toappend.getGroups(); if (sg != null) { @@ -1385,13 +1463,17 @@ public class Alignment implements AlignmentI SequenceGroup groupRef) { assert (name != null); - for (AlignmentAnnotation annot : getAlignmentAnnotation()) + if (annotations != null) { - if (annot.autoCalculated == autoCalc && (name.equals(annot.label)) - && (calcId == null || annot.getCalcId().equals(calcId)) - && annot.sequenceRef == seqRef && annot.groupRef == groupRef) + for (AlignmentAnnotation annot : getAlignmentAnnotation()) { - return annot; + if (annot.autoCalculated == autoCalc && (name.equals(annot.label)) + && (calcId == null || annot.getCalcId().equals(calcId)) + && annot.sequenceRef == seqRef + && annot.groupRef == groupRef) + { + return annot; + } } } AlignmentAnnotation annot = new AlignmentAnnotation(name, name, @@ -1425,6 +1507,27 @@ public class Alignment implements AlignmentI return aa; } + /** + * Returns an iterable collection of any annotations that match on given + * sequence ref, calcId and label (ignoring null values). + */ + @Override + public Iterable findAnnotations(SequenceI seq, + String calcId, String label) + { + ArrayList aa = new ArrayList(); + for (AlignmentAnnotation ann : getAlignmentAnnotation()) + { + if (ann.getCalcId() != null && ann.getCalcId().equals(calcId) + && ann.sequenceRef != null && ann.sequenceRef == seq + && ann.label != null && ann.label.equals(label)) + { + aa.add(ann); + } + } + return aa; + } + @Override public void moveSelectedSequencesByOne(SequenceGroup sg, Map map, boolean up) @@ -1476,4 +1579,173 @@ public class Alignment implements AlignmentI } } + @Override + public void validateAnnotation(AlignmentAnnotation alignmentAnnotation) + { + alignmentAnnotation.validateRangeAndDisplay(); + if (isNucleotide() && alignmentAnnotation.isValidStruc()) + { + hasRNAStructure = true; + } + } + + private SequenceI seqrep = null; + + /** + * + * @return the representative sequence for this group + */ + public SequenceI getSeqrep() + { + return seqrep; + } + + /** + * set the representative sequence for this group. Note - this affects the + * interpretation of the Hidereps attribute. + * + * @param seqrep + * the seqrep to set (null means no sequence representative) + */ + public void setSeqrep(SequenceI seqrep) + { + this.seqrep = seqrep; + } + + /** + * + * @return true if group has a sequence representative + */ + public boolean hasSeqrep() + { + return seqrep != null; + } + + @Override + public int getEndRes() + { + return getWidth() - 1; + } + + @Override + public int getStartRes() + { + return 0; + } + + /* + * In the case of AlignmentI - returns the dataset for the alignment, if set + * (non-Javadoc) + * + * @see jalview.datamodel.AnnotatedCollectionI#getContext() + */ + @Override + public AnnotatedCollectionI getContext() + { + return dataset; + } + + /** + * Align this alignment like the given (mapped) one. + */ + @Override + public int alignAs(AlignmentI al) + { + /* + * Currently retains unmapped gaps (in introns), regaps mapped regions + * (exons) + */ + return alignAs(al, false, true); + } + + /** + * Align this alignment 'the same as' the given one. Mapped sequences only are + * realigned. If both of the same type (nucleotide/protein) then align both + * identically. If this is nucleotide and the other is protein, make 3 gaps + * for each gap in the protein sequences. If this is protein and the other is + * nucleotide, insert a gap for each 3 gaps (or part thereof) between + * nucleotide bases. If this is protein and the other is nucleotide, gaps + * protein to match the relative ordering of codons in the nucleotide. + * + * Parameters control whether gaps in exon (mapped) and intron (unmapped) + * regions are preserved. Gaps that connect introns to exons are treated + * conservatively, i.e. only preserved if both intron and exon gaps are + * preserved. + * + * @param al + * @param preserveMappedGaps + * if true, gaps within and between mapped codons are preserved + * @param preserveUnmappedGaps + * if true, gaps within and between unmapped codons are preserved + */ + // @Override + public int alignAs(AlignmentI al, boolean preserveMappedGaps, + boolean preserveUnmappedGaps) + { + // TODO should this method signature be the one in the interface? + int count = 0; + boolean thisIsNucleotide = this.isNucleotide(); + boolean thatIsProtein = !al.isNucleotide(); + if (!thatIsProtein && !thisIsNucleotide) + { + return AlignmentUtils.alignProteinAsDna(this, al); + } + + char thisGapChar = this.getGapCharacter(); + String gap = thisIsNucleotide && thatIsProtein ? String + .valueOf(new char[] { thisGapChar, thisGapChar, thisGapChar }) + : String.valueOf(thisGapChar); + + // TODO handle intron regions? Needs a 'holistic' alignment of dna, + // not just sequence by sequence. But how to 'gap' intron regions? + + /* + * Get mappings from 'that' alignment's sequences to this. + */ + for (SequenceI alignTo : getSequences()) + { + count += AlignmentUtils.alignSequenceAs(alignTo, al, gap, + preserveMappedGaps, preserveUnmappedGaps) ? 1 : 0; + } + return count; + } + + /** + * Returns the alignment in Fasta format. Behaviour of this method is not + * guaranteed between versions. + */ + @Override + public String toString() + { + return new FastaFile().print(getSequencesArray()); + } + + /** + * Returns the set of distinct sequence names. No ordering is guaranteed. + */ + @Override + public Set getSequenceNames() + { + Set names = new HashSet(); + for (SequenceI seq : getSequences()) + { + names.add(seq.getName()); + } + return names; + } + + @Override + public boolean hasValidSequence() + { + boolean hasValidSeq = false; + for (SequenceI seq : getSequences()) + { + if ((seq.getEnd() - seq.getStart()) > 0) + { + hasValidSeq = true; + break; + } + } + return hasValidSeq; + } }