Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AnnotationFile | 50 | 755 | 298 | ||
AnnotationFile.ViewDef | 105 | 4 | 1 |
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.io; | |
22 | ||
23 | import java.awt.Color; | |
24 | import java.io.BufferedReader; | |
25 | import java.util.ArrayList; | |
26 | import java.util.BitSet; | |
27 | import java.util.Enumeration; | |
28 | import java.util.HashMap; | |
29 | import java.util.Hashtable; | |
30 | import java.util.List; | |
31 | import java.util.Map; | |
32 | import java.util.StringTokenizer; | |
33 | import java.util.Vector; | |
34 | ||
35 | import jalview.analysis.Conservation; | |
36 | import jalview.api.AlignViewportI; | |
37 | import jalview.datamodel.AlignmentAnnotation; | |
38 | import jalview.datamodel.AlignmentI; | |
39 | import jalview.datamodel.Annotation; | |
40 | import jalview.datamodel.ColumnSelection; | |
41 | import jalview.datamodel.GraphLine; | |
42 | import jalview.datamodel.HiddenColumns; | |
43 | import jalview.datamodel.HiddenSequences; | |
44 | import jalview.datamodel.SequenceGroup; | |
45 | import jalview.datamodel.SequenceI; | |
46 | import jalview.schemes.ColourSchemeI; | |
47 | import jalview.schemes.ColourSchemeProperty; | |
48 | import jalview.util.ColorUtils; | |
49 | ||
50 | public class AnnotationFile | |
51 | { | |
52 | private static final String GRAPHLINE = "GRAPHLINE"; | |
53 | ||
54 | private static final String COMBINE = "COMBINE"; | |
55 | ||
56 | protected String newline = System.getProperty("line.separator"); | |
57 | ||
58 | private StringBuffer text; | |
59 | ||
60 | private SequenceI refSeq = null; | |
61 | ||
62 | private String refSeqId = null; | |
63 | ||
64 | private long nlinesread = 0; | |
65 | ||
66 | private String lastread = ""; | |
67 | ||
68 | /** | |
69 | * Constructor | |
70 | */ | |
71 | 38 | public AnnotationFile() |
72 | { | |
73 | 38 | init(); |
74 | } | |
75 | ||
76 | 38 | private void init() |
77 | { | |
78 | 38 | text = new StringBuffer("JALVIEW_ANNOTATION" + newline + "# Created: " |
79 | + new java.util.Date() + newline + newline); | |
80 | 38 | refSeq = null; |
81 | 38 | refSeqId = null; |
82 | } | |
83 | ||
84 | /** | |
85 | * convenience method for pre-2.9 annotation files which have no view, hidden | |
86 | * columns or hidden row keywords. | |
87 | * | |
88 | * @param annotations | |
89 | * @param list | |
90 | * @param properties | |
91 | * @return annotation file as a string. | |
92 | */ | |
93 | 0 | public String printAnnotations(AlignmentAnnotation[] annotations, |
94 | List<SequenceGroup> list, Hashtable properties) | |
95 | { | |
96 | 0 | return printAnnotations(annotations, list, properties, null, null, |
97 | null); | |
98 | ||
99 | } | |
100 | ||
101 | /** | |
102 | * hold all the information about a particular view definition read from or | |
103 | * written out in an annotations file. | |
104 | */ | |
105 | public class ViewDef | |
106 | { | |
107 | // TODO this class is not used - remove? | |
108 | public final String viewname; | |
109 | ||
110 | public final HiddenSequences hidseqs; | |
111 | ||
112 | public final HiddenColumns hiddencols; | |
113 | ||
114 | public final Hashtable hiddenRepSeqs; | |
115 | ||
116 | 5 | public ViewDef(String vname, HiddenSequences hseqs, HiddenColumns hcols, |
117 | Hashtable hRepSeqs) | |
118 | { | |
119 | 5 | this.viewname = vname; |
120 | 5 | this.hidseqs = hseqs; |
121 | 5 | this.hiddencols = hcols; |
122 | 5 | this.hiddenRepSeqs = hRepSeqs; |
123 | } | |
124 | } | |
125 | ||
126 | /** | |
127 | * Prepare an annotation file given a set of annotations, groups, alignment | |
128 | * properties and views. | |
129 | * | |
130 | * @param annotations | |
131 | * @param list | |
132 | * @param properties | |
133 | * @param views | |
134 | * @return annotation file | |
135 | */ | |
136 | 5 | public String printAnnotations(AlignmentAnnotation[] annotations, |
137 | List<SequenceGroup> list, Hashtable properties, HiddenColumns cs, | |
138 | AlignmentI al, ViewDef view) | |
139 | { | |
140 | 5 | if (view != null) |
141 | { | |
142 | 5 | if (view.viewname != null) |
143 | { | |
144 | 0 | text.append("VIEW_DEF\t" + view.viewname + "\n"); |
145 | } | |
146 | 5 | if (list == null) |
147 | { | |
148 | // list = view.visibleGroups; | |
149 | } | |
150 | 5 | if (cs == null) |
151 | { | |
152 | 5 | cs = view.hiddencols; |
153 | } | |
154 | 5 | if (al == null) |
155 | { | |
156 | // add hidden rep sequences. | |
157 | } | |
158 | } | |
159 | // first target - store and restore all settings for a view. | |
160 | 5 | if (al != null && al.hasSeqrep()) |
161 | { | |
162 | 1 | text.append("VIEW_SETREF\t" + al.getSeqrep().getName() + "\n"); |
163 | } | |
164 | 5 | if (cs != null && cs.hasHiddenColumns()) |
165 | { | |
166 | 1 | text.append("VIEW_HIDECOLS\t"); |
167 | ||
168 | 1 | String regions = cs.regionsToString(",", "-"); |
169 | 1 | text.append(regions); |
170 | 1 | text.append("\n"); |
171 | } | |
172 | // TODO: allow efficient recovery of annotation data shown in several | |
173 | // different views | |
174 | 5 | if (annotations != null) |
175 | { | |
176 | 4 | boolean oneColour = true; |
177 | 4 | AlignmentAnnotation row; |
178 | 4 | String comma; |
179 | 4 | SequenceI refSeq = null; |
180 | 4 | SequenceGroup refGroup = null; |
181 | ||
182 | 4 | StringBuffer colours = new StringBuffer(); |
183 | 4 | StringBuffer graphLine = new StringBuffer(); |
184 | 4 | StringBuffer rowprops = new StringBuffer(); |
185 | 4 | Hashtable<Integer, String> graphGroup = new Hashtable<>(); |
186 | 4 | Hashtable<Integer, Object[]> graphGroup_refs = new Hashtable<>(); |
187 | 4 | BitSet graphGroupSeen = new BitSet(); |
188 | ||
189 | 4 | java.awt.Color color; |
190 | ||
191 | 84 | for (int i = 0; i < annotations.length; i++) |
192 | { | |
193 | 80 | row = annotations[i]; |
194 | ||
195 | 80 | if (!row.visible && !row.hasScore() && !(row.graphGroup > -1 |
196 | && graphGroupSeen.get(row.graphGroup))) | |
197 | { | |
198 | 0 | continue; |
199 | } | |
200 | ||
201 | 80 | color = null; |
202 | 80 | oneColour = true; |
203 | ||
204 | // mark any sequence references for the row | |
205 | 80 | writeSequence_Ref(refSeq, row.sequenceRef); |
206 | 80 | refSeq = row.sequenceRef; |
207 | // mark any group references for the row | |
208 | 80 | writeGroup_Ref(refGroup, row.groupRef); |
209 | 80 | refGroup = row.groupRef; |
210 | ||
211 | 80 | boolean hasGlyphs = row.hasIcons, hasLabels = row.hasText, |
212 | hasValues = row.hasScore, hasText = false; | |
213 | // lookahead to check what the annotation row object actually contains. | |
214 | 12348 | for (int j = 0; row.annotations != null |
215 | && j < row.annotations.length | |
216 | && (!hasGlyphs || !hasLabels || !hasValues); j++) | |
217 | { | |
218 | 12268 | if (row.annotations[j] != null) |
219 | { | |
220 | 9156 | hasLabels |= (row.annotations[j].displayCharacter != null |
221 | && row.annotations[j].displayCharacter.length() > 0 | |
222 | && !row.annotations[j].displayCharacter.equals(" ")); | |
223 | 9156 | hasGlyphs |= (row.annotations[j].secondaryStructure != 0 |
224 | && row.annotations[j].secondaryStructure != ' '); | |
225 | 9156 | hasValues |= (!Float.isNaN(row.annotations[j].value)); // NaNs can't |
226 | // be | |
227 | // rendered.. | |
228 | 9156 | hasText |= (row.annotations[j].description != null |
229 | && row.annotations[j].description.length() > 0); | |
230 | } | |
231 | } | |
232 | ||
233 | 80 | if (row.graph == AlignmentAnnotation.NO_GRAPH) |
234 | { | |
235 | 4 | text.append("NO_GRAPH\t"); |
236 | 4 | hasValues = false; // only secondary structure |
237 | // hasLabels = false; // and annotation description string. | |
238 | } | |
239 | else | |
240 | { | |
241 | 76 | if (row.graph == AlignmentAnnotation.BAR_GRAPH) |
242 | { | |
243 | 10 | text.append("BAR_GRAPH\t"); |
244 | 10 | hasGlyphs = false; // no secondary structure |
245 | ||
246 | } | |
247 | 66 | else if (row.graph == AlignmentAnnotation.LINE_GRAPH) |
248 | { | |
249 | 66 | hasGlyphs = false; // no secondary structure |
250 | 66 | text.append("LINE_GRAPH\t"); |
251 | } | |
252 | ||
253 | 76 | if (row.getThreshold() != null) |
254 | { | |
255 | 62 | graphLine.append("GRAPHLINE\t"); |
256 | 62 | graphLine.append(row.label); |
257 | 62 | graphLine.append("\t"); |
258 | 62 | graphLine.append(row.getThreshold().value); |
259 | 62 | graphLine.append("\t"); |
260 | 62 | graphLine.append(row.getThreshold().label); |
261 | 62 | graphLine.append("\t"); |
262 | 62 | graphLine.append(jalview.util.Format |
263 | .getHexString(row.getThreshold().colour)); | |
264 | 62 | graphLine.append(newline); |
265 | } | |
266 | ||
267 | 76 | if (row.graphGroup > -1) |
268 | { | |
269 | 66 | graphGroupSeen.set(row.graphGroup); |
270 | 66 | Integer key = Integer.valueOf(row.graphGroup); |
271 | 66 | if (graphGroup.containsKey(key)) |
272 | { | |
273 | 33 | graphGroup.put(key, graphGroup.get(key) + "\t" + row.label); |
274 | ||
275 | } | |
276 | else | |
277 | { | |
278 | 33 | graphGroup_refs.put(key, new Object[] { refSeq, refGroup }); |
279 | 33 | graphGroup.put(key, row.label); |
280 | } | |
281 | } | |
282 | } | |
283 | ||
284 | 80 | text.append(row.label + "\t"); |
285 | 80 | if (row.description != null) |
286 | { | |
287 | 68 | text.append(row.description + "\t"); |
288 | } | |
289 | 12640 | for (int j = 0; row.annotations != null |
290 | && j < row.annotations.length; j++) | |
291 | { | |
292 | 12560 | if (refSeq != null |
293 | && jalview.util.Comparison.isGap(refSeq.getCharAt(j))) | |
294 | { | |
295 | 1462 | continue; |
296 | } | |
297 | ||
298 | 11098 | if (row.annotations[j] != null) |
299 | { | |
300 | 9168 | comma = ""; |
301 | 9168 | if (hasGlyphs) // could be also hasGlyphs || ... |
302 | { | |
303 | ||
304 | 32 | text.append(comma); |
305 | 32 | if (row.annotations[j].secondaryStructure != ' ') |
306 | { | |
307 | // only write out the field if its not whitespace. | |
308 | 32 | text.append(row.annotations[j].secondaryStructure); |
309 | } | |
310 | 32 | comma = ","; |
311 | } | |
312 | 9168 | if (hasValues) |
313 | { | |
314 | 9136 | if (!Float.isNaN(row.annotations[j].value)) |
315 | { | |
316 | 9136 | text.append(comma + row.annotations[j].value); |
317 | } | |
318 | else | |
319 | { | |
320 | // jalview.bin.Console.errPrintln("Skipping NaN - not valid | |
321 | // value."); | |
322 | 0 | text.append(comma + 0f);// row.annotations[j].value); |
323 | } | |
324 | 9136 | comma = ","; |
325 | } | |
326 | 9168 | if (hasLabels) |
327 | { | |
328 | // TODO: labels are emitted after values for bar graphs. | |
329 | 9150 | if // empty labels are allowed, so |
330 | 9150 | (row.annotations[j].displayCharacter != null |
331 | && row.annotations[j].displayCharacter.length() > 0 | |
332 | && !row.annotations[j].displayCharacter.equals(" ")) | |
333 | { | |
334 | 362 | text.append(comma + row.annotations[j].displayCharacter); |
335 | 362 | comma = ","; |
336 | } | |
337 | } | |
338 | 9168 | if (hasText) |
339 | { | |
340 | 9070 | if (row.annotations[j].description != null |
341 | && row.annotations[j].description.length() > 0 | |
342 | && !row.annotations[j].description | |
343 | .equals(row.annotations[j].displayCharacter)) | |
344 | { | |
345 | 9028 | text.append(comma + row.annotations[j].description); |
346 | 9028 | comma = ","; |
347 | } | |
348 | } | |
349 | 9168 | if (color != null && !color.equals(row.annotations[j].colour)) |
350 | { | |
351 | 450 | oneColour = false; |
352 | } | |
353 | ||
354 | 9168 | color = row.annotations[j].colour; |
355 | ||
356 | 9168 | if (row.annotations[j].colour != null |
357 | && row.annotations[j].colour != java.awt.Color.black) | |
358 | { | |
359 | 8796 | text.append(comma + "[" + jalview.util.Format |
360 | .getHexString(row.annotations[j].colour) + "]"); | |
361 | 8796 | comma = ","; |
362 | } | |
363 | } | |
364 | 11098 | text.append("|"); |
365 | } | |
366 | ||
367 | 80 | if (row.hasScore()) |
368 | { | |
369 | 0 | text.append("\t" + row.score); |
370 | } | |
371 | ||
372 | 80 | text.append(newline); |
373 | ||
374 | 80 | if (color != null && color != java.awt.Color.black && oneColour) |
375 | { | |
376 | 68 | colours.append("COLOUR\t"); |
377 | 68 | colours.append(row.label); |
378 | 68 | colours.append("\t"); |
379 | 68 | colours.append(jalview.util.Format.getHexString(color)); |
380 | 68 | colours.append(newline); |
381 | } | |
382 | 80 | if (row.scaleColLabel || row.showAllColLabels |
383 | || row.centreColLabels) | |
384 | { | |
385 | 0 | rowprops.append("ROWPROPERTIES\t"); |
386 | 0 | rowprops.append(row.label); |
387 | 0 | rowprops.append("\tscaletofit="); |
388 | 0 | rowprops.append(row.scaleColLabel); |
389 | 0 | rowprops.append("\tshowalllabs="); |
390 | 0 | rowprops.append(row.showAllColLabels); |
391 | 0 | rowprops.append("\tcentrelabs="); |
392 | 0 | rowprops.append(row.centreColLabels); |
393 | 0 | rowprops.append(newline); |
394 | } | |
395 | 80 | if (graphLine.length() > 0) |
396 | { | |
397 | 62 | text.append(graphLine.toString()); |
398 | 62 | graphLine.setLength(0); |
399 | } | |
400 | } | |
401 | ||
402 | 4 | text.append(newline); |
403 | ||
404 | 4 | text.append(colours.toString()); |
405 | 4 | if (graphGroup.size() > 0) |
406 | { | |
407 | 4 | SequenceI oldRefSeq = refSeq; |
408 | 4 | SequenceGroup oldRefGroup = refGroup; |
409 | 4 | for (Map.Entry<Integer, String> combine_statement : graphGroup |
410 | .entrySet()) | |
411 | { | |
412 | 33 | Object[] seqRefAndGroup = graphGroup_refs |
413 | .get(combine_statement.getKey()); | |
414 | ||
415 | 33 | writeSequence_Ref(refSeq, (SequenceI) seqRefAndGroup[0]); |
416 | 33 | refSeq = (SequenceI) seqRefAndGroup[0]; |
417 | ||
418 | 33 | writeGroup_Ref(refGroup, (SequenceGroup) seqRefAndGroup[1]); |
419 | 33 | refGroup = (SequenceGroup) seqRefAndGroup[1]; |
420 | 33 | text.append("COMBINE\t"); |
421 | 33 | text.append(combine_statement.getValue()); |
422 | 33 | text.append(newline); |
423 | } | |
424 | 4 | writeSequence_Ref(refSeq, oldRefSeq); |
425 | 4 | refSeq = oldRefSeq; |
426 | ||
427 | 4 | writeGroup_Ref(refGroup, oldRefGroup); |
428 | 4 | refGroup = oldRefGroup; |
429 | } | |
430 | 4 | text.append(rowprops.toString()); |
431 | } | |
432 | ||
433 | 5 | if (list != null) |
434 | { | |
435 | 5 | printGroups(list); |
436 | } | |
437 | ||
438 | 5 | if (properties != null) |
439 | { | |
440 | 0 | text.append(newline); |
441 | 0 | text.append(newline); |
442 | 0 | text.append("ALIGNMENT"); |
443 | 0 | Enumeration en = properties.keys(); |
444 | 0 | while (en.hasMoreElements()) |
445 | { | |
446 | 0 | String key = en.nextElement().toString(); |
447 | 0 | text.append("\t"); |
448 | 0 | text.append(key); |
449 | 0 | text.append("="); |
450 | 0 | text.append(properties.get(key)); |
451 | } | |
452 | // TODO: output alignment visualization settings here if required | |
453 | // iterate through one or more views, defining, marking columns and rows | |
454 | // as visible/hidden, and emmitting view properties. | |
455 | // View specific annotation is | |
456 | } | |
457 | ||
458 | 5 | return text.toString(); |
459 | } | |
460 | ||
461 | 117 | private Object writeGroup_Ref(SequenceGroup refGroup, |
462 | SequenceGroup next_refGroup) | |
463 | { | |
464 | 117 | if (next_refGroup == null) |
465 | { | |
466 | ||
467 | 117 | if (refGroup != null) |
468 | { | |
469 | 0 | text.append(newline); |
470 | 0 | text.append("GROUP_REF\t"); |
471 | 0 | text.append("ALIGNMENT"); |
472 | 0 | text.append(newline); |
473 | } | |
474 | 117 | return true; |
475 | } | |
476 | else | |
477 | { | |
478 | 0 | if (refGroup == null || refGroup != next_refGroup) |
479 | { | |
480 | 0 | text.append(newline); |
481 | 0 | text.append("GROUP_REF\t"); |
482 | 0 | text.append(next_refGroup.getName()); |
483 | 0 | text.append(newline); |
484 | 0 | return true; |
485 | } | |
486 | } | |
487 | 0 | return false; |
488 | } | |
489 | ||
490 | 117 | private boolean writeSequence_Ref(SequenceI refSeq, SequenceI next_refSeq) |
491 | { | |
492 | ||
493 | 117 | if (next_refSeq == null) |
494 | { | |
495 | 6 | if (refSeq != null) |
496 | { | |
497 | 0 | text.append(newline); |
498 | 0 | text.append("SEQUENCE_REF\t"); |
499 | 0 | text.append("ALIGNMENT"); |
500 | 0 | text.append(newline); |
501 | 0 | return true; |
502 | } | |
503 | } | |
504 | else | |
505 | { | |
506 | 111 | if (refSeq == null || refSeq != next_refSeq) |
507 | { | |
508 | 62 | text.append(newline); |
509 | 62 | text.append("SEQUENCE_REF\t"); |
510 | 62 | text.append(next_refSeq.getName()); |
511 | 62 | text.append(newline); |
512 | 62 | return true; |
513 | } | |
514 | } | |
515 | 55 | return false; |
516 | } | |
517 | ||
518 | 5 | protected void printGroups(List<SequenceGroup> list) |
519 | { | |
520 | 5 | SequenceI seqrep = null; |
521 | 5 | for (SequenceGroup sg : list) |
522 | { | |
523 | 6 | if (!sg.hasSeqrep()) |
524 | { | |
525 | 2 | text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t" |
526 | + (sg.getStartRes() + 1) + "\t" + (sg.getEndRes() + 1) | |
527 | + "\t" + "-1\t"); | |
528 | 2 | seqrep = null; |
529 | } | |
530 | else | |
531 | { | |
532 | 4 | seqrep = sg.getSeqrep(); |
533 | 4 | text.append("SEQUENCE_REF\t"); |
534 | 4 | text.append(seqrep.getName()); |
535 | 4 | text.append(newline); |
536 | 4 | text.append("SEQUENCE_GROUP\t"); |
537 | 4 | text.append(sg.getName()); |
538 | 4 | text.append("\t"); |
539 | 4 | text.append((seqrep.findPosition(sg.getStartRes()))); |
540 | 4 | text.append("\t"); |
541 | 4 | text.append((seqrep.findPosition(sg.getEndRes()))); |
542 | 4 | text.append("\t"); |
543 | 4 | text.append("-1\t"); |
544 | } | |
545 | 47 | for (int s = 0; s < sg.getSize(); s++) |
546 | { | |
547 | 41 | text.append(sg.getSequenceAt(s).getName()); |
548 | 41 | text.append("\t"); |
549 | } | |
550 | 6 | text.append(newline); |
551 | 6 | text.append("PROPERTIES\t"); |
552 | 6 | text.append(sg.getName()); |
553 | 6 | text.append("\t"); |
554 | ||
555 | 6 | if (sg.getDescription() != null) |
556 | { | |
557 | 2 | text.append("description="); |
558 | 2 | text.append(sg.getDescription()); |
559 | 2 | text.append("\t"); |
560 | } | |
561 | 6 | if (sg.cs != null) |
562 | { | |
563 | 6 | text.append("colour="); |
564 | 6 | text.append(ColourSchemeProperty |
565 | .getColourName(sg.cs.getColourScheme())); | |
566 | 6 | text.append("\t"); |
567 | 6 | if (sg.cs.getThreshold() != 0) |
568 | { | |
569 | 0 | text.append("pidThreshold="); |
570 | 0 | text.append(sg.cs.getThreshold()); |
571 | } | |
572 | 6 | if (sg.cs.isConsensusSecondaryStructureColouring()) |
573 | { | |
574 | 0 | text.append("secondaryStructureConservationThreshold="); |
575 | 0 | text.append(sg.cs.getConsensusSecondaryStructureThreshold()); |
576 | 0 | text.append("\t"); |
577 | } | |
578 | 6 | if (sg.cs.conservationApplied()) |
579 | { | |
580 | 0 | text.append("consThreshold="); |
581 | 0 | text.append(sg.cs.getConservationInc()); |
582 | 0 | text.append("\t"); |
583 | } | |
584 | } | |
585 | 6 | text.append("outlineColour="); |
586 | 6 | text.append(jalview.util.Format.getHexString(sg.getOutlineColour())); |
587 | 6 | text.append("\t"); |
588 | ||
589 | 6 | text.append("displayBoxes="); |
590 | 6 | text.append(sg.getDisplayBoxes()); |
591 | 6 | text.append("\t"); |
592 | 6 | text.append("displayText="); |
593 | 6 | text.append(sg.getDisplayText()); |
594 | 6 | text.append("\t"); |
595 | 6 | text.append("colourText="); |
596 | 6 | text.append(sg.getColourText()); |
597 | 6 | text.append("\t"); |
598 | 6 | text.append("showUnconserved="); |
599 | 6 | text.append(sg.getShowNonconserved()); |
600 | 6 | text.append("\t"); |
601 | 6 | if (sg.textColour != java.awt.Color.black) |
602 | { | |
603 | 0 | text.append("textCol1="); |
604 | 0 | text.append(jalview.util.Format.getHexString(sg.textColour)); |
605 | 0 | text.append("\t"); |
606 | } | |
607 | 6 | if (sg.textColour2 != java.awt.Color.white) |
608 | { | |
609 | 2 | text.append("textCol2="); |
610 | 2 | text.append(jalview.util.Format.getHexString(sg.textColour2)); |
611 | 2 | text.append("\t"); |
612 | } | |
613 | 6 | if (sg.thresholdTextColour != 0) |
614 | { | |
615 | 0 | text.append("textColThreshold="); |
616 | 0 | text.append(sg.thresholdTextColour); |
617 | 0 | text.append("\t"); |
618 | } | |
619 | 6 | if (sg.idColour != null) |
620 | { | |
621 | 0 | text.append("idColour="); |
622 | 0 | text.append(jalview.util.Format.getHexString(sg.idColour)); |
623 | 0 | text.append("\t"); |
624 | } | |
625 | 6 | if (sg.isHidereps()) |
626 | { | |
627 | 0 | text.append("hide=true\t"); |
628 | } | |
629 | 6 | if (sg.isHideCols()) |
630 | { | |
631 | 0 | text.append("hidecols=true\t"); |
632 | } | |
633 | 6 | if (seqrep != null) |
634 | { | |
635 | // terminate the last line and clear the sequence ref for the group | |
636 | 4 | text.append(newline); |
637 | 4 | text.append("SEQUENCE_REF"); |
638 | } | |
639 | 6 | text.append(newline); |
640 | 6 | text.append(newline); |
641 | ||
642 | } | |
643 | } | |
644 | ||
645 | 8 | public boolean annotateAlignmentView(AlignViewportI viewport, Object file, |
646 | DataSourceType protocol) | |
647 | { | |
648 | 8 | ColumnSelection colSel = viewport.getColumnSelection(); |
649 | 8 | HiddenColumns hidden = viewport.getAlignment().getHiddenColumns(); |
650 | 8 | if (colSel == null) |
651 | { | |
652 | 0 | colSel = new ColumnSelection(); |
653 | } | |
654 | 8 | if (hidden == null) |
655 | { | |
656 | 0 | hidden = new HiddenColumns(); |
657 | } | |
658 | 8 | boolean rslt = readAnnotationFile(viewport.getAlignment(), hidden, file, |
659 | protocol); | |
660 | 8 | if (rslt && (colSel.hasSelectedColumns() || hidden.hasHiddenColumns())) |
661 | { | |
662 | 0 | viewport.setColumnSelection(colSel); |
663 | 0 | viewport.getAlignment().setHiddenColumns(hidden); |
664 | } | |
665 | ||
666 | 8 | return rslt; |
667 | } | |
668 | ||
669 | 13 | public boolean readAnnotationFile(AlignmentI al, String file, |
670 | DataSourceType sourceType) | |
671 | { | |
672 | 13 | return readAnnotationFile(al, null, file, sourceType); |
673 | } | |
674 | ||
675 | 26 | public boolean readAnnotationFile(AlignmentI al, HiddenColumns hidden, |
676 | Object file, DataSourceType sourceType) | |
677 | { | |
678 | 26 | BufferedReader in = null; |
679 | 26 | try |
680 | { | |
681 | 26 | in = new FileParse().getBufferedReader(file, sourceType); |
682 | 26 | if (in != null) |
683 | { | |
684 | 26 | return parseAnnotationFrom(al, hidden, in); |
685 | } | |
686 | } catch (Exception ex) | |
687 | { | |
688 | 0 | ex.printStackTrace(); |
689 | 0 | jalview.bin.Console |
690 | .outPrintln("Problem reading annotation file: " + ex); | |
691 | 0 | if (nlinesread > 0) |
692 | { | |
693 | 0 | jalview.bin.Console.outPrintln("Last read line " + nlinesread |
694 | + ": '" + lastread + "' (first 80 chars) ..."); | |
695 | } | |
696 | 0 | return false; |
697 | } | |
698 | 0 | return false; |
699 | } | |
700 | ||
701 | 26 | public boolean parseAnnotationFrom(AlignmentI al, HiddenColumns hidden, |
702 | BufferedReader in) throws Exception | |
703 | { | |
704 | 26 | nlinesread = 0; |
705 | 26 | ArrayList<Object[]> combineAnnotation_calls = new ArrayList<>(); |
706 | 26 | ArrayList<Object[]> deferredAnnotation_calls = new ArrayList<>(); |
707 | 26 | boolean modified = false; |
708 | 26 | String groupRef = null; |
709 | 26 | Hashtable groupRefRows = new Hashtable(); |
710 | ||
711 | 26 | Hashtable autoAnnots = new Hashtable(); |
712 | { | |
713 | 26 | String line, label, description, token; |
714 | 26 | int graphStyle, index; |
715 | 26 | int refSeqIndex = 1; |
716 | 26 | int existingAnnotations = 0; |
717 | // when true - will add new rows regardless of whether they are duplicate | |
718 | // auto-annotation like consensus or conservation graphs | |
719 | 26 | boolean overrideAutoAnnot = false; |
720 | 26 | if (al.getAlignmentAnnotation() != null) |
721 | { | |
722 | 8 | existingAnnotations = al.getAlignmentAnnotation().length; |
723 | 8 | if (existingAnnotations > 0) |
724 | { | |
725 | 8 | AlignmentAnnotation[] aa = al.getAlignmentAnnotation(); |
726 | 40 | for (int aai = 0; aai < aa.length; aai++) |
727 | { | |
728 | 32 | if (aa[aai].autoCalculated) |
729 | { | |
730 | // make a note of the name and description | |
731 | 32 | autoAnnots.put( |
732 | autoAnnotsKey(aa[aai], aa[aai].sequenceRef, | |
733 | 32 | (aa[aai].groupRef == null ? null |
734 | : aa[aai].groupRef.getName())), | |
735 | Integer.valueOf(1)); | |
736 | } | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | 26 | int alWidth = al.getWidth(); |
742 | ||
743 | 26 | StringTokenizer st; |
744 | 26 | Annotation[] annotations; |
745 | 26 | AlignmentAnnotation annotation = null; |
746 | ||
747 | // First confirm this is an Annotation file | |
748 | 26 | boolean jvAnnotationFile = false; |
749 | ? | while ((line = in.readLine()) != null) |
750 | { | |
751 | 146 | nlinesread++; |
752 | 146 | lastread = new String(line); |
753 | 146 | if (line.indexOf("#") == 0) |
754 | { | |
755 | 18 | continue; |
756 | } | |
757 | ||
758 | 128 | if (line.indexOf("JALVIEW_ANNOTATION") > -1) |
759 | { | |
760 | 23 | jvAnnotationFile = true; |
761 | 23 | break; |
762 | } | |
763 | } | |
764 | ||
765 | 26 | if (!jvAnnotationFile) |
766 | { | |
767 | 3 | in.close(); |
768 | 3 | return false; |
769 | } | |
770 | ||
771 | ? | while ((line = in.readLine()) != null) |
772 | { | |
773 | 1076 | nlinesread++; |
774 | 1076 | lastread = new String(line); |
775 | 1076 | if (line.indexOf("#") == 0 |
776 | || line.indexOf("JALVIEW_ANNOTATION") > -1 | |
777 | || line.length() == 0) | |
778 | { | |
779 | 234 | continue; |
780 | } | |
781 | ||
782 | 842 | st = new StringTokenizer(line, "\t"); |
783 | 842 | token = st.nextToken(); |
784 | 842 | if (token.equalsIgnoreCase("COLOUR")) |
785 | { | |
786 | // TODO: use graduated colour def'n here too | |
787 | 172 | colourAnnotations(al, st.nextToken(), st.nextToken()); |
788 | 172 | modified = true; |
789 | 172 | continue; |
790 | } | |
791 | ||
792 | 670 | else if (token.equalsIgnoreCase(COMBINE)) |
793 | { | |
794 | // keep a record of current state and resolve groupRef at end | |
795 | 81 | combineAnnotation_calls |
796 | .add(new Object[] | |
797 | { st, refSeq, groupRef }); | |
798 | 81 | modified = true; |
799 | 81 | continue; |
800 | } | |
801 | 589 | else if (token.equalsIgnoreCase("ROWPROPERTIES")) |
802 | { | |
803 | 8 | addRowProperties(al, st); |
804 | 8 | modified = true; |
805 | 8 | continue; |
806 | } | |
807 | 581 | else if (token.equalsIgnoreCase(GRAPHLINE)) |
808 | { | |
809 | // resolve at end | |
810 | 154 | deferredAnnotation_calls |
811 | .add(new Object[] | |
812 | { GRAPHLINE, st, refSeq, groupRef }); | |
813 | 154 | modified = true; |
814 | 154 | continue; |
815 | } | |
816 | ||
817 | 427 | else if (token.equalsIgnoreCase("SEQUENCE_REF")) |
818 | { | |
819 | 165 | if (st.hasMoreTokens()) |
820 | { | |
821 | 161 | refSeq = al.findName(refSeqId = st.nextToken()); |
822 | 161 | if (refSeq == null) |
823 | { | |
824 | 0 | refSeqId = null; |
825 | } | |
826 | 161 | try |
827 | { | |
828 | 161 | refSeqIndex = Integer.parseInt(st.nextToken()); |
829 | 2 | if (refSeqIndex < 1) |
830 | { | |
831 | 0 | refSeqIndex = 1; |
832 | 0 | jalview.bin.Console.outPrintln( |
833 | "WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile"); | |
834 | } | |
835 | } catch (Exception ex) | |
836 | { | |
837 | 159 | refSeqIndex = 1; |
838 | } | |
839 | } | |
840 | else | |
841 | { | |
842 | 4 | refSeq = null; |
843 | 4 | refSeqId = null; |
844 | } | |
845 | 165 | continue; |
846 | } | |
847 | 262 | else if (token.equalsIgnoreCase("GROUP_REF")) |
848 | { | |
849 | // Group references could be forward or backwards, so they are | |
850 | // resolved after the whole file is read in | |
851 | 0 | groupRef = null; |
852 | 0 | if (st.hasMoreTokens()) |
853 | { | |
854 | 0 | groupRef = st.nextToken(); |
855 | 0 | if (groupRef.length() < 1) |
856 | { | |
857 | 0 | groupRef = null; // empty string |
858 | } | |
859 | else | |
860 | { | |
861 | 0 | if (groupRefRows.get(groupRef) == null) |
862 | { | |
863 | 0 | groupRefRows.put(groupRef, new Vector()); |
864 | } | |
865 | } | |
866 | } | |
867 | 0 | continue; |
868 | } | |
869 | 262 | else if (token.equalsIgnoreCase("SEQUENCE_GROUP")) |
870 | { | |
871 | 22 | addGroup(al, st); |
872 | 22 | modified = true; |
873 | 22 | continue; |
874 | } | |
875 | ||
876 | 240 | else if (token.equalsIgnoreCase("PROPERTIES")) |
877 | { | |
878 | 18 | addProperties(al, st); |
879 | 18 | modified = true; |
880 | 18 | continue; |
881 | } | |
882 | ||
883 | 222 | else if (token.equalsIgnoreCase("BELOW_ALIGNMENT")) |
884 | { | |
885 | 0 | setBelowAlignment(al, st); |
886 | 0 | modified = true; |
887 | 0 | continue; |
888 | } | |
889 | 222 | else if (token.equalsIgnoreCase("ALIGNMENT")) |
890 | { | |
891 | 5 | addAlignmentDetails(al, st); |
892 | 5 | modified = true; |
893 | 5 | continue; |
894 | } | |
895 | // else if (token.equalsIgnoreCase("VIEW_DEF")) | |
896 | // { | |
897 | // addOrSetView(al,st); | |
898 | // modified = true; | |
899 | // continue; | |
900 | // } | |
901 | 217 | else if (token.equalsIgnoreCase("VIEW_SETREF")) |
902 | { | |
903 | 2 | if (refSeq != null) |
904 | { | |
905 | 1 | al.setSeqrep(refSeq); |
906 | } | |
907 | 2 | modified = true; |
908 | 2 | continue; |
909 | } | |
910 | 215 | else if (token.equalsIgnoreCase("VIEW_HIDECOLS")) |
911 | { | |
912 | 1 | if (st.hasMoreTokens()) |
913 | { | |
914 | 1 | if (hidden == null) |
915 | { | |
916 | 1 | hidden = new HiddenColumns(); |
917 | } | |
918 | 1 | parseHideCols(hidden, st.nextToken()); |
919 | } | |
920 | 1 | modified = true; |
921 | 1 | continue; |
922 | } | |
923 | 214 | else if (token.equalsIgnoreCase("HIDE_INSERTIONS")) |
924 | { | |
925 | 1 | SequenceI sr = refSeq == null ? al.getSeqrep() : refSeq; |
926 | 1 | if (sr == null) |
927 | { | |
928 | 0 | sr = al.getSequenceAt(0); |
929 | } | |
930 | 1 | if (sr != null) |
931 | { | |
932 | 1 | if (hidden == null) |
933 | { | |
934 | 0 | jalview.bin.Console.errPrintln( |
935 | "Cannot process HIDE_INSERTIONS without an alignment view: Ignoring line: " | |
936 | + line); | |
937 | } | |
938 | else | |
939 | { | |
940 | // consider deferring this till after the file has been parsed ? | |
941 | 1 | hidden.hideList(sr.getInsertions()); |
942 | } | |
943 | } | |
944 | 1 | modified = true; |
945 | 1 | continue; |
946 | } | |
947 | ||
948 | // Parse out the annotation row | |
949 | 213 | graphStyle = AlignmentAnnotation.getGraphValueFromString(token); |
950 | 213 | label = st.nextToken(); |
951 | ||
952 | 213 | index = 0; |
953 | 213 | annotations = new Annotation[alWidth]; |
954 | 213 | description = null; |
955 | 213 | float score = Float.NaN; |
956 | ||
957 | 213 | if (st.hasMoreTokens()) |
958 | { | |
959 | 213 | line = st.nextToken(); |
960 | ||
961 | 213 | if (line.indexOf("|") == -1) |
962 | { | |
963 | 187 | description = line; |
964 | 187 | if (st.hasMoreTokens()) |
965 | { | |
966 | 187 | line = st.nextToken(); |
967 | } | |
968 | } | |
969 | ||
970 | 213 | if (st.hasMoreTokens()) |
971 | { | |
972 | // This must be the score | |
973 | 12 | score = Float.valueOf(st.nextToken()).floatValue(); |
974 | } | |
975 | ||
976 | 213 | st = new StringTokenizer(line, "|", true); |
977 | ||
978 | 213 | boolean emptyColumn = true; |
979 | 213 | boolean onlyOneElement = (st.countTokens() == 1); |
980 | ||
981 | 50121 | while (st.hasMoreElements() && index < alWidth) |
982 | { | |
983 | 49908 | token = st.nextToken().trim(); |
984 | ||
985 | 49908 | if (onlyOneElement) |
986 | { | |
987 | 0 | try |
988 | { | |
989 | 0 | score = Float.valueOf(token).floatValue(); |
990 | 0 | break; |
991 | } catch (NumberFormatException ex) | |
992 | { | |
993 | } | |
994 | } | |
995 | ||
996 | 49908 | if (token.equals("|")) |
997 | { | |
998 | 26769 | if (emptyColumn) |
999 | { | |
1000 | 3662 | index++; |
1001 | } | |
1002 | ||
1003 | 26769 | emptyColumn = true; |
1004 | } | |
1005 | else | |
1006 | { | |
1007 | 23139 | annotations[index++] = parseAnnotation(token, graphStyle); |
1008 | 23139 | emptyColumn = false; |
1009 | } | |
1010 | } | |
1011 | ||
1012 | } | |
1013 | ||
1014 | 213 | annotation = new AlignmentAnnotation(label, description, |
1015 | 213 | (index == 0) ? null : annotations, 0, 0, graphStyle); |
1016 | ||
1017 | 213 | annotation.score = score; |
1018 | 213 | if (!overrideAutoAnnot && autoAnnots |
1019 | .containsKey(autoAnnotsKey(annotation, refSeq, groupRef))) | |
1020 | { | |
1021 | // skip - we've already got an automatic annotation of this type. | |
1022 | 3 | continue; |
1023 | } | |
1024 | // otherwise add it! | |
1025 | 210 | if (refSeq != null) |
1026 | { | |
1027 | ||
1028 | 181 | annotation.belowAlignment = false; |
1029 | // make a copy of refSeq so we can find other matches in the alignment | |
1030 | 181 | SequenceI referedSeq = refSeq; |
1031 | 181 | do |
1032 | { | |
1033 | // copy before we do any mapping business. | |
1034 | // TODO: verify that undo/redo with 1:many sequence associated | |
1035 | // annotations can be undone correctly | |
1036 | 181 | AlignmentAnnotation ann = new AlignmentAnnotation(annotation); |
1037 | 181 | annotation.createSequenceMapping(referedSeq, refSeqIndex, |
1038 | false); | |
1039 | 181 | annotation.adjustForAlignment(); |
1040 | 181 | referedSeq.addAlignmentAnnotation(annotation); |
1041 | 181 | al.addAnnotation(annotation); |
1042 | 181 | al.setAnnotationIndex(annotation, |
1043 | al.getAlignmentAnnotation().length - existingAnnotations | |
1044 | - 1); | |
1045 | 181 | if (groupRef != null) |
1046 | { | |
1047 | 0 | ((Vector) groupRefRows.get(groupRef)).addElement(annotation); |
1048 | } | |
1049 | // and recover our virgin copy to use again if necessary. | |
1050 | 181 | annotation = ann; |
1051 | ||
1052 | ? | } while (refSeqId != null && (referedSeq = al.findName(referedSeq, |
1053 | refSeqId, true)) != null); | |
1054 | } | |
1055 | else | |
1056 | { | |
1057 | 29 | al.addAnnotation(annotation); |
1058 | 29 | al.setAnnotationIndex(annotation, |
1059 | al.getAlignmentAnnotation().length - existingAnnotations | |
1060 | - 1); | |
1061 | 29 | if (groupRef != null) |
1062 | { | |
1063 | 0 | ((Vector) groupRefRows.get(groupRef)).addElement(annotation); |
1064 | } | |
1065 | } | |
1066 | // and set modification flag | |
1067 | 210 | modified = true; |
1068 | } | |
1069 | // Resolve the groupRefs | |
1070 | 23 | Hashtable<String, SequenceGroup> groupRefLookup = new Hashtable<>(); |
1071 | 23 | Enumeration en = groupRefRows.keys(); |
1072 | ||
1073 | 23 | while (en.hasMoreElements()) |
1074 | { | |
1075 | 0 | groupRef = (String) en.nextElement(); |
1076 | 0 | boolean matched = false; |
1077 | // Resolve group: TODO: add a getGroupByName method to alignments | |
1078 | 0 | for (SequenceGroup theGroup : al.getGroups()) |
1079 | { | |
1080 | 0 | if (theGroup.getName().equals(groupRef)) |
1081 | { | |
1082 | 0 | if (matched) |
1083 | { | |
1084 | // TODO: specify and implement duplication of alignment annotation | |
1085 | // for multiple group references. | |
1086 | 0 | jalview.bin.Console.errPrintln( |
1087 | "Ignoring 1:many group reference mappings for group name '" | |
1088 | + groupRef + "'"); | |
1089 | } | |
1090 | else | |
1091 | { | |
1092 | 0 | matched = true; |
1093 | 0 | Vector rowset = (Vector) groupRefRows.get(groupRef); |
1094 | 0 | groupRefLookup.put(groupRef, theGroup); |
1095 | 0 | if (rowset != null && rowset.size() > 0) |
1096 | { | |
1097 | 0 | AlignmentAnnotation alan = null; |
1098 | 0 | for (int elm = 0, elmSize = rowset |
1099 | 0 | .size(); elm < elmSize; elm++) |
1100 | { | |
1101 | 0 | alan = (AlignmentAnnotation) rowset.elementAt(elm); |
1102 | 0 | alan.groupRef = theGroup; |
1103 | } | |
1104 | } | |
1105 | } | |
1106 | } | |
1107 | } | |
1108 | 0 | ((Vector) groupRefRows.get(groupRef)).removeAllElements(); |
1109 | } | |
1110 | // process any deferred attribute settings for each context | |
1111 | 23 | for (Object[] _deferred_args : deferredAnnotation_calls) |
1112 | { | |
1113 | 154 | if (_deferred_args[0] == GRAPHLINE) |
1114 | { | |
1115 | 154 | addLine(al, (StringTokenizer) _deferred_args[1], // st |
1116 | (SequenceI) _deferred_args[2], // refSeq | |
1117 | 154 | (_deferred_args[3] == null) ? null |
1118 | : groupRefLookup.get(_deferred_args[3]) // the | |
1119 | // reference | |
1120 | // group, or | |
1121 | // null | |
1122 | ); | |
1123 | } | |
1124 | } | |
1125 | ||
1126 | // finally, combine all the annotation rows within each context. | |
1127 | /** | |
1128 | * number of combine statements in this annotation file. Used to create | |
1129 | * new groups for combined annotation graphs without disturbing existing | |
1130 | * ones | |
1131 | */ | |
1132 | 23 | int combinecount = 0; |
1133 | 23 | for (Object[] _combine_args : combineAnnotation_calls) |
1134 | { | |
1135 | 81 | combineAnnotations(al, ++combinecount, |
1136 | (StringTokenizer) _combine_args[0], // st | |
1137 | (SequenceI) _combine_args[1], // refSeq | |
1138 | 81 | (_combine_args[2] == null) ? null |
1139 | : groupRefLookup.get(_combine_args[2]) // the reference | |
1140 | // group, | |
1141 | // or null | |
1142 | ); | |
1143 | } | |
1144 | } | |
1145 | 23 | return modified; |
1146 | } | |
1147 | ||
1148 | 1 | private void parseHideCols(HiddenColumns hidden, String nextToken) |
1149 | { | |
1150 | 1 | StringTokenizer inval = new StringTokenizer(nextToken, ","); |
1151 | 7 | while (inval.hasMoreTokens()) |
1152 | { | |
1153 | 6 | String range = inval.nextToken().trim(); |
1154 | 6 | int from, to = range.indexOf("-"); |
1155 | 6 | if (to == -1) |
1156 | { | |
1157 | 0 | from = to = Integer.parseInt(range); |
1158 | 0 | if (from >= 0) |
1159 | { | |
1160 | 0 | hidden.hideColumns(from, to); |
1161 | } | |
1162 | } | |
1163 | else | |
1164 | { | |
1165 | 6 | from = Integer.parseInt(range.substring(0, to)); |
1166 | 6 | if (to < range.length() - 1) |
1167 | { | |
1168 | 6 | to = Integer.parseInt(range.substring(to + 1)); |
1169 | } | |
1170 | else | |
1171 | { | |
1172 | 0 | to = from; |
1173 | } | |
1174 | 6 | if (from > 0 && to >= from) |
1175 | { | |
1176 | 6 | hidden.hideColumns(from, to); |
1177 | } | |
1178 | } | |
1179 | } | |
1180 | } | |
1181 | ||
1182 | 245 | private Object autoAnnotsKey(AlignmentAnnotation annotation, |
1183 | SequenceI refSeq, String groupRef) | |
1184 | { | |
1185 | 245 | return annotation.graph + "\t" + annotation.label + "\t" |
1186 | + annotation.description + "\t" | |
1187 | 245 | + (refSeq != null ? refSeq.getDisplayId(true) : ""); |
1188 | } | |
1189 | ||
1190 | 23139 | Annotation parseAnnotation(String string, int graphStyle) |
1191 | { | |
1192 | // don't do the glyph test if we don't want secondary structure | |
1193 | 23139 | boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); |
1194 | 23139 | String desc = null, displayChar = null; |
1195 | 23139 | char ss = ' '; // secondaryStructure |
1196 | 23139 | float value = 0; |
1197 | 23139 | boolean parsedValue = false, dcset = false; |
1198 | ||
1199 | // find colour here | |
1200 | 23139 | Color colour = null; |
1201 | 23139 | int i = string.indexOf("["); |
1202 | 23139 | int j = string.indexOf("]"); |
1203 | 23139 | if (i > -1 && j > -1) |
1204 | { | |
1205 | 21882 | colour = ColorUtils.parseColourString(string.substring(i + 1, j)); |
1206 | 21882 | if (i > 0 && string.charAt(i - 1) == ',') |
1207 | { | |
1208 | // clip the preceding comma as well | |
1209 | 21882 | i--; |
1210 | } | |
1211 | 21882 | string = string.substring(0, i) + string.substring(j + 1); |
1212 | } | |
1213 | ||
1214 | 23139 | StringTokenizer st = new StringTokenizer(string, ",", true); |
1215 | 23139 | String token; |
1216 | 23139 | boolean seenContent = false; |
1217 | 23139 | int pass = 0; |
1218 | 94252 | while (st.hasMoreTokens()) |
1219 | { | |
1220 | 71113 | pass++; |
1221 | 71113 | token = st.nextToken().trim(); |
1222 | 71113 | if (token.equals(",")) |
1223 | { | |
1224 | 23991 | if (!seenContent && parsedValue && !dcset) |
1225 | { | |
1226 | // allow the value below the bar/line to be empty | |
1227 | 0 | dcset = true; |
1228 | 0 | displayChar = " "; |
1229 | } | |
1230 | 23991 | seenContent = false; |
1231 | 23991 | continue; |
1232 | } | |
1233 | else | |
1234 | { | |
1235 | 47122 | seenContent = true; |
1236 | } | |
1237 | ||
1238 | 47122 | if (!parsedValue) |
1239 | { | |
1240 | 23215 | try |
1241 | { | |
1242 | 23215 | displayChar = token; |
1243 | // foo | |
1244 | 23215 | value = Float.valueOf(token).floatValue(); |
1245 | 22815 | parsedValue = true; |
1246 | 22815 | continue; |
1247 | } catch (NumberFormatException ex) | |
1248 | { | |
1249 | } | |
1250 | } | |
1251 | else | |
1252 | { | |
1253 | 23907 | if (token.length() == 1) |
1254 | { | |
1255 | 1340 | displayChar = token; |
1256 | } | |
1257 | } | |
1258 | 24307 | if (hasSymbols && (token.length() == 1 |
1259 | && "()<>[]{}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" | |
1260 | .contains(token))) | |
1261 | { | |
1262 | // Either this character represents a helix or sheet | |
1263 | // or an integer which can be displayed | |
1264 | 352 | ss = token.charAt(0); |
1265 | 352 | if (displayChar.equals(token.substring(0, 1))) |
1266 | { | |
1267 | 352 | displayChar = ""; |
1268 | } | |
1269 | } | |
1270 | 23955 | else if (desc == null || (parsedValue && pass > 2)) |
1271 | { | |
1272 | 23945 | desc = token; |
1273 | } | |
1274 | ||
1275 | } | |
1276 | // if (!dcset && string.charAt(string.length() - 1) == ',') | |
1277 | // { | |
1278 | // displayChar = " "; // empty display char symbol. | |
1279 | // } | |
1280 | 23139 | if (displayChar != null && desc != null && desc.length() == 1) |
1281 | { | |
1282 | 80 | if (displayChar.length() > 1) |
1283 | { | |
1284 | // switch desc and displayChar - legacy support | |
1285 | 0 | String tmp = displayChar; |
1286 | 0 | displayChar = desc; |
1287 | 0 | desc = tmp; |
1288 | } | |
1289 | else | |
1290 | { | |
1291 | 80 | if (displayChar.equals(desc)) |
1292 | { | |
1293 | // duplicate label - hangover from the 'robust parser' above | |
1294 | 80 | desc = null; |
1295 | } | |
1296 | } | |
1297 | } | |
1298 | 23139 | Annotation anot = new Annotation(displayChar, desc, ss, value); |
1299 | ||
1300 | 23139 | anot.colour = colour; |
1301 | ||
1302 | 23139 | return anot; |
1303 | } | |
1304 | ||
1305 | 172 | void colourAnnotations(AlignmentI al, String label, String colour) |
1306 | { | |
1307 | 172 | Color awtColour = ColorUtils.parseColourString(colour); |
1308 | 172 | Annotation[] annotations; |
1309 | 5322 | for (int i = 0; i < al.getAlignmentAnnotation().length; i++) |
1310 | { | |
1311 | 5150 | if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label)) |
1312 | { | |
1313 | 2270 | annotations = al.getAlignmentAnnotation()[i].annotations; |
1314 | 358660 | for (int j = 0; j < annotations.length; j++) |
1315 | { | |
1316 | 356390 | if (annotations[j] != null) |
1317 | { | |
1318 | 303946 | annotations[j].colour = awtColour; |
1319 | } | |
1320 | } | |
1321 | } | |
1322 | } | |
1323 | } | |
1324 | ||
1325 | 81 | void combineAnnotations(AlignmentI al, int combineCount, |
1326 | StringTokenizer st, SequenceI seqRef, SequenceGroup groupRef) | |
1327 | { | |
1328 | 81 | String group = st.nextToken(); |
1329 | // First make sure we are not overwriting the graphIndex | |
1330 | 81 | int graphGroup = 0; |
1331 | 81 | if (al.getAlignmentAnnotation() != null) |
1332 | { | |
1333 | 1321 | for (int i = 0; i < al.getAlignmentAnnotation().length; i++) |
1334 | { | |
1335 | 1321 | AlignmentAnnotation aa = al.getAlignmentAnnotation()[i]; |
1336 | ||
1337 | 1321 | if (aa.graphGroup > graphGroup) |
1338 | { | |
1339 | // try to number graphGroups in order of occurence. | |
1340 | 113 | graphGroup = aa.graphGroup + 1; |
1341 | } | |
1342 | 1321 | if (aa.sequenceRef == seqRef && aa.groupRef == groupRef |
1343 | && aa.label.equalsIgnoreCase(group)) | |
1344 | { | |
1345 | 81 | if (aa.graphGroup > -1) |
1346 | { | |
1347 | 0 | graphGroup = aa.graphGroup; |
1348 | } | |
1349 | else | |
1350 | { | |
1351 | 81 | if (graphGroup <= combineCount) |
1352 | { | |
1353 | 66 | graphGroup = combineCount + 1; |
1354 | } | |
1355 | 81 | aa.graphGroup = graphGroup; |
1356 | } | |
1357 | 81 | break; |
1358 | } | |
1359 | } | |
1360 | ||
1361 | // Now update groups | |
1362 | 162 | while (st.hasMoreTokens()) |
1363 | { | |
1364 | 81 | group = st.nextToken(); |
1365 | 1402 | for (int i = 0; i < al.getAlignmentAnnotation().length; i++) |
1366 | { | |
1367 | 1402 | AlignmentAnnotation aa = al.getAlignmentAnnotation()[i]; |
1368 | 1402 | if (aa.sequenceRef == seqRef && aa.groupRef == groupRef |
1369 | && aa.label.equalsIgnoreCase(group)) | |
1370 | { | |
1371 | 81 | aa.graphGroup = graphGroup; |
1372 | 81 | break; |
1373 | } | |
1374 | } | |
1375 | } | |
1376 | } | |
1377 | else | |
1378 | { | |
1379 | 0 | jalview.bin.Console.errPrintln( |
1380 | "Couldn't combine annotations. None are added to alignment yet!"); | |
1381 | } | |
1382 | } | |
1383 | ||
1384 | 154 | void addLine(AlignmentI al, StringTokenizer st, SequenceI seqRef, |
1385 | SequenceGroup groupRef) | |
1386 | { | |
1387 | 154 | String group = st.nextToken(); |
1388 | 154 | AlignmentAnnotation[] alannot = al.getAlignmentAnnotation(); |
1389 | 154 | String nextToken = st.nextToken(); |
1390 | 154 | float value = 0f; |
1391 | 154 | try |
1392 | { | |
1393 | 154 | value = Float.valueOf(nextToken); |
1394 | } catch (NumberFormatException e) | |
1395 | { | |
1396 | 0 | jalview.bin.Console.errPrintln("line " + nlinesread + ": Threshold '" |
1397 | + nextToken + "' invalid, setting to zero"); | |
1398 | } | |
1399 | 154 | String label = st.hasMoreTokens() ? st.nextToken() : null; |
1400 | 154 | Color colour = null; |
1401 | 154 | if (st.hasMoreTokens()) |
1402 | { | |
1403 | 154 | colour = ColorUtils.parseColourString(st.nextToken()); |
1404 | } | |
1405 | 154 | if (alannot != null) |
1406 | { | |
1407 | 5162 | for (int i = 0; i < alannot.length; i++) |
1408 | { | |
1409 | 5008 | if (alannot[i].label.equalsIgnoreCase(group) |
1410 | && (seqRef == null || alannot[i].sequenceRef == seqRef) | |
1411 | && (groupRef == null || alannot[i].groupRef == groupRef)) | |
1412 | { | |
1413 | 154 | alannot[i].setThreshold(new GraphLine(value, label, colour)); |
1414 | } | |
1415 | } | |
1416 | } | |
1417 | } | |
1418 | ||
1419 | 22 | void addGroup(AlignmentI al, StringTokenizer st) |
1420 | { | |
1421 | 22 | SequenceGroup sg = new SequenceGroup(); |
1422 | 22 | sg.setName(st.nextToken()); |
1423 | 22 | String rng = ""; |
1424 | 22 | try |
1425 | { | |
1426 | 22 | rng = st.nextToken(); |
1427 | 22 | if (rng.length() > 0 && !rng.startsWith("*")) |
1428 | { | |
1429 | 19 | sg.setStartRes(Integer.parseInt(rng) - 1); |
1430 | } | |
1431 | else | |
1432 | { | |
1433 | 3 | sg.setStartRes(0); |
1434 | } | |
1435 | 22 | rng = st.nextToken(); |
1436 | 22 | if (rng.length() > 0 && !rng.startsWith("*")) |
1437 | { | |
1438 | 19 | sg.setEndRes(Integer.parseInt(rng) - 1); |
1439 | } | |
1440 | else | |
1441 | { | |
1442 | 3 | sg.setEndRes(al.getWidth() - 1); |
1443 | } | |
1444 | } catch (Exception e) | |
1445 | { | |
1446 | 0 | jalview.bin.Console.errPrintln( |
1447 | "Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '" | |
1448 | + rng + "' - assuming alignment width for group."); | |
1449 | // assume group is full width | |
1450 | 0 | sg.setStartRes(0); |
1451 | 0 | sg.setEndRes(al.getWidth() - 1); |
1452 | } | |
1453 | ||
1454 | 22 | String index = st.nextToken(); |
1455 | 22 | if (index.equals("-1")) |
1456 | { | |
1457 | 99 | while (st.hasMoreElements()) |
1458 | { | |
1459 | 84 | sg.addSequence(al.findName(st.nextToken()), false); |
1460 | } | |
1461 | } | |
1462 | else | |
1463 | { | |
1464 | 7 | StringTokenizer st2 = new StringTokenizer(index, ","); |
1465 | ||
1466 | 14 | while (st2.hasMoreTokens()) |
1467 | { | |
1468 | 7 | String tmp = st2.nextToken(); |
1469 | 7 | if (tmp.equals("*")) |
1470 | { | |
1471 | 32 | for (int i = 0; i < al.getHeight(); i++) |
1472 | { | |
1473 | 30 | sg.addSequence(al.getSequenceAt(i), false); |
1474 | } | |
1475 | } | |
1476 | 5 | else if (tmp.indexOf("-") >= 0) |
1477 | { | |
1478 | 2 | StringTokenizer st3 = new StringTokenizer(tmp, "-"); |
1479 | ||
1480 | 2 | int start = (Integer.parseInt(st3.nextToken())); |
1481 | 2 | int end = (Integer.parseInt(st3.nextToken())); |
1482 | ||
1483 | 2 | if (end > start) |
1484 | { | |
1485 | 10 | for (int i = start; i <= end; i++) |
1486 | { | |
1487 | 8 | sg.addSequence(al.getSequenceAt(i - 1), false); |
1488 | } | |
1489 | } | |
1490 | } | |
1491 | else | |
1492 | { | |
1493 | 3 | sg.addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1), |
1494 | false); | |
1495 | } | |
1496 | } | |
1497 | } | |
1498 | ||
1499 | 22 | if (refSeq != null) |
1500 | { | |
1501 | 10 | sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1); |
1502 | 10 | sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1); |
1503 | 10 | sg.setSeqrep(refSeq); |
1504 | } | |
1505 | ||
1506 | 22 | if (sg.getSize() > 0) |
1507 | { | |
1508 | 20 | al.addGroup(sg); |
1509 | } | |
1510 | } | |
1511 | ||
1512 | 8 | void addRowProperties(AlignmentI al, StringTokenizer st) |
1513 | { | |
1514 | 8 | String label = st.nextToken(), keyValue, key, value; |
1515 | 8 | boolean scaletofit = false, centerlab = false, showalllabs = false; |
1516 | 32 | while (st.hasMoreTokens()) |
1517 | { | |
1518 | 24 | keyValue = st.nextToken(); |
1519 | 24 | key = keyValue.substring(0, keyValue.indexOf("=")); |
1520 | 24 | value = keyValue.substring(keyValue.indexOf("=") + 1); |
1521 | 24 | if (key.equalsIgnoreCase("scaletofit")) |
1522 | { | |
1523 | 8 | scaletofit = Boolean.valueOf(value).booleanValue(); |
1524 | } | |
1525 | 24 | if (key.equalsIgnoreCase("showalllabs")) |
1526 | { | |
1527 | 8 | showalllabs = Boolean.valueOf(value).booleanValue(); |
1528 | } | |
1529 | 24 | if (key.equalsIgnoreCase("centrelabs")) |
1530 | { | |
1531 | 8 | centerlab = Boolean.valueOf(value).booleanValue(); |
1532 | } | |
1533 | 24 | AlignmentAnnotation[] alr = al.getAlignmentAnnotation(); |
1534 | 24 | if (alr != null) |
1535 | { | |
1536 | 48 | for (int i = 0; i < alr.length; i++) |
1537 | { | |
1538 | 24 | if (alr[i].label.equalsIgnoreCase(label)) |
1539 | { | |
1540 | 24 | alr[i].centreColLabels = centerlab; |
1541 | 24 | alr[i].scaleColLabel = scaletofit; |
1542 | 24 | alr[i].showAllColLabels = showalllabs; |
1543 | } | |
1544 | } | |
1545 | } | |
1546 | } | |
1547 | } | |
1548 | ||
1549 | 18 | void addProperties(AlignmentI al, StringTokenizer st) |
1550 | { | |
1551 | ||
1552 | // So far we have only added groups to the annotationHash, | |
1553 | // the idea is in the future properties can be added to | |
1554 | // alignments, other annotations etc | |
1555 | 18 | if (al.getGroups() == null) |
1556 | { | |
1557 | 0 | return; |
1558 | } | |
1559 | ||
1560 | 18 | String name = st.nextToken(); |
1561 | ||
1562 | 18 | Map<String, String> properties = new HashMap<>(); |
1563 | 120 | while (st.hasMoreTokens()) |
1564 | { | |
1565 | 102 | String keyValue = st.nextToken(); |
1566 | 102 | String key = keyValue.substring(0, keyValue.indexOf("=")); |
1567 | 102 | String value = keyValue.substring(keyValue.indexOf("=") + 1); |
1568 | 102 | properties.put(key, value); |
1569 | } | |
1570 | ||
1571 | 18 | for (SequenceGroup sg : al.getGroups()) |
1572 | { | |
1573 | 39 | if (sg.getName().equals(name)) |
1574 | { | |
1575 | 17 | addProperties(sg, properties, al); |
1576 | } | |
1577 | } | |
1578 | } | |
1579 | ||
1580 | /** | |
1581 | * Helper method that applies any specified properties to a SequenceGroup | |
1582 | * | |
1583 | * @param sg | |
1584 | * @param properties | |
1585 | * @param al | |
1586 | */ | |
1587 | 17 | private void addProperties(SequenceGroup sg, |
1588 | Map<String, String> properties, AlignmentI al) | |
1589 | { | |
1590 | 17 | ColourSchemeI def = sg.getColourScheme(); |
1591 | 17 | for (String key : properties.keySet()) |
1592 | { | |
1593 | 102 | String value = properties.get(key); |
1594 | 102 | if (key.equalsIgnoreCase("description")) |
1595 | { | |
1596 | 4 | sg.setDescription(value); |
1597 | } | |
1598 | 98 | else if (key.equalsIgnoreCase("colour")) |
1599 | { | |
1600 | // TODO need to notify colourscheme of view reference once it is | |
1601 | // available | |
1602 | 9 | sg.cs.setColourScheme( |
1603 | ColourSchemeProperty.getColourScheme(null, al, value)); | |
1604 | } | |
1605 | 89 | else if (key.equalsIgnoreCase("pidThreshold")) |
1606 | { | |
1607 | 2 | sg.cs.setThreshold(Integer.parseInt(value), true); |
1608 | ||
1609 | } | |
1610 | 87 | else if (key |
1611 | .equalsIgnoreCase("secondaryStructureConservationThreshold")) | |
1612 | { | |
1613 | 0 | sg.cs.setConsensusSecondaryStructureThreshold( |
1614 | Integer.parseInt(value)); | |
1615 | 0 | sg.cs.setConsensusSecondaryStructureColouring(true); |
1616 | ||
1617 | } | |
1618 | 87 | else if (key.equalsIgnoreCase("consThreshold")) |
1619 | { | |
1620 | 0 | sg.cs.setConservationInc(Integer.parseInt(value)); |
1621 | 0 | Conservation c = new Conservation("Group", sg.getSequences(null), |
1622 | sg.getStartRes(), sg.getEndRes() + 1); | |
1623 | ||
1624 | 0 | c.calculate(); |
1625 | 0 | c.verdict(false, 25); // TODO: refer to conservation percent threshold |
1626 | ||
1627 | 0 | sg.cs.setConservation(c); |
1628 | ||
1629 | } | |
1630 | 87 | else if (key.equalsIgnoreCase("outlineColour")) |
1631 | { | |
1632 | 17 | sg.setOutlineColour(ColorUtils.parseColourString(value)); |
1633 | } | |
1634 | 70 | else if (key.equalsIgnoreCase("displayBoxes")) |
1635 | { | |
1636 | 13 | sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue()); |
1637 | } | |
1638 | 57 | else if (key.equalsIgnoreCase("showUnconserved")) |
1639 | { | |
1640 | 11 | sg.setShowNonconserved(Boolean.valueOf(value).booleanValue()); |
1641 | } | |
1642 | 46 | else if (key.equalsIgnoreCase("displayText")) |
1643 | { | |
1644 | 13 | sg.setDisplayText(Boolean.valueOf(value).booleanValue()); |
1645 | } | |
1646 | 33 | else if (key.equalsIgnoreCase("colourText")) |
1647 | { | |
1648 | 13 | sg.setColourText(Boolean.valueOf(value).booleanValue()); |
1649 | } | |
1650 | 20 | else if (key.equalsIgnoreCase("textCol1")) |
1651 | { | |
1652 | 7 | sg.textColour = ColorUtils.parseColourString(value); |
1653 | } | |
1654 | 13 | else if (key.equalsIgnoreCase("textCol2")) |
1655 | { | |
1656 | 9 | sg.textColour2 = ColorUtils.parseColourString(value); |
1657 | } | |
1658 | 4 | else if (key.equalsIgnoreCase("textColThreshold")) |
1659 | { | |
1660 | 2 | sg.thresholdTextColour = Integer.parseInt(value); |
1661 | } | |
1662 | 2 | else if (key.equalsIgnoreCase("idColour")) |
1663 | { | |
1664 | 2 | Color idColour = ColorUtils.parseColourString(value); |
1665 | 2 | sg.setIdColour(idColour == null ? Color.black : idColour); |
1666 | } | |
1667 | 0 | else if (key.equalsIgnoreCase("hide")) |
1668 | { | |
1669 | // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847 | |
1670 | 0 | sg.setHidereps(true); |
1671 | } | |
1672 | 0 | else if (key.equalsIgnoreCase("hidecols")) |
1673 | { | |
1674 | // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847 | |
1675 | 0 | sg.setHideCols(true); |
1676 | } | |
1677 | 102 | sg.recalcConservation(); |
1678 | } | |
1679 | ||
1680 | 17 | if (sg.getColourScheme() == null) |
1681 | { | |
1682 | 13 | sg.setColourScheme(def); |
1683 | } | |
1684 | } | |
1685 | ||
1686 | 0 | void setBelowAlignment(AlignmentI al, StringTokenizer st) |
1687 | { | |
1688 | 0 | String token; |
1689 | 0 | AlignmentAnnotation aa, ala[] = al.getAlignmentAnnotation(); |
1690 | 0 | if (ala == null) |
1691 | { | |
1692 | 0 | System.err.print( |
1693 | "Warning - no annotation to set below for sequence associated annotation:"); | |
1694 | } | |
1695 | 0 | while (st.hasMoreTokens()) |
1696 | { | |
1697 | 0 | token = st.nextToken(); |
1698 | 0 | if (ala == null) |
1699 | { | |
1700 | 0 | System.err.print(" " + token); |
1701 | } | |
1702 | else | |
1703 | { | |
1704 | 0 | for (int i = 0; i < al.getAlignmentAnnotation().length; i++) |
1705 | { | |
1706 | 0 | aa = al.getAlignmentAnnotation()[i]; |
1707 | 0 | if (aa.sequenceRef == refSeq && aa.label.equals(token)) |
1708 | { | |
1709 | 0 | aa.belowAlignment = true; |
1710 | } | |
1711 | } | |
1712 | } | |
1713 | } | |
1714 | 0 | if (ala == null) |
1715 | { | |
1716 | 0 | System.err.print("\n"); |
1717 | } | |
1718 | } | |
1719 | ||
1720 | 5 | void addAlignmentDetails(AlignmentI al, StringTokenizer st) |
1721 | { | |
1722 | 5 | String keyValue, key, value; |
1723 | 15 | while (st.hasMoreTokens()) |
1724 | { | |
1725 | 10 | keyValue = st.nextToken(); |
1726 | 10 | key = keyValue.substring(0, keyValue.indexOf("=")); |
1727 | 10 | value = keyValue.substring(keyValue.indexOf("=") + 1); |
1728 | 10 | al.setProperty(key, value); |
1729 | } | |
1730 | } | |
1731 | ||
1732 | /** | |
1733 | * Write annotations as a CSV file of the form 'label, value, value, ...' for | |
1734 | * each row. | |
1735 | * | |
1736 | * @param annotations | |
1737 | * @return CSV file as a string. | |
1738 | */ | |
1739 | 2 | public String printCSVAnnotations(AlignmentAnnotation[] annotations) |
1740 | { | |
1741 | 2 | if (annotations == null) |
1742 | { | |
1743 | 0 | return ""; |
1744 | } | |
1745 | 2 | StringBuffer sp = new StringBuffer(); |
1746 | 7 | for (int i = 0; i < annotations.length; i++) |
1747 | { | |
1748 | 5 | String atos = annotations[i].toString(); |
1749 | 5 | int p = 0; |
1750 | 5 | do |
1751 | { | |
1752 | 6 | int cp = atos.indexOf("\n", p); |
1753 | 6 | sp.append(annotations[i].label); |
1754 | 6 | sp.append(","); |
1755 | 6 | if (cp > p) |
1756 | { | |
1757 | 1 | sp.append(atos.substring(p, cp + 1)); |
1758 | } | |
1759 | else | |
1760 | { | |
1761 | 5 | sp.append(atos.substring(p)); |
1762 | 5 | sp.append(newline); |
1763 | } | |
1764 | 6 | p = cp + 1; |
1765 | 6 | } while (p > 0); |
1766 | } | |
1767 | 2 | return sp.toString(); |
1768 | } | |
1769 | ||
1770 | 0 | public String printAnnotationsForView(AlignViewportI viewport) |
1771 | { | |
1772 | 0 | return printAnnotations( |
1773 | 0 | viewport.isShowAnnotation() |
1774 | ? viewport.getAlignment().getAlignmentAnnotation() | |
1775 | : null, | |
1776 | viewport.getAlignment().getGroups(), | |
1777 | viewport.getAlignment().getProperties(), | |
1778 | viewport.getAlignment().getHiddenColumns(), | |
1779 | viewport.getAlignment(), null); | |
1780 | } | |
1781 | ||
1782 | 0 | public String printAnnotationsForAlignment(AlignmentI al) |
1783 | { | |
1784 | 0 | return printAnnotations(al.getAlignmentAnnotation(), al.getGroups(), |
1785 | al.getProperties(), null, al, null); | |
1786 | } | |
1787 | } |