Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AlignmentAnnotationUtils | 38 | 66 | 40 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.analysis; | |
22 | ||
23 | import jalview.datamodel.AlignmentAnnotation; | |
24 | import jalview.datamodel.SequenceGroup; | |
25 | import jalview.datamodel.SequenceI; | |
26 | import jalview.renderer.AnnotationRenderer; | |
27 | import jalview.util.Constants; | |
28 | ||
29 | import java.util.ArrayList; | |
30 | import java.util.Arrays; | |
31 | import java.util.BitSet; | |
32 | import java.util.Collections; | |
33 | import java.util.HashMap; | |
34 | import java.util.List; | |
35 | import java.util.Map; | |
36 | import java.util.Optional; | |
37 | ||
38 | public class AlignmentAnnotationUtils | |
39 | { | |
40 | ||
41 | /** | |
42 | * Helper method to populate lists of annotation types for the Show/Hide | |
43 | * Annotations menus. If sequenceGroup is not null, this is restricted to | |
44 | * annotations which are associated with sequences in the selection group. | |
45 | * <p/> | |
46 | * If an annotation row is currently visible, its type (label) is added (once | |
47 | * only per type), to the shownTypes list. If it is currently hidden, it is | |
48 | * added to the hiddenTypesList. | |
49 | * <p/> | |
50 | * For rows that belong to a line graph group, so are always rendered | |
51 | * together: | |
52 | * <ul> | |
53 | * <li>Treat all rows in the group as visible, if at least one of them is</li> | |
54 | * <li>Build a list of all the annotation types that belong to the group</li> | |
55 | * </ul> | |
56 | * | |
57 | * @param shownTypes | |
58 | * a map, keyed by calcId (annotation source), whose entries are the | |
59 | * lists of annotation types found for the calcId; each annotation | |
60 | * type in turn may be a list (in the case of grouped annotations) | |
61 | * @param hiddenTypes | |
62 | * a map, similar to shownTypes, but for hidden annotation types | |
63 | * @param annotations | |
64 | * the annotations on the alignment to scan | |
65 | * @param forSequences | |
66 | * the sequences to restrict search to | |
67 | */ | |
68 | 47 | public static void getShownHiddenTypes( |
69 | Map<String, List<List<String>>> shownTypes, | |
70 | Map<String, List<List<String>>> hiddenTypes, | |
71 | List<AlignmentAnnotation> annotations, | |
72 | List<SequenceI> forSequences) | |
73 | { | |
74 | 47 | BitSet visibleGraphGroups = AlignmentAnnotationUtils |
75 | .getVisibleLineGraphGroups(annotations); | |
76 | ||
77 | /* | |
78 | * Build a lookup, by calcId (annotation source), of all annotation types in | |
79 | * each graph group. | |
80 | */ | |
81 | 47 | Map<String, Map<Integer, List<String>>> groupLabels = new HashMap<String, Map<Integer, List<String>>>(); |
82 | ||
83 | // trackers for which calcId!label combinations we have dealt with | |
84 | 47 | List<String> addedToShown = new ArrayList<String>(); |
85 | 47 | List<String> addedToHidden = new ArrayList<String>(); |
86 | ||
87 | 47 | for (AlignmentAnnotation aa : annotations) |
88 | { | |
89 | /* | |
90 | * Ignore non-positional annotations, can't render these against an | |
91 | * alignment | |
92 | */ | |
93 | 224 | if (aa.annotations == null) |
94 | { | |
95 | 0 | continue; |
96 | } | |
97 | 224 | if (forSequences != null && (aa.sequenceRef != null |
98 | && forSequences.contains(aa.sequenceRef))) | |
99 | { | |
100 | 22 | String calcId = aa.getCalcId(); |
101 | ||
102 | /* | |
103 | * Build a 'composite label' for types in line graph groups. | |
104 | */ | |
105 | 22 | final List<String> labelAsList = new ArrayList<String>(); |
106 | 22 | final String displayLabel = aa.label; |
107 | 22 | labelAsList.add(displayLabel); |
108 | 22 | if (aa.graph == AlignmentAnnotation.LINE_GRAPH |
109 | && aa.graphGroup > -1) | |
110 | { | |
111 | 8 | if (!groupLabels.containsKey(calcId)) |
112 | { | |
113 | 2 | groupLabels.put(calcId, new HashMap<Integer, List<String>>()); |
114 | } | |
115 | 8 | Map<Integer, List<String>> groupLabelsForCalcId = groupLabels |
116 | .get(calcId); | |
117 | 8 | if (groupLabelsForCalcId.containsKey(aa.graphGroup)) |
118 | { | |
119 | 4 | if (!groupLabelsForCalcId.get(aa.graphGroup) |
120 | .contains(displayLabel)) | |
121 | { | |
122 | 4 | groupLabelsForCalcId.get(aa.graphGroup).add(displayLabel); |
123 | } | |
124 | } | |
125 | else | |
126 | { | |
127 | 4 | groupLabelsForCalcId.put(aa.graphGroup, labelAsList); |
128 | } | |
129 | } | |
130 | else | |
131 | /* | |
132 | * 'Simple case' - not a grouped annotation type - list of one label | |
133 | * only | |
134 | */ | |
135 | { | |
136 | 14 | String rememberAs = calcId + "!" + displayLabel; |
137 | 14 | if (aa.visible && !addedToShown.contains(rememberAs)) |
138 | { | |
139 | 6 | if (!shownTypes.containsKey(calcId)) |
140 | { | |
141 | 4 | shownTypes.put(calcId, new ArrayList<List<String>>()); |
142 | } | |
143 | 6 | shownTypes.get(calcId).add(labelAsList); |
144 | 6 | addedToShown.add(rememberAs); |
145 | } | |
146 | else | |
147 | { | |
148 | 8 | if (!aa.visible && !addedToHidden.contains(rememberAs)) |
149 | { | |
150 | 8 | if (!hiddenTypes.containsKey(calcId)) |
151 | { | |
152 | 7 | hiddenTypes.put(calcId, new ArrayList<List<String>>()); |
153 | } | |
154 | 8 | hiddenTypes.get(calcId).add(labelAsList); |
155 | 8 | addedToHidden.add(rememberAs); |
156 | } | |
157 | } | |
158 | } | |
159 | } | |
160 | } | |
161 | /* | |
162 | * Finally add the 'composite group labels' to the appropriate lists, | |
163 | * depending on whether the group is identified as visible or hidden. Don't | |
164 | * add the same label more than once (there may be many graph groups that | |
165 | * generate it). | |
166 | */ | |
167 | 47 | for (String calcId : groupLabels.keySet()) |
168 | { | |
169 | 2 | for (int group : groupLabels.get(calcId).keySet()) |
170 | { | |
171 | 4 | final List<String> groupLabel = groupLabels.get(calcId).get(group); |
172 | // don't want to duplicate 'same types in different order' | |
173 | 4 | Collections.sort(groupLabel); |
174 | 4 | if (visibleGraphGroups.get(group)) |
175 | { | |
176 | 2 | if (!shownTypes.containsKey(calcId)) |
177 | { | |
178 | 1 | shownTypes.put(calcId, new ArrayList<List<String>>()); |
179 | } | |
180 | 2 | if (!shownTypes.get(calcId).contains(groupLabel)) |
181 | { | |
182 | 1 | shownTypes.get(calcId).add(groupLabel); |
183 | } | |
184 | } | |
185 | else | |
186 | { | |
187 | 2 | if (!hiddenTypes.containsKey(calcId)) |
188 | { | |
189 | 1 | hiddenTypes.put(calcId, new ArrayList<List<String>>()); |
190 | } | |
191 | 2 | if (!hiddenTypes.get(calcId).contains(groupLabel)) |
192 | { | |
193 | 1 | hiddenTypes.get(calcId).add(groupLabel); |
194 | } | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
200 | /** | |
201 | * Updates the lists of shown and hidden secondary structure types based on | |
202 | * the selected sequence group. | |
203 | * | |
204 | * @param shownTypes | |
205 | * A list that will be populated with the providers of secondary | |
206 | * structures that are shown. | |
207 | * @param hiddenTypes | |
208 | * A list that will be populated with the providers of secondary | |
209 | * structures that are hidden. | |
210 | * @param annotations | |
211 | * A list of AlignmentAnnotation objects. | |
212 | * @param selectedSequenceGroup | |
213 | * The sequence group selected by the user. | |
214 | */ | |
215 | 4 | public static void getShownHiddenSecondaryStructureProvidersForGroup( |
216 | List<String> shownTypes, List<String> hiddenTypes, | |
217 | List<AlignmentAnnotation> annotations, | |
218 | SequenceGroup selectedSequenceGroup) | |
219 | { | |
220 | // Return if the selected sequence group or annotations are null | |
221 | 4 | if (selectedSequenceGroup == null || annotations == null) |
222 | { | |
223 | 0 | return; |
224 | } | |
225 | ||
226 | // Get the secondary structure sources of the selected sequence group | |
227 | 4 | List<String> ssSourcesForSelectedGroup = selectedSequenceGroup |
228 | .getSecondaryStructureSources(); | |
229 | ||
230 | // Return if there are no secondary structure sources for the selected group | |
231 | 4 | if (ssSourcesForSelectedGroup == null |
232 | || ssSourcesForSelectedGroup.isEmpty()) | |
233 | { | |
234 | 4 | return; |
235 | } | |
236 | ||
237 | // Iterate through each annotation | |
238 | 0 | for (AlignmentAnnotation aa : annotations) |
239 | { | |
240 | /* Skip to the next annotation if the annotation, the annotation's group | |
241 | * reference is null, or the annotation's group reference does not match | |
242 | * the selected group | |
243 | */ | |
244 | 0 | if (aa.annotations == null || aa.groupRef == null |
245 | || selectedSequenceGroup != aa.groupRef | |
246 | || !aa.label.startsWith( | |
247 | Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL)) | |
248 | { | |
249 | 0 | continue; |
250 | } | |
251 | ||
252 | /* Find a provider from the secondary structure sources that matches | |
253 | * the annotation's label. This is to exclude secondary structure | |
254 | * providers which has no secondary structure data for the selected group. | |
255 | */ | |
256 | 0 | Optional<String> provider = ssSourcesForSelectedGroup.stream() |
257 | .filter(aa.label::contains).findFirst() | |
258 | .map(substring -> aa.label.substring(0, | |
259 | aa.label.indexOf(substring) + substring.length())); | |
260 | ||
261 | // If a matching provider is found and the annotation is visible, add | |
262 | // the provider to the shown types list (if not already in shownTypes). | |
263 | // If the annotation is not visible, add it to hiddenTypes list. | |
264 | 0 | provider.ifPresent(p -> { |
265 | 0 | if (aa.visible && !shownTypes.contains(p)) |
266 | { | |
267 | 0 | shownTypes.add(p); |
268 | } | |
269 | 0 | else if (!aa.visible && !shownTypes.contains(p)) |
270 | { | |
271 | 0 | hiddenTypes.add(p); |
272 | } | |
273 | }); | |
274 | } | |
275 | } | |
276 | ||
277 | /** | |
278 | * Returns a BitSet (possibly empty) of those graphGroups for line graph | |
279 | * annotations, which have at least one member annotation row marked visible. | |
280 | * <p/> | |
281 | * Only one row in each visible group is marked visible, but when it is drawn, | |
282 | * so are all the other rows in the same group. | |
283 | * <p/> | |
284 | * This lookup set allows us to check whether rows apparently marked not | |
285 | * visible are in fact shown. | |
286 | * | |
287 | * @see AnnotationRenderer#drawComponent | |
288 | * @param annotations | |
289 | * @return | |
290 | */ | |
291 | 48 | public static BitSet getVisibleLineGraphGroups( |
292 | List<AlignmentAnnotation> annotations) | |
293 | { | |
294 | 48 | BitSet result = new BitSet(); |
295 | 48 | for (AlignmentAnnotation ann : annotations) |
296 | { | |
297 | 236 | if (ann.graph == AlignmentAnnotation.LINE_GRAPH && ann.visible) |
298 | { | |
299 | 6 | int gg = ann.graphGroup; |
300 | 6 | if (gg > -1) |
301 | { | |
302 | 5 | result.set(gg); |
303 | } | |
304 | } | |
305 | } | |
306 | 48 | return result; |
307 | } | |
308 | ||
309 | /** | |
310 | * Converts an array of AlignmentAnnotation into a List of | |
311 | * AlignmentAnnotation. A null array is converted to an empty list. | |
312 | * | |
313 | * @param anns | |
314 | * @return | |
315 | */ | |
316 | 55 | public static List<AlignmentAnnotation> asList(AlignmentAnnotation[] anns) |
317 | { | |
318 | // TODO use AlignmentAnnotationI instead when it exists | |
319 | 55 | return (anns == null ? Collections.<AlignmentAnnotation> emptyList() |
320 | : Arrays.asList(anns)); | |
321 | } | |
322 | } |