+
+ /**
+ * Returns true if a cDNA/Protein mapping either exists, or could be made,
+ * between at least one pair of sequences in the two alignments. Currently,
+ * the logic is:
+ * <ul>
+ * <li>One alignment must be nucleotide, and the other protein</li>
+ * <li>At least one pair of sequences must be already mapped, or mappable</li>
+ * <li>Mappable means the nucleotide translation matches the protein sequence</li>
+ * <li>The translation may ignore start and stop codons if present in the
+ * nucleotide</li>
+ * </ul>
+ *
+ * @param al1
+ * @param al2
+ * @return
+ */
+ public static boolean isMappable(AlignmentI al1, AlignmentI al2)
+ {
+ /*
+ * Require one nucleotide and one protein
+ */
+ if (al1.isNucleotide() == al2.isNucleotide())
+ {
+ return false;
+ }
+ AlignmentI dna = al1.isNucleotide() ? al1 : al2;
+ AlignmentI protein = dna == al1 ? al2 : al1;
+ Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+ for (SequenceI dnaSeq : dna.getSequences())
+ {
+ for (SequenceI proteinSeq : protein.getSequences())
+ {
+ if (isMappable(dnaSeq, proteinSeq, mappings))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the dna sequence is mapped, or could be mapped, to the
+ * protein sequence.
+ *
+ * @param dnaSeq
+ * @param proteinSeq
+ * @param mappings
+ * @return
+ */
+ public static boolean isMappable(SequenceI dnaSeq, SequenceI proteinSeq,
+ Set<AlignedCodonFrame> mappings)
+ {
+ SequenceI dnaDs = dnaSeq.getDatasetSequence() == null ? dnaSeq : dnaSeq.getDatasetSequence();
+ SequenceI proteinDs = proteinSeq.getDatasetSequence() == null ? proteinSeq
+ : proteinSeq.getDatasetSequence();
+
+ /*
+ * Already mapped?
+ */
+ for (AlignedCodonFrame mapping : mappings) {
+ if ( proteinDs == mapping.getAaForDnaSeq(dnaDs)) {
+ return true;
+ }
+ }
+
+ /*
+ * Just try to make a mapping (it is not yet stored), test whether
+ * successful.
+ */
+ return mapProteinToCdna(proteinDs, dnaDs) != null;
+ }
+
+ /**
+ * Finds any reference annotations associated with the sequences in
+ * sequenceScope, that are not already added to the alignment, and adds them
+ * to the 'candidates' map. Also populates a lookup table of annotation
+ * labels, keyed by calcId, for use in constructing tooltips or the like.
+ *
+ * @param sequenceScope
+ * the sequences to scan for reference annotations
+ * @param labelForCalcId
+ * (optional) map to populate with label for calcId
+ * @param candidates
+ * map to populate with annotations for sequence
+ * @param al
+ * the alignment to check for presence of annotations
+ */
+ public static void findAddableReferenceAnnotations(
+ List<SequenceI> sequenceScope, Map<String, String> labelForCalcId,
+ final Map<SequenceI, List<AlignmentAnnotation>> candidates,
+ AlignmentI al)
+ {
+ if (sequenceScope == null)
+ {
+ return;
+ }
+
+ /*
+ * For each sequence in scope, make a list of any annotations on the
+ * underlying dataset sequence which are not already on the alignment.
+ *
+ * Add to a map of { alignmentSequence, <List of annotations to add> }
+ */
+ for (SequenceI seq : sequenceScope)
+ {
+ SequenceI dataset = seq.getDatasetSequence();
+ if (dataset == null)
+ {
+ continue;
+ }
+ AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
+ if (datasetAnnotations == null)
+ {
+ continue;
+ }
+ final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+ for (AlignmentAnnotation dsann : datasetAnnotations)
+ {
+ /*
+ * Find matching annotations on the alignment. If none is found, then
+ * add this annotation to the list of 'addable' annotations for this
+ * sequence.
+ */
+ final Iterable<AlignmentAnnotation> matchedAlignmentAnnotations = al
+ .findAnnotations(seq, dsann.getCalcId(),
+ dsann.label);
+ if (!matchedAlignmentAnnotations.iterator().hasNext())
+ {
+ result.add(dsann);
+ if (labelForCalcId != null)
+ {
+ labelForCalcId.put(dsann.getCalcId(), dsann.label);
+ }
+ }
+ }
+ /*
+ * Save any addable annotations for this sequence
+ */
+ if (!result.isEmpty())
+ {
+ candidates.put(seq, result);
+ }
+ }
+ }
+
+ /**
+ * Adds annotations to the top of the alignment annotations, in the same order
+ * as their related sequences.
+ *
+ * @param annotations
+ * the annotations to add
+ * @param alignment
+ * the alignment to add them to
+ * @param selectionGroup
+ * current selection group (or null if none)
+ */
+ public static void addReferenceAnnotations(
+ Map<SequenceI, List<AlignmentAnnotation>> annotations,
+ final AlignmentI alignment, final SequenceGroup selectionGroup)
+ {
+ for (SequenceI seq : annotations.keySet())
+ {
+ for (AlignmentAnnotation ann : annotations.get(seq))
+ {
+ AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
+ int startRes = 0;
+ int endRes = ann.annotations.length;
+ if (selectionGroup != null)
+ {
+ startRes = selectionGroup.getStartRes();
+ endRes = selectionGroup.getEndRes();
+ }
+ copyAnn.restrict(startRes, endRes);
+
+ /*
+ * Add to the sequence (sets copyAnn.datasetSequence), unless the
+ * original annotation is already on the sequence.
+ */
+ if (!seq.hasAnnotation(ann))
+ {
+ seq.addAlignmentAnnotation(copyAnn);
+ }
+ // adjust for gaps
+ copyAnn.adjustForAlignment();
+ // add to the alignment and set visible
+ alignment.addAnnotation(copyAnn);
+ copyAnn.visible = true;
+ }
+ }
+ }
+
+ /**
+ * Set visibility of alignment annotations of specified types (labels), for
+ * specified sequences. This supports controls like
+ * "Show all secondary structure", "Hide all Temp factor", etc.
+ *
+ * @al the alignment to scan for annotations
+ * @param types
+ * the types (labels) of annotations to be updated
+ * @param forSequences
+ * if not null, only annotations linked to one of these sequences are
+ * in scope for update; if null, acts on all sequence annotations
+ * @param anyType
+ * if this flag is true, 'types' is ignored (label not checked)
+ * @param doShow
+ * if true, set visibility on, else set off
+ */
+ public static void showOrHideSequenceAnnotations(AlignmentI al,
+ Collection<String> types, List<SequenceI> forSequences,
+ boolean anyType, boolean doShow)
+ {
+ for (AlignmentAnnotation aa : al
+ .getAlignmentAnnotation())
+ {
+ if (anyType || types.contains(aa.label))
+ {
+ if ((aa.sequenceRef != null)
+ && (forSequences == null || forSequences
+ .contains(aa.sequenceRef)))
+ {
+ aa.visible = doShow;
+ }
+ }
+ }
+ }