From c7bc2d0abe568732fd9249c56b98370944a6e43f Mon Sep 17 00:00:00 2001 From: Jim Procter Date: Thu, 26 Sep 2019 13:27:55 +0100 Subject: [PATCH] JAL-3070 JAL-3066 AlignmentI.findOrAddAnnotation(AlignmentAnnotation ala) - calls copy constructor or the AlignmentAnnotation.updateAlignmentAnnotationFrom method to sync alignment annotation with annotation generated by a service --- src/jalview/analysis/AlignmentAnnotationUtils.java | 38 +++++++++++++ src/jalview/datamodel/Alignment.java | 56 +++++++++++++------- src/jalview/datamodel/AlignmentAnnotation.java | 29 ++++++++++ src/jalview/datamodel/AlignmentI.java | 11 ++++ .../datamodel/AlignmentAnnotationTests.java | 5 ++ test/jalview/datamodel/AlignmentTest.java | 28 ++++++++++ 6 files changed, 147 insertions(+), 20 deletions(-) diff --git a/src/jalview/analysis/AlignmentAnnotationUtils.java b/src/jalview/analysis/AlignmentAnnotationUtils.java index f5626ce..194ef80 100644 --- a/src/jalview/analysis/AlignmentAnnotationUtils.java +++ b/src/jalview/analysis/AlignmentAnnotationUtils.java @@ -239,4 +239,42 @@ public class AlignmentAnnotationUtils return (anns == null ? Collections. emptyList() : Arrays.asList(anns)); } + + /** + * replace an existing sequence associated annotation with another, creating + * association as necessary. + * + * @param newAnnot + * - annotation row associated with an alignment sequence to be + * propagated to its dataset sequence. + * @param typeName + * - label used to match existing row + * @param calcId + * - calcId for existing row + */ + public static void replaceAnnotationOnAlignmentWith( + AlignmentAnnotation newAnnot, String typeName, String calcId) + { + if (newAnnot.sequenceRef != null) + { + SequenceI dsseq = newAnnot.sequenceRef.getDatasetSequence(); + while (dsseq.getDatasetSequence() != null) + { + dsseq = dsseq.getDatasetSequence(); + } + // look for same annotation on dataset and lift this one over + List dsan = dsseq.getAlignmentAnnotations(calcId, + typeName); + if (dsan != null && dsan.size() > 0) + { + for (AlignmentAnnotation dssan : dsan) + { + dsseq.removeAlignmentAnnotation(dssan); + } + } + AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot); + dsseq.addAlignmentAnnotation(dssan); + dssan.adjustForAlignment(); + } + } } diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 70a3cc2..d124960 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -1628,34 +1628,50 @@ public class Alignment implements AlignmentI, AutoCloseable String calcId, boolean autoCalc, SequenceI seqRef, SequenceGroup groupRef) { - if (annotations != null) + AlignmentAnnotation annot = annotations == null ? null + : AlignmentAnnotation.findFirstAnnotation( + Arrays.asList(getAlignmentAnnotation()), name, calcId, + autoCalc, seqRef, groupRef); + + if (annot == null) { - for (AlignmentAnnotation annot : getAlignmentAnnotation()) + + annot = new AlignmentAnnotation(name, name, new Annotation[1], 0f, 0f, + AlignmentAnnotation.BAR_GRAPH); + annot.hasText = false; + if (calcId != null) { - if (annot.autoCalculated == autoCalc && (name.equals(annot.label)) - && (calcId == null || annot.getCalcId().equals(calcId)) - && annot.sequenceRef == seqRef - && annot.groupRef == groupRef) - { - return annot; - } + annot.setCalcId(calcId); + } + annot.autoCalculated = autoCalc; + if (seqRef != null) + { + annot.setSequenceRef(seqRef); } + annot.groupRef = groupRef; + addAnnotation(annot); } - AlignmentAnnotation annot = new AlignmentAnnotation(name, name, - new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH); - annot.hasText = false; - if (calcId != null) + return annot; + } + + + @Override + public AlignmentAnnotation updateFromOrCopyAnnotation( + AlignmentAnnotation ala) + { + AlignmentAnnotation annot = AlignmentAnnotation.findFirstAnnotation( + Arrays.asList(getAlignmentAnnotation()), ala.label, ala.calcId, + ala.autoCalculated, ala.sequenceRef, ala.groupRef); + if (annot == null) { - annot.setCalcId(calcId); + annot = new AlignmentAnnotation(ala); + addAnnotation(annot); } - annot.autoCalculated = autoCalc; - if (seqRef != null) + else { - annot.setSequenceRef(seqRef); + annot.updateAlignmentAnnotationFrom(ala); } - annot.groupRef = groupRef; - addAnnotation(annot); - + validateAnnotation(annot); return annot; } diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index 3e532df..4ba0ac4 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -710,6 +710,17 @@ public class AlignmentAnnotation public AlignmentAnnotation(AlignmentAnnotation annotation) { setAnnotationId(); + updateAlignmentAnnotationFrom(annotation); + } + + /** + * copy attributes and annotation from an existing annotation (used by copy + * constructor). This method does not update the unique annotationId + * + * @param annotation + */ + public void updateAlignmentAnnotationFrom(AlignmentAnnotation annotation) + { this.label = new String(annotation.label); if (annotation.description != null) { @@ -1741,4 +1752,22 @@ public class AlignmentAnnotation this.eValue = eValue; } + public static AlignmentAnnotation findFirstAnnotation( + Iterable alignmentAnnotation, String name, + String calcId, boolean autoCalc, SequenceI seqRef, + SequenceGroup groupRef) + { + + for (AlignmentAnnotation annot : alignmentAnnotation) + { + if (annot.autoCalculated == autoCalc && (name.equals(annot.label)) + && (calcId == null || annot.getCalcId().equals(calcId)) + && annot.sequenceRef == seqRef && annot.groupRef == groupRef) + { + return annot; + } + } + return null; + } + } diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index 7386d4b..cea1c5b 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -525,6 +525,17 @@ public interface AlignmentI extends AnnotatedCollectionI boolean autoCalc, SequenceI seqRef, SequenceGroup groupRef); /** + * like findOrCreateAnnotation - looks for an existing alignment annotation + * row with matching name, calcId, sequenceRef, groupRef and autoCalculated + * flag and updates it from the annotation. If none is found the annotation is + * added directly. + * + * @param ala + * @return ala or the annotation row that was updated. + */ + AlignmentAnnotation updateFromOrCopyAnnotation(AlignmentAnnotation ala); + + /** * move the given group up or down in the alignment by the given number of * rows. Implementor assumes given group is already present on alignment - no * recalculations are triggered. diff --git a/test/jalview/datamodel/AlignmentAnnotationTests.java b/test/jalview/datamodel/AlignmentAnnotationTests.java index 19a725e..ade9150 100644 --- a/test/jalview/datamodel/AlignmentAnnotationTests.java +++ b/test/jalview/datamodel/AlignmentAnnotationTests.java @@ -49,6 +49,11 @@ public class AlignmentAnnotationTests createAnnotation(sq); AlignmentAnnotation alc, alo = sq.getAnnotation()[0]; alc = new AlignmentAnnotation(alo); + + // TODO: this only tests string equals (which is unreliable), should use + // refactored tests from StockholmFileTest + Assert.assertEquals(alc.toString(), alo.toString()); + for (String key : alo.getProperties()) { assertEquals("Property mismatch", alo.getProperty(key), diff --git a/test/jalview/datamodel/AlignmentTest.java b/test/jalview/datamodel/AlignmentTest.java index fa73aba..6dad024 100644 --- a/test/jalview/datamodel/AlignmentTest.java +++ b/test/jalview/datamodel/AlignmentTest.java @@ -1354,6 +1354,34 @@ public class AlignmentTest assertEquals("", ala.getCalcId()); } + @Test(groups = {"Functional"}) + public void testUpdateFromOrAddAnnotation() + { + SequenceI seq = new Sequence("seq1", "FRMLPSRT-A--L-"); + AlignmentI alignment = new Alignment(new SequenceI[] { seq }); + + AlignmentAnnotation ala = alignment.findOrCreateAnnotation( + "Temperature Factor", null, false, seq, null); + + assertNotNull(ala); + assertEquals(seq, ala.sequenceRef); + assertEquals("", ala.getCalcId()); + + // Assuming findOrCreateForNullCalcId passed then this should work + + assertTrue(ala == alignment.updateFromOrCopyAnnotation(ala)); + AlignmentAnnotation updatedAla = new AlignmentAnnotation(ala); + updatedAla.description = "updated Description"; + Assert.assertTrue( + ala == alignment.updateFromOrCopyAnnotation(updatedAla)); + Assert.assertEquals(ala.toString(), updatedAla.toString()); + updatedAla.calcId = "newCalcId"; + AlignmentAnnotation newUpdatedAla = alignment + .updateFromOrCopyAnnotation(updatedAla); + Assert.assertTrue(updatedAla != newUpdatedAla); + Assert.assertEquals(updatedAla.toString(), newUpdatedAla.toString()); + } + @Test(groups = "Functional") public void testPropagateInsertions() { -- 1.7.10.2