import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
public class AnnotationSorter
{
/**
+ * A special comparator for use when reloading from project, that supports
+ * reordering annotations to match their order in the project. It incorporates
+ * special handling of autocalculated annotations, that are recreated on load:
+ * <ul>
+ * <li>these are matched based on autocalc flag and label ("Consensus") etc
+ * rather than object identity</li>
+ * <li>on successful match, visibility and height settings are applied from
+ * those saved in the project</li>
+ * </ul>
+ */
+ private final class ReloadComparator
+ implements Comparator<AlignmentAnnotation>
+ {
+ private List<AlignmentAnnotation> order;
+
+ ReloadComparator(List<AlignmentAnnotation> theOrder)
+ {
+ order = theOrder;
+ }
+
+ @Override
+ public int compare(AlignmentAnnotation o1,
+ AlignmentAnnotation o2)
+ {
+ int i1 = findPosition(o1);
+ int i2 = findPosition(o2);
+ return Integer.compare(i1, i2);
+ }
+
+ /**
+ * A helper method that returns the position of the annotation in the
+ * {@code order} list, by object identity, or failing that, by matched
+ * autocalc label ("Consensus" etc). Returns -1 if not found. This can
+ * happen if, for example, Consensus was saved but since turned off in
+ * Preferences.
+ *
+ * @param aa
+ * @return
+ */
+ private int findPosition(AlignmentAnnotation aa)
+ {
+ int i = order.indexOf(aa);
+ if (i == -1 && aa.autoCalculated && aa.label != null)
+ {
+ for (int j = 0; j < order.size(); j++)
+ {
+ AlignmentAnnotation ann = order.get(j);
+ if (aa.label.equals(ann.label))
+ {
+ i = j;
+ aa.visible = ann.visible;
+ if (ann.graphHeight >= 0)
+ {
+ aa.graphHeight = ann.graphHeight;
+ }
+ break;
+ }
+ }
+ }
+ return i;
+ }
+ }
+
+ /**
* enum for annotation sort options. The text description is used in the
* Preferences drop-down options. The enum name is saved in the preferences
* file.
}
return Integer.compare(index1.intValue(), index2.intValue());
}
+
+ /**
+ * Sort annotations to match the order of the provided list. This is intended
+ * only for use when reloading a project, in order to set the saved order
+ * after constructing a viewport (which might sort differently based on user
+ * preferences for sort order).
+ * <p>
+ * There is some special handling specific to auto-calculated annotations.
+ * These are saved in the project, to preserve their visibility and height
+ * properties, but are recalculated when the viewport is reconstructed.
+ * <ul>
+ * <li>Autocalculated annotations are matched to the list by label
+ * "Consensus/Quality/Conservation/Occupancy"</li>
+ * <li>those that are matched have their visibility and height set</li>
+ * </ul>
+ *
+ * @param addedAnnotation
+ */
+ public void sort(List<AlignmentAnnotation> annotations)
+ {
+ Arrays.sort(alignment.getAlignmentAnnotation(),
+ new ReloadComparator(annotations));
+ }
}
import static jalview.math.RotatableMatrix.Axis.Y;
import static jalview.math.RotatableMatrix.Axis.Z;
+import jalview.analysis.AnnotationSorter;
import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
import jalview.analysis.Conservation;
import jalview.analysis.PCA;
// ////////////////////////////////
// LOAD ANNOTATIONS
- List<JvAnnotRow> autoAlan = new ArrayList<>();
+ List<AlignmentAnnotation> addedAnnotation = new ArrayList<>();
/*
* store any annotations which forward reference a group's ID
}
}
}
- jalview.datamodel.AlignmentAnnotation jaa = null;
+
+ /*
+ * construct the Jalview AlignmentAnnotation, add to alignment
+ */
+ AlignmentAnnotation jaa = null;
if (annotation.isGraph())
{
jaa.autoCalculated = true; // means annotation will be marked for
// update at end of load.
}
- if (annotation.getGraphHeight() != null)
+ Integer graphHeight = annotation.getGraphHeight();
+ if (graphHeight != null)
{
- jaa.graphHeight = annotation.getGraphHeight().intValue();
+ jaa.graphHeight = graphHeight.intValue();
}
jaa.belowAlignment = annotation.isBelowAlignment();
jaa.setCalcId(annotation.getCalcId());
jaa.setProperty(prop.getName(), prop.getValue());
}
}
- if (jaa.autoCalculated)
- {
- autoAlan.add(new JvAnnotRow(i, jaa));
- }
- else
- // if (!autoForView)
+ if (!jaa.autoCalculated)
{
- // add autocalculated group annotation and any user created annotation
- // for the view
+ // TODO ensure Consensus etc is enabled if found in project?
+ /*
+ * add autocalculated group annotation and any user created annotation
+ * for the view
+ */
al.addAnnotation(jaa);
}
+ addedAnnotation.add(jaa);
}
}
// ///////////////////////
if (isnewview)
{
af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
- uniqueSeqSetId, viewId, autoAlan);
- // TODO resort annotations here to their order in the project?
+ uniqueSeqSetId, viewId);
+ // TODO restore autocalc preferences if overridden earlier?
+ /*
+ * resort annotations to their order in the project
+ * (also sets height and visibility for autocalc'd annotation)
+ */
av = af.getViewport();
+ new AnnotationSorter(av).sort(addedAnnotation);
ap = af.alignPanel;
+ ap.adjustAnnotationHeight();
}
/*
AlignFrame loadViewport(String file, List<JSeq> JSEQ,
List<SequenceI> hiddenSeqs, AlignmentI al,
JalviewModel jm, Viewport view, String uniqueSeqSetId,
- String viewId, List<JvAnnotRow> autoAlan)
+ String viewId)
{
AlignFrame af = null;
af = new AlignFrame(al, safeInt(view.getWidth()),
safeInt(view.getWidth()), safeInt(view.getHeight()));
// recompute any autoannotation
af.alignPanel.updateAnnotation(false, true);
- reorderAutoannotation(af, al, autoAlan);
- af.sortAnnotations(false);
af.alignPanel.alignmentChanged();
}
else
return cs;
}
- private void reorderAutoannotation(AlignFrame af, AlignmentI al,
- List<JvAnnotRow> autoAlan)
- {
- // copy over visualization settings for autocalculated annotation in the
- // view
- if (al.getAlignmentAnnotation() != null)
- {
- /**
- * Kludge for magic autoannotation names (see JAL-811)
- */
- String[] magicNames = new String[] { "Consensus", "Quality",
- "Conservation" };
- JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
- Hashtable<String, JvAnnotRow> visan = new Hashtable<>();
- for (String nm : magicNames)
- {
- visan.put(nm, nullAnnot);
- }
- for (JvAnnotRow auan : autoAlan)
- {
- visan.put(auan.template.label
- + (auan.template.getCalcId() == null ? ""
- : "\t" + auan.template.getCalcId()),
- auan);
- }
- int hSize = al.getAlignmentAnnotation().length;
- List<JvAnnotRow> reorder = new ArrayList<>();
- // work through any autoCalculated annotation already on the view
- // removing it if it should be placed in a different location on the
- // annotation panel.
- List<String> remains = new ArrayList<>(visan.keySet());
- for (int h = 0; h < hSize; h++)
- {
- jalview.datamodel.AlignmentAnnotation jalan = al
- .getAlignmentAnnotation()[h];
- if (jalan.autoCalculated)
- {
- String k;
- JvAnnotRow valan = visan.get(k = jalan.label);
- if (jalan.getCalcId() != null)
- {
- valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
- }
-
- if (valan != null)
- {
- // delete the auto calculated row from the alignment
- al.deleteAnnotation(jalan, false);
- remains.remove(k);
- hSize--;
- h--;
- if (valan != nullAnnot)
- {
- if (jalan != valan.template)
- {
- // newly created autoannotation row instance
- // so keep a reference to the visible annotation row
- // and copy over all relevant attributes
- if (valan.template.graphHeight >= 0)
-
- {
- jalan.graphHeight = valan.template.graphHeight;
- }
- jalan.visible = valan.template.visible;
- }
- reorder.add(new JvAnnotRow(valan.order, jalan));
- }
- }
- }
- }
- // Add any (possibly stale) autocalculated rows that were not appended to
- // the view during construction
- for (String other : remains)
- {
- JvAnnotRow othera = visan.get(other);
- if (othera != nullAnnot && othera.template.getCalcId() != null
- && othera.template.getCalcId().length() > 0)
- {
- reorder.add(othera);
- }
- }
- // now put the automatic annotation in its correct place
- int s = 0, srt[] = new int[reorder.size()];
- JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
- for (JvAnnotRow jvar : reorder)
- {
- rws[s] = jvar;
- srt[s++] = jvar.order;
- }
- reorder.clear();
- jalview.util.QuickSort.sort(srt, rws);
- // and re-insert the annotation at its correct position
- for (JvAnnotRow jvar : rws)
- {
- al.addAnnotation(jvar.template, jvar.order);
- }
- af.alignPanel.adjustAnnotationHeight();
- }
- }
-
Hashtable skipList = null;
/**
import jalview.gui.AlignViewport;
import jalview.gui.JvOptionPane;
+import java.util.Arrays;
+import java.util.List;
import java.util.Random;
import org.testng.annotations.BeforeClass;
av.setShowAutocalculatedAbove(false);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("label5", anns[0].label); // for sequence 0
assertEquals("label0", anns[1].label); // for sequence 1
assertEquals("iron", anns[2].label); // sequence 3 /iron
av.setShowAutocalculatedAbove(true);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("Quality", anns[0].label); // autocalc annotations
assertEquals("Consensus", anns[1].label); // retain ordering
assertEquals("label5", anns[2].label); // for sequence 0
av.setShowAutocalculatedAbove(false);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("IRON", anns[0].label); // IRON / sequence 0
assertEquals("iron", anns[1].label); // iron / sequence 3
assertEquals("label0", anns[2].label); // label0 / sequence 1
av.setShowAutocalculatedAbove(true);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("Quality", anns[0].label); // autocalc annotations
assertEquals("Consensus", anns[1].label); // retain ordering
assertEquals("IRON", anns[2].label); // IRON / sequence 0
av.setShowAutocalculatedAbove(true);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.NONE, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("Quality", anns[0].label); // autocalc annotations
assertEquals("Consensus", anns[1].label); // retain ordering
assertEquals("label0", anns[2].label);
av.setShowAutocalculatedAbove(true);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.CUSTOM, false);
+ anns = al.getAlignmentAnnotation();
assertEquals("label0", anns[0].label); // all unchanged
assertEquals("structure", anns[1].label);
assertEquals("iron", anns[2].label);
av.setShowAutocalculatedAbove(true);
AnnotationSorter testee = new AnnotationSorter(av);
testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, true);
+ anns = al.getAlignmentAnnotation();
assertEquals("Quality", anns[0].label); // moved to top
assertEquals("Consensus", anns[1].label); // moved to top
assertEquals("label0", anns[2].label); // the rest unchanged
assertEquals("Quality", anns[5].label); // moved to bottom
assertEquals("Consensus", anns[6].label); // moved to bottom
}
+
+ /**
+ * Test sorting by annotation order
+ */
+ @Test(groups = { "Functional" })
+ public void testSortByAnnotation()
+ {
+ AlignmentI al = av.getAlignment();
+ AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+
+ // @formatter:off
+ anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
+ anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
+ anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
+ anns[3].autoCalculated = true; anns[3].label = "Quality";
+ anns[4].autoCalculated = true; anns[4].label = "Consensus";
+ anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
+ anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
+ // @formatter:on
+
+ av.setShowAutocalculatedAbove(false);
+ AnnotationSorter testee = new AnnotationSorter(av);
+ List<AlignmentAnnotation> reorder = Arrays.asList(anns[2], anns[6],
+ anns[3], anns[0], anns[5], anns[1], anns[4]);
+ testee.sort(reorder);
+
+ /*
+ * should now be ordered as specified by the list
+ */
+ anns = al.getAlignmentAnnotation();
+ assertEquals("iron", anns[0].label);
+ assertEquals("IRP", anns[1].label);
+ assertEquals("Quality", anns[2].label);
+ assertEquals("label0", anns[3].label);
+ assertEquals("label5", anns[4].label);
+ assertEquals("structure", anns[5].label);
+ assertEquals("Consensus", anns[6].label);
+ }
}