/* * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9) * 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. * * 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 . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.datamodel; 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 */ /** * @author JimP * */ public class Alignment implements AlignmentI { protected Alignment dataset; protected List sequences; protected List groups = java.util.Collections .synchronizedList(new ArrayList()); protected char gapCharacter = '-'; protected int type = NUCLEOTIDE; public static final int PROTEIN = 0; public static final int NUCLEOTIDE = 1; public boolean hasRNAStructure = false; /** DOCUMENT ME!! */ public AlignmentAnnotation[] annotations; HiddenSequences hiddenSequences = new HiddenSequences(this); public Hashtable alignmentProperties; private Set codonFrameList = new LinkedHashSet(); private void initAlignment(SequenceI[] seqs) { int i = 0; if (jalview.util.Comparison.isNucleotide(seqs)) { type = NUCLEOTIDE; } else { type = PROTEIN; } sequences = java.util.Collections .synchronizedList(new ArrayList()); for (i = 0; i < seqs.length; i++) { sequences.add(seqs[i]); } } /** * 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 */ public Alignment(SequenceI[] seqs) { initAlignment(seqs); } /** * Make a new alignment from an array of SeqCigars * * @param seqs * SeqCigar[] */ public Alignment(SeqCigar[] alseqs) { SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter, new ColumnSelection(), null); initAlignment(seqs); } /** * Make a new alignment from an CigarArray JBPNote - can only do this when * compactAlignment does not contain hidden regions. JBPNote - must also check * that compactAlignment resolves to a set of SeqCigars - or construct them * appropriately. * * @param compactAlignment * CigarArray */ public static AlignmentI createAlignment(CigarArray compactAlignment) { throw new Error( MessageManager .getString("error.alignment_cigararray_not_implemented")); // this(compactAlignment.refCigars); } @Override public List getSequences() { return sequences; } @Override public List getSequences( Map hiddenReps) { // TODO: in jalview 2.8 we don't do anything with hiddenreps - fix design to // work on this. return sequences; } @Override public SequenceI[] getSequencesArray() { if (sequences == null) { return null; } synchronized (sequences) { return sequences.toArray(new SequenceI[sequences.size()]); } } /** * Returns a map of lists of sequences keyed by sequence name. * * @return */ @Override public Map> getSequencesByName() { return AlignmentUtils.getSequencesByName(this); } /** * DOCUMENT ME! * * @param i * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public SequenceI getSequenceAt(int i) { synchronized (sequences) { if (i > -1 && i < sequences.size()) { return sequences.get(i); } } return null; } /** * Adds a sequence to the alignment. Recalculates maxLength and size. * * @param snew */ @Override public void addSequence(SequenceI snew) { if (dataset != null) { // maintain dataset integrity if (snew.getDatasetSequence() != null) { getDataset().addSequence(snew.getDatasetSequence()); } else { // derive new sequence SequenceI adding = snew.deriveSequence(); getDataset().addSequence(adding.getDatasetSequence()); snew = adding; } } if (sequences == null) { initAlignment(new SequenceI[] { snew }); } else { synchronized (sequences) { sequences.add(snew); } } if (hiddenSequences != null) { hiddenSequences.adjustHeightSequenceAdded(); } } /** * Adds a sequence to the alignment. Recalculates maxLength and size. * * @param snew */ @Override public void setSequenceAt(int i, SequenceI snew) { synchronized (sequences) { deleteSequence(i); sequences.set(i, snew); } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public List getGroups() { return groups; } @Override public void finalize() { if (getDataset() != null) { getDataset().removeAlignmentRef(); } dataset = null; sequences = null; groups = null; annotations = null; hiddenSequences = null; } /** * decrement the alignmentRefs counter by one and call finalize if it goes to * zero. */ private void removeAlignmentRef() { if (--alignmentRefs == 0) { finalize(); } } /** * DOCUMENT ME! * * @param s * DOCUMENT ME! */ @Override public void deleteSequence(SequenceI s) { deleteSequence(findIndex(s)); } /** * DOCUMENT ME! * * @param i * DOCUMENT ME! */ @Override public void deleteSequence(int i) { if (i > -1 && i < getHeight()) { synchronized (sequences) { sequences.remove(i); hiddenSequences.adjustHeightSequenceDeleted(i); } } } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#findGroup(jalview.datamodel.SequenceI) */ @Override public SequenceGroup findGroup(SequenceI s) { synchronized (groups) { for (int i = 0; i < this.groups.size(); i++) { SequenceGroup sg = groups.get(i); if (sg.getSequences(null).contains(s)) { return sg; } } } return null; } /* * (non-Javadoc) * * @see * jalview.datamodel.AlignmentI#findAllGroups(jalview.datamodel.SequenceI) */ @Override public SequenceGroup[] findAllGroups(SequenceI s) { ArrayList temp = new ArrayList(); synchronized (groups) { int gSize = groups.size(); for (int i = 0; i < gSize; i++) { SequenceGroup sg = groups.get(i); if (sg == null || sg.getSequences() == null) { this.deleteGroup(sg); gSize--; continue; } if (sg.getSequences().contains(s)) { temp.add(sg); } } } SequenceGroup[] ret = new SequenceGroup[temp.size()]; return temp.toArray(ret); } /** */ @Override public void addGroup(SequenceGroup sg) { synchronized (groups) { if (!groups.contains(sg)) { if (hiddenSequences.getSize() > 0) { int i, iSize = sg.getSize(); for (i = 0; i < iSize; i++) { if (!sequences.contains(sg.getSequenceAt(i))) { sg.deleteSequence(sg.getSequenceAt(i), false); iSize--; i--; } } if (sg.getSize() < 1) { return; } } sg.setContext(this); groups.add(sg); } } } /** * remove any annotation that references gp * * @param gp * (if null, removes all group associated annotation) */ private void removeAnnotationForGroup(SequenceGroup gp) { if (annotations == null || annotations.length == 0) { return; } // remove annotation very quickly AlignmentAnnotation[] t, todelete = new AlignmentAnnotation[annotations.length], tokeep = new AlignmentAnnotation[annotations.length]; int i, p, k; if (gp == null) { for (i = 0, p = 0, k = 0; i < annotations.length; i++) { if (annotations[i].groupRef != null) { todelete[p++] = annotations[i]; } else { tokeep[k++] = annotations[i]; } } } else { for (i = 0, p = 0, k = 0; i < annotations.length; i++) { if (annotations[i].groupRef == gp) { todelete[p++] = annotations[i]; } else { tokeep[k++] = annotations[i]; } } } if (p > 0) { // clear out the group associated annotation. for (i = 0; i < p; i++) { unhookAnnotation(todelete[i]); todelete[i] = null; } t = new AlignmentAnnotation[k]; for (i = 0; i < k; i++) { t[i] = tokeep[i]; } annotations = t; } } @Override public void deleteAllGroups() { synchronized (groups) { if (annotations != null) { removeAnnotationForGroup(null); } for (SequenceGroup sg : groups) { sg.setContext(null); } groups.clear(); } } /** */ @Override public void deleteGroup(SequenceGroup g) { synchronized (groups) { if (groups.contains(g)) { removeAnnotationForGroup(g); groups.remove(g); g.setContext(null); } } } /** */ @Override public SequenceI findName(String name) { return findName(name, false); } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#findName(java.lang.String, boolean) */ @Override public SequenceI findName(String token, boolean b) { return findName(null, token, b); } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#findName(SequenceI, java.lang.String, * boolean) */ @Override public SequenceI findName(SequenceI startAfter, String token, boolean b) { int i = 0; SequenceI sq = null; String sqname = null; if (startAfter != null) { // try to find the sequence in the alignment boolean matched = false; while (i < sequences.size()) { if (getSequenceAt(i++) == startAfter) { matched = true; break; } } if (!matched) { i = 0; } } while (i < sequences.size()) { sq = getSequenceAt(i); sqname = sq.getName(); if (sqname.equals(token) // exact match || (b && // allow imperfect matches - case varies (sqname.equalsIgnoreCase(token)))) { return getSequenceAt(i); } i++; } return null; } @Override public SequenceI[] findSequenceMatch(String name) { Vector matches = new Vector(); int i = 0; while (i < sequences.size()) { if (getSequenceAt(i).getName().equals(name)) { matches.addElement(getSequenceAt(i)); } i++; } SequenceI[] result = new SequenceI[matches.size()]; for (i = 0; i < result.length; i++) { result[i] = (SequenceI) matches.elementAt(i); } return result; } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SequenceI) */ @Override public int findIndex(SequenceI s) { int i = 0; while (i < sequences.size()) { if (s == getSequenceAt(i)) { return i; } i++; } return -1; } /* * (non-Javadoc) * * @see * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults) */ @Override public int findIndex(SearchResults results) { int i = 0; while (i < sequences.size()) { if (results.involvesSequence(getSequenceAt(i))) { return i; } i++; } return -1; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public int getHeight() { return sequences.size(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public int getWidth() { int maxLength = -1; for (int i = 0; i < sequences.size(); i++) { if (getSequenceAt(i).getLength() > maxLength) { maxLength = getSequenceAt(i).getLength(); } } return maxLength; } /** * DOCUMENT ME! * * @param gc * DOCUMENT ME! */ @Override public void setGapCharacter(char gc) { gapCharacter = gc; synchronized (sequences) { for (SequenceI seq : sequences) { seq.setSequence(seq.getSequenceAsString().replace('.', gc) .replace('-', gc).replace(' ', gc)); } } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ @Override public char getGapCharacter() { return gapCharacter; } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#isAligned() */ @Override public boolean isAligned() { return isAligned(false); } /* * (non-Javadoc) * * @see jalview.datamodel.AlignmentI#isAligned(boolean) */ @Override public boolean isAligned(boolean includeHidden) { int width = getWidth(); if (hiddenSequences == null || hiddenSequences.getSize() == 0) { includeHidden = true; // no hidden sequences to check against. } for (int i = 0; i < sequences.size(); i++) { if (includeHidden || !hiddenSequences.isHidden(getSequenceAt(i))) { if (getSequenceAt(i).getLength() != width) { return false; } } } 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) * * @seejalview.datamodel.AlignmentI#deleteAnnotation(jalview.datamodel. * AlignmentAnnotation) */ @Override public boolean deleteAnnotation(AlignmentAnnotation aa) { return deleteAnnotation(aa, true); } @Override public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook) { int aSize = 1; if (annotations != null) { aSize = annotations.length; } if (aSize < 1) { return false; } AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1]; boolean swap = false; int tIndex = 0; for (int i = 0; i < aSize; i++) { if (annotations[i] == aa) { swap = true; continue; } if (tIndex < temp.length) { temp[tIndex++] = annotations[i]; } } if (swap) { annotations = temp; if (unhook) { unhookAnnotation(aa); } } return swap; } /** * remove any object references associated with this annotation * * @param aa */ private void unhookAnnotation(AlignmentAnnotation aa) { if (aa.sequenceRef != null) { aa.sequenceRef.removeAlignmentAnnotation(aa); } if (aa.groupRef != null) { // probably need to do more here in the future (post 2.5.0) aa.groupRef = null; } } /* * (non-Javadoc) * * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel. * AlignmentAnnotation) */ @Override public void addAnnotation(AlignmentAnnotation aa) { addAnnotation(aa, -1); } /* * (non-Javadoc) * * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel. * AlignmentAnnotation, int) */ @Override public void addAnnotation(AlignmentAnnotation aa, int pos) { if (aa.getRNAStruc() != null) { hasRNAStructure = true; } int aSize = 1; if (annotations != null) { aSize = annotations.length + 1; } AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize]; int i = 0; if (pos == -1 || pos >= aSize) { temp[aSize - 1] = aa; } else { temp[pos] = aa; } if (aSize > 1) { int p = 0; for (i = 0; i < (aSize - 1); i++, p++) { if (p == pos) { p++; } if (p < temp.length) { temp[p] = annotations[i]; } } } annotations = temp; } @Override public void setAnnotationIndex(AlignmentAnnotation aa, int index) { if (aa == null || annotations == null || annotations.length - 1 < index) { return; } int aSize = annotations.length; AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize]; temp[index] = aa; for (int i = 0; i < aSize; i++) { if (i == index) { continue; } if (i < index) { temp[i] = annotations[i]; } else { temp[i] = annotations[i - 1]; } } annotations = temp; } @Override /** * returns all annotation on the alignment */ public AlignmentAnnotation[] getAlignmentAnnotation() { return annotations; } @Override public void setNucleotide(boolean b) { if (b) { type = NUCLEOTIDE; } else { type = PROTEIN; } } @Override public boolean isNucleotide() { if (type == NUCLEOTIDE) { return true; } else { return false; } } @Override public boolean hasRNAStructure() { // TODO can it happen that structure is removed from alignment? return hasRNAStructure; } @Override public void setDataset(Alignment data) { if (dataset == null && data == null) { // Create a new dataset for this alignment. // Can only be done once, if dataset is not null // This will not be performed SequenceI[] seqs = new SequenceI[getHeight()]; SequenceI currentSeq; for (int i = 0; i < getHeight(); i++) { currentSeq = getSequenceAt(i); if (currentSeq.getDatasetSequence() != null) { seqs[i] = currentSeq.getDatasetSequence(); } else { seqs[i] = currentSeq.createDatasetSequence(); } } dataset = new Alignment(seqs); } 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(); } /** * reference count for number of alignments referencing this one. */ int alignmentRefs = 0; /** * increase reference count to this alignment. */ private void addAlignmentRef() { alignmentRefs++; } @Override public Alignment getDataset() { return dataset; } @Override public boolean padGaps() { boolean modified = false; // Remove excess gaps from the end of alignment int maxLength = -1; SequenceI current; for (int i = 0; i < sequences.size(); i++) { current = getSequenceAt(i); for (int j = current.getLength(); j > maxLength; j--) { if (j > maxLength && !jalview.util.Comparison.isGap(current.getCharAt(j))) { maxLength = j; break; } } } maxLength++; int cLength; for (int i = 0; i < sequences.size(); i++) { current = getSequenceAt(i); cLength = current.getLength(); if (cLength < maxLength) { current.insertCharAt(cLength, maxLength - cLength, gapCharacter); modified = true; } else if (current.getLength() > maxLength) { current.deleteChars(maxLength, current.getLength()); } } return modified; } /** * Justify the sequences to the left or right by deleting and inserting gaps * before the initial residue or after the terminal residue * * @param right * true if alignment padded to right, false to justify to left * @return true if alignment was changed */ @Override public boolean justify(boolean right) { boolean modified = false; // Remove excess gaps from the end of alignment int maxLength = -1; int ends[] = new int[sequences.size() * 2]; SequenceI current; for (int i = 0; i < sequences.size(); i++) { current = getSequenceAt(i); // This should really be a sequence method ends[i * 2] = current.findIndex(current.getStart()); ends[i * 2 + 1] = current.findIndex(current.getStart() + current.getLength()); boolean hitres = false; for (int j = 0, rs = 0, ssiz = current.getLength(); j < ssiz; j++) { if (!jalview.util.Comparison.isGap(current.getCharAt(j))) { if (!hitres) { ends[i * 2] = j; hitres = true; } else { ends[i * 2 + 1] = j; if (j - ends[i * 2] > maxLength) { maxLength = j - ends[i * 2]; } } } } } maxLength++; // now edit the flanking gaps to justify to either left or right int cLength, extent, diff; for (int i = 0; i < sequences.size(); i++) { current = getSequenceAt(i); cLength = 1 + ends[i * 2 + 1] - ends[i * 2]; diff = maxLength - cLength; // number of gaps to indent extent = current.getLength(); if (right) { // right justify if (extent > ends[i * 2 + 1]) { current.deleteChars(ends[i * 2 + 1] + 1, extent); modified = true; } if (ends[i * 2] > diff) { current.deleteChars(0, ends[i * 2] - diff); modified = true; } else { if (ends[i * 2] < diff) { current.insertCharAt(0, diff - ends[i * 2], gapCharacter); modified = true; } } } else { // left justify if (ends[i * 2] > 0) { current.deleteChars(0, ends[i * 2]); modified = true; ends[i * 2 + 1] -= ends[i * 2]; extent -= ends[i * 2]; } if (extent > maxLength) { current.deleteChars(maxLength + 1, extent); modified = true; } else { if (extent < maxLength) { current.insertCharAt(extent, maxLength - extent, gapCharacter); modified = true; } } } } return modified; } @Override public HiddenSequences getHiddenSequences() { return hiddenSequences; } @Override public CigarArray getCompactAlignment() { synchronized (sequences) { SeqCigar alseqs[] = new SeqCigar[sequences.size()]; int i = 0; for (SequenceI seq : sequences) { alseqs[i++] = new SeqCigar(seq); } CigarArray cal = new CigarArray(alseqs); cal.addOperation(CigarArray.M, getWidth()); return cal; } } @Override public void setProperty(Object key, Object value) { if (alignmentProperties == null) { alignmentProperties = new Hashtable(); } alignmentProperties.put(key, value); } @Override public Object getProperty(Object key) { if (alignmentProperties != null) { return alignmentProperties.get(key); } else { return null; } } @Override public Hashtable getProperties() { return alignmentProperties; } /* * (non-Javadoc) * * @see * jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame * ) */ @Override public void addCodonFrame(AlignedCodonFrame codons) { if (codons != null) { codonFrameList.add(codons); } } /* * (non-Javadoc) * * @see * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI) */ @Override public List getCodonFrame(SequenceI seq) { if (seq == null) { return null; } List cframes = new ArrayList(); for (AlignedCodonFrame acf : codonFrameList) { if (acf.involvesSequence(seq)) { cframes.add(acf); } } return cframes; } /** * Sets the codon frame mappings (replacing any existing mappings). * * @see jalview.datamodel.AlignmentI#setCodonFrames() */ @Override public void setCodonFrames(Set acfs) { this.codonFrameList = acfs; } /** * Returns the set of codon frame mappings. Any changes to the returned set * will affect the alignment. * * @see jalview.datamodel.AlignmentI#getCodonFrames() */ @Override public Set getCodonFrames() { return codonFrameList; } /* * (non-Javadoc) * * @seejalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel. * AlignedCodonFrame) */ @Override public boolean removeCodonFrame(AlignedCodonFrame codons) { if (codons == null || codonFrameList == null) { return false; } return codonFrameList.remove(codons); } @Override public void append(AlignmentI toappend) { if (toappend == this) { System.err.println("Self append may cause a deadlock."); } // TODO test this method for a future 2.5 release // currently tested for use in jalview.gui.SequenceFetcher boolean samegap = toappend.getGapCharacter() == getGapCharacter(); char oldc = toappend.getGapCharacter(); boolean hashidden = toappend.getHiddenSequences() != null && toappend.getHiddenSequences().hiddenSequences != null; // get all sequences including any hidden ones List sqs = (hashidden) ? toappend.getHiddenSequences() .getFullAlignment().getSequences() : toappend.getSequences(); if (sqs != null) { synchronized (sqs) { for (SequenceI addedsq : sqs) { if (!samegap) { char[] oldseq = addedsq.getSequence(); for (int c = 0; c < oldseq.length; c++) { if (oldseq[c] == oldc) { oldseq[c] = gapCharacter; } } } addSequence(addedsq); } } } AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation(); for (int a = 0; alan != null && a < alan.length; a++) { addAnnotation(alan[a]); } this.codonFrameList.addAll(toappend.getCodonFrames()); List sg = toappend.getGroups(); if (sg != null) { for (SequenceGroup _sg : sg) { addGroup(_sg); } } if (toappend.getHiddenSequences() != null) { HiddenSequences hs = toappend.getHiddenSequences(); if (hiddenSequences == null) { hiddenSequences = new HiddenSequences(this); } if (hs.hiddenSequences != null) { for (int s = 0; s < hs.hiddenSequences.length; s++) { // hide the newly appended sequence in the alignment if (hs.hiddenSequences[s] != null) { hiddenSequences.hideSequence(hs.hiddenSequences[s]); } } } } if (toappend.getProperties() != null) { // we really can't do very much here - just try to concatenate strings // where property collisions occur. Enumeration key = toappend.getProperties().keys(); while (key.hasMoreElements()) { Object k = key.nextElement(); Object ourval = this.getProperty(k); Object toapprop = toappend.getProperty(k); if (ourval != null) { if (ourval.getClass().equals(toapprop.getClass()) && !ourval.equals(toapprop)) { if (ourval instanceof String) { // append strings this.setProperty(k, ((String) ourval) + "; " + ((String) toapprop)); } else { if (ourval instanceof Vector) { // append vectors Enumeration theirv = ((Vector) toapprop).elements(); while (theirv.hasMoreElements()) { ((Vector) ourval).addElement(theirv); } } } } } else { // just add new property directly setProperty(k, toapprop); } } } } @Override public AlignmentAnnotation findOrCreateAnnotation(String name, String calcId, boolean autoCalc, SequenceI seqRef, SequenceGroup groupRef) { assert (name != null); if (annotations != null) { for (AlignmentAnnotation annot : getAlignmentAnnotation()) { 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, new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH); annot.hasText = false; annot.setCalcId(new String(calcId)); annot.autoCalculated = autoCalc; if (seqRef != null) { annot.setSequenceRef(seqRef); } annot.groupRef = groupRef; addAnnotation(annot); return annot; } @Override public Iterable findAnnotation(String calcId) { ArrayList aa = new ArrayList(); for (AlignmentAnnotation a : getAlignmentAnnotation()) { if (a.getCalcId() == calcId || (a.getCalcId() != null && calcId != null && a.getCalcId() .equals(calcId))) { aa.add(a); } } 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) { synchronized (sequences) { if (up) { for (int i = 1, iSize = sequences.size(); i < iSize; i++) { SequenceI seq = sequences.get(i); if (!sg.getSequences(map).contains(seq)) { continue; } SequenceI temp = sequences.get(i - 1); if (sg.getSequences(null).contains(temp)) { continue; } sequences.set(i, temp); sequences.set(i - 1, seq); } } else { for (int i = sequences.size() - 2; i > -1; i--) { SequenceI seq = sequences.get(i); if (!sg.getSequences(map).contains(seq)) { continue; } SequenceI temp = sequences.get(i + 1); if (sg.getSequences(map).contains(temp)) { continue; } sequences.set(i, temp); sequences.set(i + 1, seq); } } } } @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; } }