+ /**
+ * Adds the given mapping to the stored set. Note this may be held on the
+ * dataset alignment.
+ */
+ @Override
+ public void addCodonFrame(AlignedCodonFrame codons)
+ {
+ List<AlignedCodonFrame> acfs = getCodonFrames();
+ if (codons != null && acfs != null && !acfs.contains(codons))
+ {
+ acfs.add(codons);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
+ */
+ @Override
+ public List<AlignedCodonFrame> getCodonFrame(SequenceI seq)
+ {
+ if (seq == null)
+ {
+ return null;
+ }
+ List<AlignedCodonFrame> cframes = new ArrayList<AlignedCodonFrame>();
+ for (AlignedCodonFrame acf : getCodonFrames())
+ {
+ if (acf.involvesSequence(seq))
+ {
+ cframes.add(acf);
+ }
+ }
+ return cframes;
+ }
+
+ /**
+ * Sets the codon frame mappings (replacing any existing mappings). Note the
+ * mappings are set on the dataset alignment instead if there is one.
+ *
+ * @see jalview.datamodel.AlignmentI#setCodonFrames()
+ */
+ @Override
+ public void setCodonFrames(List<AlignedCodonFrame> acfs)
+ {
+ if (dataset != null)
+ {
+ dataset.setCodonFrames(acfs);
+ }
+ else
+ {
+ this.codonFrameList = acfs;
+ }
+ }
+
+ /**
+ * Returns the set of codon frame mappings. Any changes to the returned set
+ * will affect the alignment. The mappings are held on (and read from) the
+ * dataset alignment if there is one.
+ *
+ * @see jalview.datamodel.AlignmentI#getCodonFrames()
+ */
+ @Override
+ public List<AlignedCodonFrame> getCodonFrames()
+ {
+ // TODO: Fix this method to fix failing AlignedCodonFrame tests
+ // this behaviour is currently incorrect. method should return codon frames
+ // for just the alignment,
+ // selected from dataset
+ return dataset != null ? dataset.getCodonFrames() : codonFrameList;
+ }
+
+ /**
+ * Removes the given mapping from the stored set. Note that the mappings are
+ * held on the dataset alignment if there is one.
+ */
+ @Override
+ public boolean removeCodonFrame(AlignedCodonFrame codons)
+ {
+ List<AlignedCodonFrame> acfs = getCodonFrames();
+ if (codons == null || acfs == null)
+ {
+ return false;
+ }
+ return acfs.remove(codons);
+ }
+
+ @Override
+ public void append(AlignmentI toappend)
+ {
+ // TODO JAL-1270 needs test coverage
+ // 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<SequenceI> sqs = (hashidden) ? toappend.getHiddenSequences()
+ .getFullAlignment().getSequences() : toappend.getSequences();
+ if (sqs != null)
+ {
+ // avoid self append deadlock by
+ List<SequenceI> toappendsq = new ArrayList<SequenceI>();
+ 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;
+ }
+ }
+ }
+ toappendsq.add(addedsq);
+ }
+ }
+ for (SequenceI addedsq : toappendsq)
+ {
+ addSequence(addedsq);
+ }
+ }
+ AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
+ for (int a = 0; alan != null && a < alan.length; a++)
+ {
+ addAnnotation(alan[a]);
+ }
+
+ // use add method
+ getCodonFrames().addAll(toappend.getCodonFrames());
+
+ List<SequenceGroup> 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<AlignmentAnnotation> findAnnotation(String calcId)
+ {
+ ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+ 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<AlignmentAnnotation> findAnnotations(SequenceI seq,
+ String calcId, String label)
+ {
+ ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+ 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<SequenceI, SequenceCollectionI> 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
+ */
+ @Override
+ 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)
+ */
+ @Override
+ public void setSeqrep(SequenceI seqrep)
+ {
+ this.seqrep = seqrep;
+ }
+
+ /**
+ *
+ * @return true if group has a sequence representative
+ */
+ @Override
+ 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. TODO: check caveats below where the implementation fails
+ *
+ * @param al
+ * - must have same dataset, and sequences in al must have equivalent
+ * dataset sequence and start/end bounds under given mapping
+ * @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?
+ boolean thisIsNucleotide = this.isNucleotide();
+ boolean thatIsProtein = !al.isNucleotide();
+ if (!thatIsProtein && !thisIsNucleotide)
+ {
+ return AlignmentUtils.alignProteinAsDna(this, al);
+ }
+ else if (thatIsProtein && thisIsNucleotide)
+ {
+ return AlignmentUtils.alignCdsAsProtein(this, al);
+ }
+ return AlignmentUtils.alignAs(this, al);
+ }
+
+ /**
+ * 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<String> getSequenceNames()
+ {
+ Set<String> names = new HashSet<String>();
+ 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;
+ }
+
+ /**
+ * Update any mappings to 'virtual' sequences to compatible real ones, if
+ * present in the added sequences. Returns a count of mappings updated.
+ *
+ * @param seqs
+ * @return
+ */
+ @Override
+ public int realiseMappings(List<SequenceI> seqs)
+ {
+ int count = 0;
+ for (SequenceI seq : seqs)
+ {
+ for (AlignedCodonFrame mapping : getCodonFrames())
+ {
+ count += mapping.realiseWith(seq);
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns the first AlignedCodonFrame that has a mapping between the given
+ * dataset sequences
+ *
+ * @param mapFrom
+ * @param mapTo
+ * @return
+ */
+ @Override
+ public AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo)
+ {
+ for (AlignedCodonFrame acf : getCodonFrames())
+ {
+ if (acf.getAaForDnaSeq(mapFrom) == mapTo)
+ {
+ return acf;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols)
+ {
+ int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 };
+ int startPos = alignmentStartEnd[0];
+ int endPos = alignmentStartEnd[1];
+
+ int[] lowestRange = new int[] { -1, -1 };
+ int[] higestRange = new int[] { -1, -1 };
+
+ for (int[] hiddenCol : hiddenCols)
+ {
+ lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+ higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+ }
+
+ if (lowestRange[0] == -1 && lowestRange[1] == -1)
+ {
+ startPos = alignmentStartEnd[0];
+ }
+ else
+ {
+ startPos = lowestRange[1] + 1;
+ }
+
+ if (higestRange[0] == -1 && higestRange[1] == -1)
+ {
+ endPos = alignmentStartEnd[1];
+ }
+ else
+ {
+ endPos = higestRange[0] - 1;
+ }
+ return new int[] { startPos, endPos };
+ }