package jalview.analysis;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.SequenceI;
import jalview.renderer.AnnotationRenderer;
public class AlignmentAnnotationUtils
{
/**
* Helper method to populate lists of annotation types for the Show/Hide
* Annotations menus. If sequenceGroup is not null, this is restricted to
* annotations which are associated with sequences in the selection group.
*
* If an annotation row is currently visible, its type (label) is added (once
* only per type), to the shownTypes list. If it is currently hidden, it is
* added to the hiddenTypesList.
*
* For rows that belong to a line graph group, so are always rendered
* together:
*
* - Treat all rows in the group as visible, if at least one of them is
* - Build a list of all the annotation types that belong to the group
*
*
* @param shownTypes
* a map, keyed by calcId (annotation source), whose entries are the
* lists of annotation types found for the calcId; each annotation
* type in turn may be a list (in the case of grouped annotations)
* @param hiddenTypes
* a map, similar to shownTypes, but for hidden annotation types
* @param annotations
* the annotations on the alignment to scan
* @param forSequences
* the sequences to restrict search to
*/
public static void getShownHiddenTypes(
Map>> shownTypes,
Map>> hiddenTypes,
List annotations,
List forSequences)
{
BitSet visibleGraphGroups = AlignmentAnnotationUtils
.getVisibleLineGraphGroups(annotations);
/*
* Build a lookup, by calcId (annotation source), of all annotation types in
* each graph group.
*/
Map>> groupLabels = new HashMap>>();
// trackers for which calcId!label combinations we have dealt with
List addedToShown = new ArrayList();
List addedToHidden = new ArrayList();
for (AlignmentAnnotation aa : annotations)
{
/*
* Ignore non-positional annotations, can't render these against an
* alignment
*/
if (aa.annotations == null)
{
continue;
}
if (forSequences != null
&& (aa.sequenceRef != null && forSequences
.contains(aa.sequenceRef)))
{
String calcId = aa.getCalcId();
/*
* Build a 'composite label' for types in line graph groups.
*/
final List labelAsList = new ArrayList();
final String displayLabel = aa.label;
labelAsList.add(displayLabel);
if (aa.graph == AlignmentAnnotation.LINE_GRAPH
&& aa.graphGroup > -1)
{
if (!groupLabels.containsKey(calcId))
{
groupLabels.put(calcId, new HashMap>());
}
Map> groupLabelsForCalcId = groupLabels
.get(calcId);
if (groupLabelsForCalcId.containsKey(aa.graphGroup))
{
if (!groupLabelsForCalcId.get(aa.graphGroup).contains(
displayLabel))
{
groupLabelsForCalcId.get(aa.graphGroup).add(displayLabel);
}
}
else
{
groupLabelsForCalcId.put(aa.graphGroup, labelAsList);
}
}
else
/*
* 'Simple case' - not a grouped annotation type - list of one label
* only
*/
{
String rememberAs = calcId + "!" + displayLabel;
if (aa.visible && !addedToShown.contains(rememberAs))
{
if (!shownTypes.containsKey(calcId))
{
shownTypes.put(calcId, new ArrayList>());
}
shownTypes.get(calcId).add(labelAsList);
addedToShown.add(rememberAs);
}
else
{
if (!aa.visible && !addedToHidden.contains(rememberAs))
{
if (!hiddenTypes.containsKey(calcId))
{
hiddenTypes.put(calcId, new ArrayList>());
}
hiddenTypes.get(calcId).add(labelAsList);
addedToHidden.add(rememberAs);
}
}
}
}
}
/*
* Finally add the 'composite group labels' to the appropriate lists,
* depending on whether the group is identified as visible or hidden. Don't
* add the same label more than once (there may be many graph groups that
* generate it).
*/
for (String calcId : groupLabels.keySet())
{
for (int group : groupLabels.get(calcId).keySet())
{
final List groupLabel = groupLabels.get(calcId).get(group);
// don't want to duplicate 'same types in different order'
Collections.sort(groupLabel);
if (visibleGraphGroups.get(group))
{
if (!shownTypes.containsKey(calcId))
{
shownTypes.put(calcId, new ArrayList>());
}
if (!shownTypes.get(calcId).contains(groupLabel))
{
shownTypes.get(calcId).add(groupLabel);
}
}
else
{
if (!hiddenTypes.containsKey(calcId))
{
hiddenTypes.put(calcId, new ArrayList>());
}
if (!hiddenTypes.get(calcId).contains(groupLabel))
{
hiddenTypes.get(calcId).add(groupLabel);
}
}
}
}
}
/**
* Returns a BitSet (possibly empty) of those graphGroups for line graph
* annotations, which have at least one member annotation row marked visible.
*
* Only one row in each visible group is marked visible, but when it is drawn,
* so are all the other rows in the same group.
*
* This lookup set allows us to check whether rows apparently marked not
* visible are in fact shown.
*
* @see AnnotationRenderer#drawComponent
* @param annotations
* @return
*/
public static BitSet getVisibleLineGraphGroups(
List annotations)
{
BitSet result = new BitSet();
for (AlignmentAnnotation ann : annotations)
{
if (ann.graph == AlignmentAnnotation.LINE_GRAPH && ann.visible)
{
int gg = ann.graphGroup;
if (gg > -1)
{
result.set(gg);
}
}
}
return result;
}
/**
* Converts an array of AlignmentAnnotation into a List of
* AlignmentAnnotation. A null array is converted to an empty list.
*
* @param anns
* @return
*/
public static List asList(AlignmentAnnotation[] anns)
{
// TODO use AlignmentAnnotationI instead when it exists
return (anns == null ? Collections. emptyList()
: Arrays.asList(anns));
}
}