Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
StructureSelectionManager | 70 | 471 | 226 |
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.structure; | |
22 | ||
23 | import java.io.PrintStream; | |
24 | import java.util.ArrayList; | |
25 | import java.util.Arrays; | |
26 | import java.util.Collections; | |
27 | import java.util.Enumeration; | |
28 | import java.util.HashMap; | |
29 | import java.util.IdentityHashMap; | |
30 | import java.util.List; | |
31 | import java.util.Locale; | |
32 | import java.util.Map; | |
33 | import java.util.Vector; | |
34 | ||
35 | import jalview.analysis.AlignSeq; | |
36 | import jalview.api.StructureSelectionManagerProvider; | |
37 | import jalview.bin.Cache; | |
38 | import jalview.bin.Console; | |
39 | import jalview.commands.CommandI; | |
40 | import jalview.commands.EditCommand; | |
41 | import jalview.commands.OrderCommand; | |
42 | import jalview.datamodel.AlignedCodonFrame; | |
43 | import jalview.datamodel.AlignmentAnnotation; | |
44 | import jalview.datamodel.AlignmentI; | |
45 | import jalview.datamodel.Annotation; | |
46 | import jalview.datamodel.ContiguousI; | |
47 | import jalview.datamodel.HiddenColumns; | |
48 | import jalview.datamodel.PDBEntry; | |
49 | import jalview.datamodel.SearchResults; | |
50 | import jalview.datamodel.SearchResultsI; | |
51 | import jalview.datamodel.SequenceI; | |
52 | import jalview.ext.jmol.JmolParser; | |
53 | import jalview.gui.IProgressIndicator; | |
54 | import jalview.gui.Preferences; | |
55 | import jalview.io.AppletFormatAdapter; | |
56 | import jalview.io.DataSourceType; | |
57 | import jalview.io.StructureFile; | |
58 | import jalview.structure.StructureImportSettings.TFType; | |
59 | import jalview.util.Constants; | |
60 | import jalview.util.MappingUtils; | |
61 | import jalview.util.MessageManager; | |
62 | import jalview.util.Platform; | |
63 | import jalview.ws.sifts.SiftsClient; | |
64 | import jalview.ws.sifts.SiftsException; | |
65 | import jalview.ws.sifts.SiftsSettings; | |
66 | import mc_view.Atom; | |
67 | import mc_view.PDBChain; | |
68 | import mc_view.PDBfile; | |
69 | ||
70 | public class StructureSelectionManager | |
71 | { | |
72 | public final static String NEWLINE = System.lineSeparator(); | |
73 | ||
74 | static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances; | |
75 | ||
76 | private List<StructureMapping> mappings = new ArrayList<>(); | |
77 | ||
78 | private boolean processSecondaryStructure = false; | |
79 | ||
80 | private boolean secStructServices = false; | |
81 | ||
82 | private boolean addTempFacAnnot = false; | |
83 | ||
84 | /* | |
85 | * Set of any registered mappings between (dataset) sequences. | |
86 | */ | |
87 | private List<AlignedCodonFrame> seqmappings = new ArrayList<>(); | |
88 | ||
89 | private List<CommandListener> commandListeners = new ArrayList<>(); | |
90 | ||
91 | private List<SelectionListener> sel_listeners = new ArrayList<>(); | |
92 | ||
93 | /** | |
94 | * @return true if will try to use external services for processing secondary | |
95 | * structure | |
96 | */ | |
97 | 0 | public boolean isSecStructServices() |
98 | { | |
99 | 0 | return secStructServices; |
100 | } | |
101 | ||
102 | /** | |
103 | * control use of external services for processing secondary structure | |
104 | * | |
105 | * @param secStructServices | |
106 | */ | |
107 | 127 | public void setSecStructServices(boolean secStructServices) |
108 | { | |
109 | 127 | this.secStructServices = secStructServices; |
110 | } | |
111 | ||
112 | /** | |
113 | * flag controlling addition of any kind of structural annotation | |
114 | * | |
115 | * @return true if temperature factor annotation will be added | |
116 | */ | |
117 | 0 | public boolean isAddTempFacAnnot() |
118 | { | |
119 | 0 | return addTempFacAnnot; |
120 | } | |
121 | ||
122 | /** | |
123 | * set flag controlling addition of structural annotation | |
124 | * | |
125 | * @param addTempFacAnnot | |
126 | */ | |
127 | 129 | public void setAddTempFacAnnot(boolean addTempFacAnnot) |
128 | { | |
129 | 129 | this.addTempFacAnnot = addTempFacAnnot; |
130 | } | |
131 | ||
132 | /** | |
133 | * | |
134 | * @return if true, the structure manager will attempt to add secondary | |
135 | * structure lines for unannotated sequences | |
136 | */ | |
137 | ||
138 | 0 | public boolean isProcessSecondaryStructure() |
139 | { | |
140 | 0 | return processSecondaryStructure; |
141 | } | |
142 | ||
143 | /** | |
144 | * Control whether structure manager will try to annotate mapped sequences | |
145 | * with secondary structure from PDB data. | |
146 | * | |
147 | * @param enable | |
148 | */ | |
149 | 129 | public void setProcessSecondaryStructure(boolean enable) |
150 | { | |
151 | 129 | processSecondaryStructure = enable; |
152 | } | |
153 | ||
154 | /** | |
155 | * debug function - write all mappings to stdout | |
156 | */ | |
157 | 0 | public void reportMapping() |
158 | { | |
159 | 0 | if (mappings.isEmpty()) |
160 | { | |
161 | 0 | jalview.bin.Console |
162 | .errPrintln("reportMapping: No PDB/Sequence mappings."); | |
163 | } | |
164 | else | |
165 | { | |
166 | 0 | jalview.bin.Console.errPrintln( |
167 | "reportMapping: There are " + mappings.size() + " mappings."); | |
168 | 0 | int i = 0; |
169 | 0 | for (StructureMapping sm : mappings) |
170 | { | |
171 | 0 | jalview.bin.Console |
172 | .errPrintln("mapping " + i++ + " : " + sm.pdbfile); | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | /** | |
178 | * map between the PDB IDs (or structure identifiers) used by Jalview and the | |
179 | * absolute filenames for PDB data that corresponds to it | |
180 | */ | |
181 | Map<String, String> pdbIdFileName = new HashMap<>(); | |
182 | ||
183 | Map<String, String> pdbFileNameId = new HashMap<>(); | |
184 | ||
185 | 276 | public void registerPDBFile(String idForFile, String absoluteFile) |
186 | { | |
187 | 276 | pdbIdFileName.put(idForFile, absoluteFile); |
188 | 276 | pdbFileNameId.put(absoluteFile, idForFile); |
189 | } | |
190 | ||
191 | 250 | public String findIdForPDBFile(String idOrFile) |
192 | { | |
193 | 250 | String id = pdbFileNameId.get(idOrFile); |
194 | 250 | return id; |
195 | } | |
196 | ||
197 | 1 | public String findFileForPDBId(String idOrFile) |
198 | { | |
199 | 1 | String id = pdbIdFileName.get(idOrFile); |
200 | 1 | return id; |
201 | } | |
202 | ||
203 | 134 | public boolean isPDBFileRegistered(String idOrFile) |
204 | { | |
205 | 134 | return pdbFileNameId.containsKey(idOrFile) |
206 | || pdbIdFileName.containsKey(idOrFile); | |
207 | } | |
208 | ||
209 | private static StructureSelectionManager nullProvider = null; | |
210 | ||
211 | 6086 | public static StructureSelectionManager getStructureSelectionManager( |
212 | StructureSelectionManagerProvider context) | |
213 | { | |
214 | 6086 | if (context == null) |
215 | { | |
216 | 607 | if (nullProvider == null) |
217 | { | |
218 | 47 | if (instances != null) |
219 | { | |
220 | 0 | throw new Error(MessageManager.getString( |
221 | "error.implementation_error_structure_selection_manager_null"), | |
222 | new NullPointerException(MessageManager | |
223 | .getString("exception.ssm_context_is_null"))); | |
224 | } | |
225 | else | |
226 | { | |
227 | 47 | nullProvider = new StructureSelectionManager(); |
228 | } | |
229 | 47 | return nullProvider; |
230 | } | |
231 | } | |
232 | 6039 | if (instances == null) |
233 | { | |
234 | 54 | instances = new java.util.IdentityHashMap<>(); |
235 | } | |
236 | 6039 | StructureSelectionManager instance = instances.get(context); |
237 | 6039 | if (instance == null) |
238 | { | |
239 | 122 | if (nullProvider != null) |
240 | { | |
241 | 115 | instance = nullProvider; |
242 | } | |
243 | else | |
244 | { | |
245 | 7 | instance = new StructureSelectionManager(); |
246 | } | |
247 | 122 | instances.put(context, instance); |
248 | } | |
249 | 6038 | return instance; |
250 | } | |
251 | ||
252 | /** | |
253 | * flag controlling whether SeqMappings are relayed from received sequence | |
254 | * mouse over events to other sequences | |
255 | */ | |
256 | boolean relaySeqMappings = true; | |
257 | ||
258 | /** | |
259 | * Enable or disable relay of seqMapping events to other sequences. You might | |
260 | * want to do this if there are many sequence mappings and the host computer | |
261 | * is slow | |
262 | * | |
263 | * @param relay | |
264 | */ | |
265 | 0 | public void setRelaySeqMappings(boolean relay) |
266 | { | |
267 | 0 | relaySeqMappings = relay; |
268 | } | |
269 | ||
270 | /** | |
271 | * get the state of the relay seqMappings flag. | |
272 | * | |
273 | * @return true if sequence mouse overs are being relayed to other mapped | |
274 | * sequences | |
275 | */ | |
276 | 0 | public boolean isRelaySeqMappingsEnabled() |
277 | { | |
278 | 0 | return relaySeqMappings; |
279 | } | |
280 | ||
281 | Vector listeners = new Vector(); | |
282 | ||
283 | /** | |
284 | * register a listener for alignment sequence mouseover events | |
285 | * | |
286 | * @param svl | |
287 | */ | |
288 | 524 | public void addStructureViewerListener(Object svl) |
289 | { | |
290 | 524 | if (!listeners.contains(svl)) |
291 | { | |
292 | 523 | listeners.addElement(svl); |
293 | } | |
294 | } | |
295 | ||
296 | /** | |
297 | * Returns the filename the PDB id is already mapped to if known, or null if | |
298 | * it is not mapped | |
299 | * | |
300 | * @param pdbid | |
301 | * @return | |
302 | */ | |
303 | 1 | public String alreadyMappedToFile(String pdbid) |
304 | { | |
305 | 1 | for (StructureMapping sm : mappings) |
306 | { | |
307 | 1 | if (sm.getPdbId().equalsIgnoreCase(pdbid)) |
308 | { | |
309 | 0 | return sm.pdbfile; |
310 | } | |
311 | } | |
312 | 1 | return null; |
313 | } | |
314 | ||
315 | /** | |
316 | * Import structure data and register a structure mapping for broadcasting | |
317 | * colouring, mouseovers and selection events (convenience wrapper). | |
318 | * | |
319 | * @param sequence | |
320 | * - one or more sequences to be mapped to pdbFile | |
321 | * @param targetChains | |
322 | * - optional chain specification for mapping each sequence to pdb | |
323 | * (may be nill, individual elements may be nill) | |
324 | * @param pdbFile | |
325 | * - structure data resource | |
326 | * @param protocol | |
327 | * - how to resolve data from resource | |
328 | * @return null or the structure data parsed as a pdb file | |
329 | */ | |
330 | 89 | synchronized public StructureFile setMapping(SequenceI[] sequence, |
331 | String[] targetChains, String pdbFile, DataSourceType protocol, | |
332 | IProgressIndicator progress) | |
333 | { | |
334 | 89 | return computeMapping(true, sequence, targetChains, pdbFile, protocol, |
335 | progress, null, null, true); | |
336 | } | |
337 | ||
338 | /** | |
339 | * Import a single structure file and register sequence structure mappings for | |
340 | * broadcasting colouring, mouseovers and selection events (convenience | |
341 | * wrapper). | |
342 | * | |
343 | * @param forStructureView | |
344 | * when true, record the mapping for use in mouseOvers | |
345 | * @param sequence | |
346 | * - one or more sequences to be mapped to pdbFile | |
347 | * @param targetChains | |
348 | * - optional chain specification for mapping each sequence to pdb | |
349 | * (may be nill, individual elements may be nill) | |
350 | * @param pdbFile | |
351 | * - structure data resource | |
352 | * @param protocol | |
353 | * - how to resolve data from resource | |
354 | * @return null or the structure data parsed as a pdb file | |
355 | */ | |
356 | 3 | synchronized public StructureFile setMapping(boolean forStructureView, |
357 | SequenceI[] sequenceArray, String[] targetChainIds, | |
358 | String pdbFile, DataSourceType sourceType, TFType tft, | |
359 | String paeFilename) | |
360 | { | |
361 | 3 | return setMapping(forStructureView, sequenceArray, targetChainIds, |
362 | pdbFile, sourceType, tft, paeFilename, true); | |
363 | } | |
364 | ||
365 | /** | |
366 | * create sequence structure mappings between each sequence and the given | |
367 | * pdbFile (retrieved via the given protocol). Either constructs a mapping | |
368 | * using NW alignment or derives one from any available SIFTS mapping data. | |
369 | * | |
370 | * @param forStructureView | |
371 | * when true, record the mapping for use in mouseOvers | |
372 | * | |
373 | * @param sequenceArray | |
374 | * - one or more sequences to be mapped to pdbFile | |
375 | * @param targetChainIds | |
376 | * - optional chain specification for mapping each sequence to pdb | |
377 | * (may be nill, individual elements may be nill) - JBPNote: JAL-2693 | |
378 | * - this should be List<List<String>>, empty lists indicate no | |
379 | * predefined mappings | |
380 | * @param pdbFile | |
381 | * - structure data resource | |
382 | * @param sourceType | |
383 | * - how to resolve data from resource | |
384 | * @param tft | |
385 | * - specify how to interpret the temperature factor column in the | |
386 | * atom data | |
387 | * @param paeFilename | |
388 | * - when not null, specifies a filename containing a matrix | |
389 | * formatted in JSON using one of the known PAE formats | |
390 | * @param doXferSettings | |
391 | * - when true, transfer annotation to mapped sequences in | |
392 | * sequenceArray | |
393 | * @return null or the structure data parsed as a pdb file | |
394 | */ | |
395 | 58 | synchronized public StructureFile setMapping(boolean forStructureView, |
396 | SequenceI[] sequenceArray, String[] targetChainIds, | |
397 | String pdbFile, DataSourceType sourceType, TFType tft, | |
398 | String paeFilename, boolean doXferSettings) | |
399 | { | |
400 | 58 | return computeMapping(forStructureView, sequenceArray, targetChainIds, |
401 | pdbFile, sourceType, null, tft, paeFilename, doXferSettings); | |
402 | } | |
403 | ||
404 | /** | |
405 | * create sequence structure mappings between each sequence and the given | |
406 | * pdbFile (retrieved via the given protocol). Either constructs a mapping | |
407 | * using NW alignment or derives one from any available SIFTS mapping data. | |
408 | * | |
409 | * @param forStructureView | |
410 | * when true, record the mapping for use in mouseOvers | |
411 | * | |
412 | * @param sequenceArray | |
413 | * - one or more sequences to be mapped to pdbFile | |
414 | * @param targetChainIds | |
415 | * - optional chain specification for mapping each sequence to pdb | |
416 | * (may be nill, individual elements may be nill) - JBPNote: JAL-2693 | |
417 | * - this should be List<List<String>>, empty lists indicate no | |
418 | * predefined mappings | |
419 | * @param pdbFile | |
420 | * - structure data resource | |
421 | * @param sourceType | |
422 | * - how to resolve data from resource | |
423 | * @param IProgressIndicator | |
424 | * reference to UI component that maintains a progress bar for the | |
425 | * mapping operation | |
426 | * @param tft | |
427 | * - specify how to interpret the temperature factor column in the | |
428 | * atom data | |
429 | * @param paeFilename | |
430 | * - when not null, specifies a filename containing a matrix | |
431 | * formatted in JSON using one of the known PAE formats | |
432 | * @param doXferSettings | |
433 | * - when true, transfer annotation to mapped sequences in | |
434 | * sequenceArray | |
435 | * @return null or the structure data parsed as a pdb file | |
436 | */ | |
437 | 149 | synchronized public StructureFile computeMapping(boolean forStructureView, |
438 | SequenceI[] sequenceArray, String[] targetChainIds, | |
439 | String pdbFile, DataSourceType sourceType, | |
440 | IProgressIndicator progress, TFType tft, String paeFilename, | |
441 | boolean doXferSettings) | |
442 | { | |
443 | 149 | long progressSessionId = System.currentTimeMillis() * 3; |
444 | ||
445 | /** | |
446 | * do we extract and transfer annotation from 3D data ? | |
447 | */ | |
448 | // FIXME: possibly should just delete | |
449 | ||
450 | 149 | boolean parseSecStr = processSecondaryStructure |
451 | && !isStructureFileProcessed(pdbFile, sequenceArray); | |
452 | ||
453 | 149 | StructureFile pdb = null; |
454 | 149 | boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts(); |
455 | 149 | try |
456 | { | |
457 | // FIXME if sourceType is not null, we've lost data here | |
458 | 149 | sourceType = AppletFormatAdapter.checkProtocol(pdbFile); |
459 | 149 | pdb = new JmolParser(false, pdbFile, sourceType); |
460 | 149 | if (paeFilename != null) |
461 | { | |
462 | 36 | pdb.setPAEMatrix(paeFilename); |
463 | } | |
464 | 149 | pdb.setTemperatureFactorType(tft); |
465 | 149 | pdb.addSettings(parseSecStr && processSecondaryStructure, |
466 | parseSecStr && addTempFacAnnot, | |
467 | parseSecStr && secStructServices); | |
468 | // save doXferSettings and reset after doParse() | |
469 | 149 | boolean temp = pdb.getDoXferSettings(); |
470 | 149 | pdb.setDoXferSettings(doXferSettings); |
471 | 149 | pdb.doParse(); |
472 | 149 | pdb.setDoXferSettings(temp); |
473 | 149 | if (pdb.getId() != null && pdb.getId().trim().length() > 0 |
474 | && DataSourceType.FILE == sourceType) | |
475 | { | |
476 | 137 | registerPDBFile(pdb.getId().trim(), pdbFile); |
477 | } | |
478 | // if PDBId is unavailable then skip SIFTS mapping execution path | |
479 | // TODO: JAL-3868 need to know if structure is actually from | |
480 | // PDB (has valid PDB ID and has provenance suggesting it | |
481 | // actually came from PDB) | |
482 | 149 | boolean isProtein = false; |
483 | 149 | for (SequenceI s : sequenceArray) |
484 | { | |
485 | 149 | if (s.isProtein()) |
486 | { | |
487 | 149 | isProtein = true; |
488 | 149 | break; |
489 | } | |
490 | } | |
491 | 149 | isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() |
492 | && !pdb.getId().startsWith("AF-") && isProtein; | |
493 | ||
494 | } catch (Exception ex) | |
495 | { | |
496 | 0 | ex.printStackTrace(); |
497 | 0 | return null; |
498 | } | |
499 | /* | |
500 | * sifts client - non null if SIFTS mappings are to be used | |
501 | */ | |
502 | 149 | SiftsClient siftsClient = null; |
503 | 149 | try |
504 | { | |
505 | 149 | if (isMapUsingSIFTs) |
506 | { | |
507 | 0 | siftsClient = new SiftsClient(pdb); |
508 | } | |
509 | } catch (SiftsException e) | |
510 | { | |
511 | 0 | isMapUsingSIFTs = false; |
512 | 0 | Console.error("SIFTS mapping failed", e); |
513 | 0 | Console.error("Falling back on Needleman & Wunsch alignment"); |
514 | 0 | siftsClient = null; |
515 | } | |
516 | ||
517 | 149 | String targetChainId; |
518 | 342 | for (int s = 0; s < sequenceArray.length; s++) |
519 | { | |
520 | 193 | boolean infChain = true; |
521 | 193 | final SequenceI seq = sequenceArray[s]; |
522 | 193 | SequenceI ds = seq; |
523 | 384 | while (ds.getDatasetSequence() != null) |
524 | { | |
525 | 191 | ds = ds.getDatasetSequence(); |
526 | } | |
527 | ||
528 | 193 | if (targetChainIds != null && targetChainIds[s] != null) |
529 | { | |
530 | 40 | infChain = false; |
531 | 40 | targetChainId = targetChainIds[s]; |
532 | } | |
533 | 153 | else if (seq.getName().indexOf("|") > -1) |
534 | { | |
535 | 58 | targetChainId = seq.getName() |
536 | .substring(seq.getName().lastIndexOf("|") + 1); | |
537 | 58 | if (targetChainId.length() > 1) |
538 | { | |
539 | 1 | if (targetChainId.trim().length() == 0) |
540 | { | |
541 | 0 | targetChainId = " "; |
542 | } | |
543 | else | |
544 | { | |
545 | // not a valid chain identifier | |
546 | 1 | targetChainId = ""; |
547 | } | |
548 | } | |
549 | } | |
550 | else | |
551 | { | |
552 | 95 | targetChainId = ""; |
553 | } | |
554 | ||
555 | /* | |
556 | * Attempt pairwise alignment of the sequence with each chain in the PDB, | |
557 | * and remember the highest scoring chain | |
558 | */ | |
559 | 193 | float max = -10; |
560 | 193 | AlignSeq maxAlignseq = null; |
561 | 193 | String maxChainId = " "; |
562 | 193 | PDBChain maxChain = null; |
563 | 193 | boolean first = true; |
564 | 193 | for (PDBChain chain : pdb.getChains()) |
565 | { | |
566 | 245 | if (targetChainId.length() > 0 && !targetChainId.equals(chain.id) |
567 | && !infChain) | |
568 | { | |
569 | 16 | continue; // don't try to map chains don't match. |
570 | } | |
571 | // TODO: correctly determine sequence type for mixed na/peptide | |
572 | // structures | |
573 | 229 | final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP; |
574 | 229 | AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence, |
575 | type); | |
576 | // equivalent to: | |
577 | // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type); | |
578 | // as.calcScoreMatrix(); | |
579 | // as.traceAlignment(); | |
580 | ||
581 | 229 | if (first || as.maxscore > max |
582 | || (as.maxscore == max && chain.id.equals(targetChainId))) | |
583 | { | |
584 | 201 | first = false; |
585 | 201 | maxChain = chain; |
586 | 201 | max = as.maxscore; |
587 | 201 | maxAlignseq = as; |
588 | 201 | maxChainId = chain.id; |
589 | } | |
590 | } | |
591 | 193 | if (maxChain == null) |
592 | { | |
593 | 0 | continue; |
594 | } | |
595 | ||
596 | 193 | if (sourceType == DataSourceType.PASTE) |
597 | { | |
598 | 16 | pdbFile = "INLINE" + pdb.getId(); |
599 | } | |
600 | ||
601 | 193 | List<StructureMapping> seqToStrucMapping = new ArrayList<>(); |
602 | 193 | String provider = null; |
603 | 193 | if (isMapUsingSIFTs && seq.isProtein()) |
604 | { | |
605 | 0 | if (progress != null) |
606 | { | |
607 | 0 | progress.setProgressBar( |
608 | MessageManager | |
609 | .getString("status.obtaining_mapping_with_sifts"), | |
610 | progressSessionId); | |
611 | } | |
612 | 0 | jalview.datamodel.Mapping sqmpping = maxAlignseq |
613 | .getMappingFromS1(false); | |
614 | 0 | if (targetChainId != null && !targetChainId.trim().isEmpty()) |
615 | { | |
616 | 0 | StructureMapping siftsMapping; |
617 | 0 | try |
618 | { | |
619 | 0 | siftsMapping = getStructureMapping(seq, pdbFile, targetChainId, |
620 | pdb, maxChain, sqmpping, maxAlignseq, siftsClient); | |
621 | 0 | seqToStrucMapping.add(siftsMapping); |
622 | 0 | maxChain.makeExactMapping(siftsMapping, seq); |
623 | 0 | maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS", |
624 | pdb.getId().toLowerCase(Locale.ROOT)); | |
625 | 0 | maxChain.transferResidueAnnotation(siftsMapping, null); |
626 | 0 | ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); |
627 | ||
628 | } catch (SiftsException e) | |
629 | { | |
630 | // fall back to NW alignment | |
631 | 0 | Console.error(e.getMessage()); |
632 | 0 | StructureMapping nwMapping = getNWMappings(seq, pdbFile, |
633 | targetChainId, maxChain, pdb, maxAlignseq); | |
634 | 0 | seqToStrucMapping.add(nwMapping); |
635 | 0 | maxChain.makeExactMapping(maxAlignseq, seq); |
636 | 0 | maxChain.transferRESNUMFeatures(seq, "IEA:Jalview", |
637 | pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is | |
638 | // "IEA:Jalview" ? | |
639 | 0 | maxChain.transferResidueAnnotation(nwMapping, sqmpping); |
640 | 0 | ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); |
641 | } | |
642 | } | |
643 | else | |
644 | { | |
645 | 0 | List<StructureMapping> foundSiftsMappings = new ArrayList<>(); |
646 | 0 | for (PDBChain chain : pdb.getChains()) |
647 | { | |
648 | 0 | StructureMapping siftsMapping = null; |
649 | 0 | try |
650 | { | |
651 | 0 | siftsMapping = getStructureMapping(seq, pdbFile, chain.id, |
652 | pdb, chain, sqmpping, maxAlignseq, siftsClient); | |
653 | 0 | foundSiftsMappings.add(siftsMapping); |
654 | 0 | chain.makeExactMapping(siftsMapping, seq); |
655 | 0 | chain.transferRESNUMFeatures(seq, "IEA: SIFTS", |
656 | pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this | |
657 | // "IEA:SIFTS" ? | |
658 | 0 | chain.transferResidueAnnotation(siftsMapping, null); |
659 | } catch (SiftsException e) | |
660 | { | |
661 | 0 | jalview.bin.Console.errPrintln(e.getMessage()); |
662 | } catch (Exception e) | |
663 | { | |
664 | 0 | jalview.bin.Console.errPrintln( |
665 | "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair"); | |
666 | 0 | jalview.bin.Console.errPrintln(e.getMessage()); |
667 | } | |
668 | } | |
669 | 0 | if (!foundSiftsMappings.isEmpty()) |
670 | { | |
671 | 0 | seqToStrucMapping.addAll(foundSiftsMappings); |
672 | 0 | ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0)); |
673 | } | |
674 | else | |
675 | { | |
676 | 0 | StructureMapping nwMapping = getNWMappings(seq, pdbFile, |
677 | maxChainId, maxChain, pdb, maxAlignseq); | |
678 | 0 | seqToStrucMapping.add(nwMapping); |
679 | 0 | maxChain.transferRESNUMFeatures(seq, null, |
680 | pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this | |
681 | // "IEA:Jalview" ? | |
682 | 0 | maxChain.transferResidueAnnotation(nwMapping, sqmpping); |
683 | 0 | ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); |
684 | } | |
685 | } | |
686 | } | |
687 | else | |
688 | { | |
689 | 193 | if (progress != null) |
690 | { | |
691 | 0 | progress.setProgressBar( |
692 | MessageManager.getString( | |
693 | "status.obtaining_mapping_with_nw_alignment"), | |
694 | progressSessionId); | |
695 | } | |
696 | 193 | StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId, |
697 | maxChain, pdb, maxAlignseq); | |
698 | 193 | seqToStrucMapping.add(nwMapping); |
699 | 193 | ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); |
700 | } | |
701 | ||
702 | // JAL-4392 TODO: Unable to match PDBProvider with Annotation without | |
703 | // matching struct file path | |
704 | 193 | String ssAnnotDescriptionInPDB = null; |
705 | 193 | String ssStructFilePathNameInPDB = pdb.getInFile(); // Structure file name |
706 | // in PDB data model | |
707 | // Secondary structure annotations in pdb data model | |
708 | 193 | AlignmentAnnotation[] ssAnnotationsInPDB = pdb.getSeqs().get(0) |
709 | .getAnnotation(Constants.SS_ANNOTATION_LABEL); | |
710 | 193 | if (ssAnnotationsInPDB != null && ssAnnotationsInPDB.length > 0) |
711 | { | |
712 | 147 | ssAnnotDescriptionInPDB = ssAnnotationsInPDB[0].description; |
713 | } | |
714 | ||
715 | // Match the PDB entry using file path in the pdb data model and get the | |
716 | // provider | |
717 | 193 | if (ssStructFilePathNameInPDB != null |
718 | && seq.getDatasetSequence() != null) | |
719 | { | |
720 | 191 | Vector<PDBEntry> pdbEntries = seq.getDatasetSequence() |
721 | .getAllPDBEntries(); | |
722 | 191 | if (pdbEntries != null) |
723 | { | |
724 | 191 | for (PDBEntry pdbEntry : pdbEntries) |
725 | { | |
726 | 234 | if (pdbEntry.getFile() != null && ssStructFilePathNameInPDB |
727 | .startsWith(pdbEntry.getFile())) | |
728 | { | |
729 | 175 | provider = pdbEntry.getProvider(); |
730 | 175 | break; |
731 | } | |
732 | } | |
733 | // Add provider value as property to the ss annotation | |
734 | 191 | if (provider != null) |
735 | { | |
736 | // TODO - JAL-2880 JAL-4441 this should be applied to all structure | |
737 | // derived annotations, not just secondary structure! | |
738 | 0 | AlignmentAnnotation[] ssAnnotList = ds |
739 | .getAnnotation(Constants.SS_ANNOTATION_LABEL); | |
740 | 0 | if (ssAnnotList != null) |
741 | { | |
742 | 0 | for (AlignmentAnnotation ssAnnot : ssAnnotList) |
743 | { | |
744 | // Match the annotation description with the annotation in pdb | |
745 | // data object | |
746 | 0 | if (ssAnnot |
747 | .getProperty(Constants.SS_PROVIDER_PROPERTY) == null | |
748 | && ssAnnot.description | |
749 | .equals(ssAnnotDescriptionInPDB)) | |
750 | { | |
751 | 0 | ssAnnot.setProperty(Constants.SS_PROVIDER_PROPERTY, |
752 | provider); | |
753 | } | |
754 | } | |
755 | } | |
756 | } | |
757 | } | |
758 | } | |
759 | ||
760 | 193 | if (forStructureView) |
761 | { | |
762 | 136 | for (StructureMapping sm : seqToStrucMapping) |
763 | { | |
764 | 136 | addStructureMapping(sm); // not addAll! |
765 | } | |
766 | } | |
767 | 193 | if (progress != null) |
768 | { | |
769 | 0 | progress.setProgressBar(null, progressSessionId); |
770 | } | |
771 | } | |
772 | 149 | return pdb; |
773 | } | |
774 | ||
775 | /** | |
776 | * check if we need to extract secondary structure from given pdbFile and | |
777 | * transfer to sequences | |
778 | * | |
779 | * @param pdbFile | |
780 | * @param sequenceArray | |
781 | * @return | |
782 | */ | |
783 | 134 | private boolean isStructureFileProcessed(String pdbFile, |
784 | SequenceI[] sequenceArray) | |
785 | { | |
786 | 134 | boolean processed = false; |
787 | 134 | if (isPDBFileRegistered(pdbFile)) |
788 | { | |
789 | 83 | for (SequenceI sq : sequenceArray) |
790 | { | |
791 | 123 | SequenceI ds = sq; |
792 | 246 | while (ds.getDatasetSequence() != null) |
793 | { | |
794 | 123 | ds = ds.getDatasetSequence(); |
795 | } | |
796 | 123 | ; |
797 | 123 | if (ds.getAnnotation() != null) |
798 | { | |
799 | 101 | for (AlignmentAnnotation ala : ds.getAnnotation()) |
800 | { | |
801 | // false if any annotation present from this structure | |
802 | // JBPNote this fails for jmol/chimera view because the *file* is | |
803 | // passed, not the structure data ID - | |
804 | 250 | if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile))) |
805 | { | |
806 | 0 | processed = true; |
807 | } | |
808 | } | |
809 | } | |
810 | } | |
811 | } | |
812 | 134 | return processed; |
813 | } | |
814 | ||
815 | 143 | public void addStructureMapping(StructureMapping sm) |
816 | { | |
817 | 143 | if (!mappings.contains(sm)) |
818 | { | |
819 | 78 | mappings.add(sm); |
820 | } | |
821 | } | |
822 | ||
823 | /** | |
824 | * retrieve a mapping for seq from SIFTs using associated DBRefEntry for | |
825 | * uniprot or PDB | |
826 | * | |
827 | * @param seq | |
828 | * @param pdbFile | |
829 | * @param targetChainId | |
830 | * @param pdb | |
831 | * @param maxChain | |
832 | * @param sqmpping | |
833 | * @param maxAlignseq | |
834 | * @param siftsClient | |
835 | * client for retrieval of SIFTS mappings for this structure | |
836 | * @return | |
837 | * @throws SiftsException | |
838 | */ | |
839 | 0 | private StructureMapping getStructureMapping(SequenceI seq, |
840 | String pdbFile, String targetChainId, StructureFile pdb, | |
841 | PDBChain maxChain, jalview.datamodel.Mapping sqmpping, | |
842 | AlignSeq maxAlignseq, SiftsClient siftsClient) | |
843 | throws SiftsException | |
844 | { | |
845 | 0 | StructureMapping curChainMapping = siftsClient |
846 | .getSiftsStructureMapping(seq, pdbFile, targetChainId); | |
847 | 0 | try |
848 | { | |
849 | 0 | PDBChain chain = pdb.findChain(targetChainId); |
850 | 0 | if (chain != null) |
851 | { | |
852 | 0 | chain.transferResidueAnnotation(curChainMapping, null); |
853 | } | |
854 | } catch (Exception e) | |
855 | { | |
856 | 0 | e.printStackTrace(); |
857 | } | |
858 | 0 | return curChainMapping; |
859 | } | |
860 | ||
861 | 193 | private StructureMapping getNWMappings(SequenceI seq, String pdbFile, |
862 | String maxChainId, PDBChain maxChain, StructureFile pdb, | |
863 | AlignSeq maxAlignseq) | |
864 | { | |
865 | 193 | final StringBuilder mappingDetails = new StringBuilder(128); |
866 | 193 | mappingDetails.append(NEWLINE) |
867 | .append("Sequence \u27f7 Structure mapping details"); | |
868 | 193 | mappingDetails.append(NEWLINE); |
869 | 193 | mappingDetails |
870 | .append("Method: inferred with Needleman & Wunsch alignment"); | |
871 | 193 | mappingDetails.append(NEWLINE).append("PDB Sequence is :") |
872 | .append(NEWLINE).append("Sequence = ") | |
873 | .append(maxChain.sequence.getSequenceAsString()); | |
874 | 193 | mappingDetails.append(NEWLINE).append("No of residues = ") |
875 | .append(maxChain.residues.size()).append(NEWLINE) | |
876 | .append(NEWLINE); | |
877 | 193 | PrintStream ps = new PrintStream(System.out) |
878 | { | |
879 | 193 | @Override |
880 | public void print(String x) | |
881 | { | |
882 | 193 | mappingDetails.append(x); |
883 | } | |
884 | ||
885 | 0 | @Override |
886 | public void println() | |
887 | { | |
888 | 0 | mappingDetails.append(NEWLINE); |
889 | } | |
890 | }; | |
891 | ||
892 | 193 | maxAlignseq.printAlignment(ps); |
893 | ||
894 | 193 | mappingDetails.append(NEWLINE).append("PDB start/end "); |
895 | 193 | mappingDetails.append(String.valueOf(maxAlignseq.seq2start)) |
896 | .append(" "); | |
897 | 193 | mappingDetails.append(String.valueOf(maxAlignseq.seq2end)); |
898 | 193 | mappingDetails.append(NEWLINE).append("SEQ start/end "); |
899 | 193 | mappingDetails |
900 | .append(String | |
901 | .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1))) | |
902 | .append(" "); | |
903 | 193 | mappingDetails.append( |
904 | String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1))); | |
905 | 193 | mappingDetails.append(NEWLINE); |
906 | 193 | maxChain.makeExactMapping(maxAlignseq, seq); |
907 | 193 | jalview.datamodel.Mapping sqmpping = maxAlignseq |
908 | .getMappingFromS1(false); | |
909 | 193 | maxChain.transferRESNUMFeatures(seq, null, |
910 | pdb.getId().toLowerCase(Locale.ROOT)); | |
911 | ||
912 | 193 | HashMap<Integer, int[]> mapping = new HashMap<>(); |
913 | 193 | int resNum = -10000; |
914 | 193 | int index = 0; |
915 | 193 | char insCode = ' '; |
916 | ||
917 | 193 | do |
918 | { | |
919 | 21210 | Atom tmp = maxChain.atoms.elementAt(index); |
920 | 21210 | if ((resNum != tmp.resNumber || insCode != tmp.insCode) |
921 | && tmp.alignmentMapping != -1) | |
922 | { | |
923 | 21161 | resNum = tmp.resNumber; |
924 | 21161 | insCode = tmp.insCode; |
925 | 21161 | if (tmp.alignmentMapping >= -1) |
926 | { | |
927 | 21161 | mapping.put(tmp.alignmentMapping + 1, |
928 | new int[] | |
929 | { tmp.resNumber, tmp.atomIndex }); | |
930 | } | |
931 | } | |
932 | ||
933 | 21210 | index++; |
934 | 21210 | } while (index < maxChain.atoms.size()); |
935 | ||
936 | 193 | StructureMapping nwMapping = new StructureMapping(seq, pdbFile, |
937 | pdb.getId(), maxChainId, mapping, mappingDetails.toString()); | |
938 | 193 | maxChain.transferResidueAnnotation(nwMapping, sqmpping); |
939 | 193 | return nwMapping; |
940 | } | |
941 | ||
942 | 529 | public void removeStructureViewerListener(Object svl, String[] pdbfiles) |
943 | { | |
944 | 529 | listeners.removeElement(svl); |
945 | 529 | if (svl instanceof SequenceListener) |
946 | { | |
947 | 1324 | for (int i = 0; i < listeners.size(); i++) |
948 | { | |
949 | 818 | if (listeners.elementAt(i) instanceof StructureListener) |
950 | { | |
951 | 42 | ((StructureListener) listeners.elementAt(i)) |
952 | .releaseReferences(svl); | |
953 | } | |
954 | } | |
955 | } | |
956 | ||
957 | 529 | if (pdbfiles == null) |
958 | { | |
959 | 506 | return; |
960 | } | |
961 | ||
962 | /* | |
963 | * Remove mappings to the closed listener's PDB files, but first check if | |
964 | * another listener is still interested | |
965 | */ | |
966 | 23 | List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles)); |
967 | ||
968 | 23 | StructureListener sl; |
969 | 76 | for (int i = 0; i < listeners.size(); i++) |
970 | { | |
971 | 53 | if (listeners.elementAt(i) instanceof StructureListener) |
972 | { | |
973 | 6 | sl = (StructureListener) listeners.elementAt(i); |
974 | 6 | for (String pdbfile : sl.getStructureFiles()) |
975 | { | |
976 | 7 | pdbs.remove(pdbfile); |
977 | } | |
978 | } | |
979 | } | |
980 | ||
981 | /* | |
982 | * Rebuild the mappings set, retaining only those which are for 'other' PDB | |
983 | * files | |
984 | */ | |
985 | 23 | if (pdbs.size() > 0) |
986 | { | |
987 | 22 | List<StructureMapping> tmp = new ArrayList<>(); |
988 | 22 | for (StructureMapping sm : mappings) |
989 | { | |
990 | 37 | if (!pdbs.contains(sm.pdbfile)) |
991 | { | |
992 | 7 | tmp.add(sm); |
993 | } | |
994 | } | |
995 | ||
996 | 22 | mappings = tmp; |
997 | } | |
998 | } | |
999 | ||
1000 | /** | |
1001 | * hack to highlight a range of positions at once on any structure views | |
1002 | * | |
1003 | * @param sequenceRef | |
1004 | * @param is | |
1005 | * - series of int start-end ranges as positions on sequenceRef | |
1006 | * @param i | |
1007 | * @param object | |
1008 | */ | |
1009 | 0 | public void highlightPositionsOn(SequenceI sequenceRef, int[][] is, |
1010 | Object source) | |
1011 | { | |
1012 | 0 | boolean hasSequenceListeners = handlingVamsasMo |
1013 | || !seqmappings.isEmpty(); | |
1014 | 0 | SearchResultsI results = null; |
1015 | 0 | ArrayList<Integer> listOfPositions = new ArrayList<Integer>(); |
1016 | 0 | for (int[] s_e : is) |
1017 | { | |
1018 | 0 | for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++)) |
1019 | 0 | ; |
1020 | } | |
1021 | 0 | int seqpos[] = new int[listOfPositions.size()]; |
1022 | 0 | int i = 0; |
1023 | 0 | for (Integer p : listOfPositions) |
1024 | { | |
1025 | 0 | seqpos[i++] = p; |
1026 | } | |
1027 | ||
1028 | 0 | for (i = 0; i < listeners.size(); i++) |
1029 | { | |
1030 | 0 | Object listener = listeners.elementAt(i); |
1031 | 0 | if (listener == source) |
1032 | { | |
1033 | // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) | |
1034 | // Temporary fudge with SequenceListener.getVamsasSource() | |
1035 | 0 | continue; |
1036 | } | |
1037 | 0 | if (listener instanceof StructureListener) |
1038 | { | |
1039 | 0 | highlightStructure((StructureListener) listener, sequenceRef, |
1040 | seqpos); | |
1041 | } | |
1042 | ||
1043 | } | |
1044 | } | |
1045 | ||
1046 | /** | |
1047 | * Propagate mouseover of a single position in a structure | |
1048 | * | |
1049 | * @param pdbResNum | |
1050 | * @param chain | |
1051 | * @param pdbfile | |
1052 | * @return | |
1053 | */ | |
1054 | 1 | public String mouseOverStructure(int pdbResNum, String chain, |
1055 | String pdbfile) | |
1056 | { | |
1057 | 1 | AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0); |
1058 | 1 | List<AtomSpec> atoms = Collections.singletonList(atomSpec); |
1059 | 1 | return mouseOverStructure(atoms); |
1060 | } | |
1061 | ||
1062 | /** | |
1063 | * Propagate mouseover or selection of multiple positions in a structure | |
1064 | * | |
1065 | * @param atoms | |
1066 | */ | |
1067 | 1 | public String mouseOverStructure(List<AtomSpec> atoms) |
1068 | { | |
1069 | 1 | if (listeners == null) |
1070 | { | |
1071 | // old or prematurely sent event | |
1072 | 0 | return null; |
1073 | } | |
1074 | 1 | boolean hasSequenceListener = false; |
1075 | 3 | for (int i = 0; i < listeners.size(); i++) |
1076 | { | |
1077 | 2 | if (listeners.elementAt(i) instanceof SequenceListener) |
1078 | { | |
1079 | 1 | hasSequenceListener = true; |
1080 | } | |
1081 | } | |
1082 | 1 | if (!hasSequenceListener) |
1083 | { | |
1084 | 0 | return null; |
1085 | } | |
1086 | ||
1087 | 1 | SearchResultsI results = findAlignmentPositionsForStructurePositions( |
1088 | atoms); | |
1089 | 1 | String result = null; |
1090 | 1 | for (Object li : listeners) |
1091 | { | |
1092 | 2 | if (li instanceof SequenceListener) |
1093 | { | |
1094 | 1 | String s = ((SequenceListener) li).highlightSequence(results); |
1095 | 1 | if (s != null) |
1096 | { | |
1097 | 0 | result = s; |
1098 | } | |
1099 | } | |
1100 | } | |
1101 | 1 | return result; |
1102 | } | |
1103 | ||
1104 | /** | |
1105 | * Constructs a SearchResults object holding regions (if any) in the Jalview | |
1106 | * alignment which have a mapping to the structure viewer positions in the | |
1107 | * supplied list | |
1108 | * | |
1109 | * @param atoms | |
1110 | * @return | |
1111 | */ | |
1112 | 1 | public SearchResultsI findAlignmentPositionsForStructurePositions( |
1113 | List<AtomSpec> atoms) | |
1114 | { | |
1115 | 1 | SearchResultsI results = new SearchResults(); |
1116 | 1 | for (AtomSpec atom : atoms) |
1117 | { | |
1118 | 1 | SequenceI lastseq = null; |
1119 | 1 | int lastipos = -1; |
1120 | 1 | for (StructureMapping sm : mappings) |
1121 | { | |
1122 | 2 | if (sm.pdbfile.equals(atom.getPdbFile()) |
1123 | && sm.pdbchain.equals(atom.getChain())) | |
1124 | { | |
1125 | 1 | int indexpos = sm.getSeqPos(atom.getPdbResNum()); |
1126 | 1 | if (lastipos != indexpos || lastseq != sm.sequence) |
1127 | { | |
1128 | 1 | results.appendResult(sm.sequence, indexpos, indexpos); |
1129 | 1 | lastipos = indexpos; |
1130 | 1 | lastseq = sm.sequence; |
1131 | // construct highlighted sequence list | |
1132 | 1 | for (AlignedCodonFrame acf : seqmappings) |
1133 | { | |
1134 | 0 | acf.markMappedRegion(sm.sequence, indexpos, results); |
1135 | } | |
1136 | } | |
1137 | } | |
1138 | } | |
1139 | } | |
1140 | 1 | return results; |
1141 | } | |
1142 | ||
1143 | /** | |
1144 | * highlight regions associated with a position (indexpos) in seq | |
1145 | * | |
1146 | * @param seq | |
1147 | * the sequence that the mouse over occurred on | |
1148 | * @param indexpos | |
1149 | * the absolute position being mouseovered in seq (0 to seq.length()) | |
1150 | * @param seqPos | |
1151 | * the sequence position (if -1, seq.findPosition is called to | |
1152 | * resolve the residue number) | |
1153 | */ | |
1154 | 0 | public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos, |
1155 | VamsasSource source) | |
1156 | { | |
1157 | 0 | boolean hasSequenceListeners = handlingVamsasMo |
1158 | || !seqmappings.isEmpty(); | |
1159 | 0 | SearchResultsI results = null; |
1160 | 0 | if (seqPos == -1) |
1161 | { | |
1162 | 0 | seqPos = seq.findPosition(indexpos); |
1163 | } | |
1164 | 0 | for (int i = 0; i < listeners.size(); i++) |
1165 | { | |
1166 | 0 | Object listener = listeners.elementAt(i); |
1167 | 0 | if (listener == source) |
1168 | { | |
1169 | // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) | |
1170 | // Temporary fudge with SequenceListener.getVamsasSource() | |
1171 | 0 | continue; |
1172 | } | |
1173 | 0 | if (listener instanceof StructureListener) |
1174 | { | |
1175 | 0 | highlightStructure((StructureListener) listener, seq, seqPos); |
1176 | } | |
1177 | else | |
1178 | { | |
1179 | 0 | if (listener instanceof SequenceListener) |
1180 | { | |
1181 | 0 | final SequenceListener seqListener = (SequenceListener) listener; |
1182 | 0 | if (hasSequenceListeners |
1183 | && seqListener.getVamsasSource() != source) | |
1184 | { | |
1185 | 0 | if (relaySeqMappings) |
1186 | { | |
1187 | 0 | if (results == null) |
1188 | { | |
1189 | 0 | results = MappingUtils.buildSearchResults(seq, seqPos, |
1190 | seqmappings); | |
1191 | } | |
1192 | 0 | if (handlingVamsasMo) |
1193 | { | |
1194 | 0 | results.addResult(seq, seqPos, seqPos); |
1195 | ||
1196 | } | |
1197 | 0 | if (!results.isEmpty()) |
1198 | { | |
1199 | 0 | seqListener.highlightSequence(results); |
1200 | } | |
1201 | } | |
1202 | } | |
1203 | } | |
1204 | 0 | else if (listener instanceof VamsasListener && !handlingVamsasMo) |
1205 | { | |
1206 | 0 | ((VamsasListener) listener).mouseOverSequence(seq, indexpos, |
1207 | source); | |
1208 | } | |
1209 | 0 | else if (listener instanceof SecondaryStructureListener) |
1210 | { | |
1211 | 0 | ((SecondaryStructureListener) listener).mouseOverSequence(seq, |
1212 | indexpos, seqPos); | |
1213 | } | |
1214 | } | |
1215 | } | |
1216 | } | |
1217 | ||
1218 | /** | |
1219 | * Send suitable messages to a StructureListener to highlight atoms | |
1220 | * corresponding to the given sequence position(s) | |
1221 | * | |
1222 | * @param sl | |
1223 | * @param seq | |
1224 | * @param positions | |
1225 | */ | |
1226 | 0 | public void highlightStructure(StructureListener sl, SequenceI seq, |
1227 | int... positions) | |
1228 | { | |
1229 | 0 | if (!sl.isListeningFor(seq)) |
1230 | { | |
1231 | 0 | return; |
1232 | } | |
1233 | 0 | int atomNo; |
1234 | 0 | List<AtomSpec> atoms = new ArrayList<>(); |
1235 | 0 | for (StructureMapping sm : mappings) |
1236 | { | |
1237 | 0 | if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() |
1238 | || (sm.sequence.getDatasetSequence() != null && sm.sequence | |
1239 | .getDatasetSequence() == seq.getDatasetSequence())) | |
1240 | { | |
1241 | 0 | for (int index : positions) |
1242 | { | |
1243 | 0 | atomNo = sm.getAtomNum(index); |
1244 | ||
1245 | 0 | if (atomNo > 0) |
1246 | { | |
1247 | 0 | atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, |
1248 | sm.getPDBResNum(index), atomNo)); | |
1249 | } | |
1250 | } | |
1251 | } | |
1252 | } | |
1253 | 0 | sl.highlightAtoms(atoms); |
1254 | } | |
1255 | ||
1256 | 0 | public void highlightStructureRegionsFor(StructureListener sl, |
1257 | SequenceI[] seqs, int... columns) | |
1258 | { | |
1259 | 0 | List<SequenceI> to_highlight = new ArrayList<SequenceI>(); |
1260 | 0 | for (SequenceI seq : seqs) |
1261 | { | |
1262 | 0 | if (sl.isListeningFor(seq)) |
1263 | { | |
1264 | 0 | to_highlight.add(seq); |
1265 | } | |
1266 | } | |
1267 | 0 | if (to_highlight.size() == 0) |
1268 | { | |
1269 | 0 | return; |
1270 | } | |
1271 | 0 | List<AtomSpec> atoms = new ArrayList<>(); |
1272 | 0 | for (SequenceI seq : to_highlight) |
1273 | { | |
1274 | 0 | int atomNo; |
1275 | 0 | for (StructureMapping sm : mappings) |
1276 | { | |
1277 | 0 | if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() |
1278 | || (sm.sequence.getDatasetSequence() != null && sm.sequence | |
1279 | .getDatasetSequence() == seq.getDatasetSequence())) | |
1280 | { | |
1281 | ||
1282 | 0 | for (int i = 0; i < columns.length; i += 2) |
1283 | { | |
1284 | 0 | ContiguousI positions = seq.findPositions(columns[i] + 1, |
1285 | columns[i + 1] + 1); | |
1286 | 0 | if (positions == null) |
1287 | { | |
1288 | 0 | continue; |
1289 | } | |
1290 | 0 | for (int index = positions.getBegin(); index <= positions |
1291 | .getEnd(); index++) | |
1292 | { | |
1293 | ||
1294 | 0 | atomNo = sm.getAtomNum(index); |
1295 | ||
1296 | 0 | if (atomNo > 0) |
1297 | { | |
1298 | 0 | atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, |
1299 | sm.getPDBResNum(index), atomNo)); | |
1300 | } | |
1301 | } | |
1302 | } | |
1303 | } | |
1304 | } | |
1305 | 0 | if (atoms.size() > 0) |
1306 | { | |
1307 | 0 | sl.highlightAtoms(atoms); |
1308 | } | |
1309 | } | |
1310 | } | |
1311 | ||
1312 | /** | |
1313 | * true if a mouse over event from an external (ie Vamsas) source is being | |
1314 | * handled | |
1315 | */ | |
1316 | boolean handlingVamsasMo = false; | |
1317 | ||
1318 | long lastmsg = 0; | |
1319 | ||
1320 | /** | |
1321 | * as mouseOverSequence but only route event to SequenceListeners | |
1322 | * | |
1323 | * @param sequenceI | |
1324 | * @param position | |
1325 | * in an alignment sequence | |
1326 | */ | |
1327 | 0 | public void mouseOverVamsasSequence(SequenceI sequenceI, int position, |
1328 | VamsasSource source) | |
1329 | { | |
1330 | 0 | handlingVamsasMo = true; |
1331 | 0 | long msg = sequenceI.hashCode() * (1 + position); |
1332 | 0 | if (lastmsg != msg) |
1333 | { | |
1334 | 0 | lastmsg = msg; |
1335 | 0 | mouseOverSequence(sequenceI, position, -1, source); |
1336 | } | |
1337 | 0 | handlingVamsasMo = false; |
1338 | } | |
1339 | ||
1340 | 0 | public Annotation[] colourSequenceFromStructure(SequenceI seq, |
1341 | String pdbid) | |
1342 | { | |
1343 | 0 | return null; |
1344 | // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3, | |
1345 | // UNTIL THE COLOUR BY ANNOTATION IS REWORKED | |
1346 | /* | |
1347 | * Annotation [] annotations = new Annotation[seq.getLength()]; | |
1348 | * | |
1349 | * StructureListener sl; int atomNo = 0; for (int i = 0; i < | |
1350 | * listeners.size(); i++) { if (listeners.elementAt(i) instanceof | |
1351 | * StructureListener) { sl = (StructureListener) listeners.elementAt(i); | |
1352 | * | |
1353 | * for (int j = 0; j < mappings.length; j++) { | |
1354 | * | |
1355 | * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid) | |
1356 | * && mappings[j].pdbfile.equals(sl.getPdbFile())) { | |
1357 | * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +" | |
1358 | * "+mappings[j].pdbfile); | |
1359 | * | |
1360 | * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) { | |
1361 | * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue; | |
1362 | * | |
1363 | * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col = | |
1364 | * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo, | |
1365 | * mappings[j].getPDBResNum(index), mappings[j].pdbchain, | |
1366 | * mappings[j].pdbfile); } | |
1367 | * | |
1368 | * annotations[index] = new Annotation("X",null,' ',0,col); } return | |
1369 | * annotations; } } } } | |
1370 | * | |
1371 | * return annotations; | |
1372 | */ | |
1373 | } | |
1374 | ||
1375 | 0 | public void structureSelectionChanged() |
1376 | { | |
1377 | } | |
1378 | ||
1379 | 0 | public void sequenceSelectionChanged() |
1380 | { | |
1381 | } | |
1382 | ||
1383 | 3673 | public void sequenceColoursChanged(Object source) |
1384 | { | |
1385 | 3673 | StructureListener sl; |
1386 | 17184 | for (int i = 0; i < listeners.size(); i++) |
1387 | { | |
1388 | 13514 | if (listeners.elementAt(i) instanceof StructureListener) |
1389 | { | |
1390 | 1152 | sl = (StructureListener) listeners.elementAt(i); |
1391 | 1152 | sl.updateColours(source); |
1392 | } | |
1393 | } | |
1394 | } | |
1395 | ||
1396 | 693 | public StructureMapping[] getMapping(String pdbfile) |
1397 | { | |
1398 | 693 | List<StructureMapping> tmp = new ArrayList<>(); |
1399 | 693 | for (StructureMapping sm : mappings) |
1400 | { | |
1401 | 710 | if (sm.pdbfile.equals(pdbfile)) |
1402 | { | |
1403 | 497 | tmp.add(sm); |
1404 | } | |
1405 | } | |
1406 | 693 | return tmp.toArray(new StructureMapping[tmp.size()]); |
1407 | } | |
1408 | ||
1409 | /** | |
1410 | * Returns a readable description of all mappings for the given pdbfile to any | |
1411 | * of the given sequences | |
1412 | * | |
1413 | * @param pdbfile | |
1414 | * @param seqs | |
1415 | * @return | |
1416 | */ | |
1417 | 0 | public String printMappings(String pdbfile, List<SequenceI> seqs) |
1418 | { | |
1419 | 0 | if (pdbfile == null || seqs == null || seqs.isEmpty()) |
1420 | { | |
1421 | 0 | return ""; |
1422 | } | |
1423 | ||
1424 | 0 | StringBuilder sb = new StringBuilder(64); |
1425 | 0 | for (StructureMapping sm : mappings) |
1426 | { | |
1427 | 0 | if (Platform.pathEquals(sm.pdbfile, pdbfile) |
1428 | && seqs.contains(sm.sequence)) | |
1429 | { | |
1430 | 0 | sb.append(sm.mappingDetails); |
1431 | 0 | sb.append(NEWLINE); |
1432 | // separator makes it easier to read multiple mappings | |
1433 | 0 | sb.append("====================="); |
1434 | 0 | sb.append(NEWLINE); |
1435 | } | |
1436 | } | |
1437 | 0 | sb.append(NEWLINE); |
1438 | ||
1439 | 0 | return sb.toString(); |
1440 | } | |
1441 | ||
1442 | /** | |
1443 | * Remove the given mapping | |
1444 | * | |
1445 | * @param acf | |
1446 | */ | |
1447 | 27 | public void deregisterMapping(AlignedCodonFrame acf) |
1448 | { | |
1449 | 27 | if (acf != null) |
1450 | { | |
1451 | 27 | boolean removed = seqmappings.remove(acf); |
1452 | 27 | if (removed && seqmappings.isEmpty()) |
1453 | { // debug | |
1454 | 2 | jalview.bin.Console.outPrintln("All mappings removed"); |
1455 | } | |
1456 | } | |
1457 | } | |
1458 | ||
1459 | /** | |
1460 | * Add each of the given codonFrames to the stored set, if not aready present. | |
1461 | * | |
1462 | * @param mappings | |
1463 | */ | |
1464 | 554 | public void registerMappings(List<AlignedCodonFrame> mappings) |
1465 | { | |
1466 | 554 | if (mappings != null) |
1467 | { | |
1468 | 554 | for (AlignedCodonFrame acf : mappings) |
1469 | { | |
1470 | 152 | registerMapping(acf); |
1471 | } | |
1472 | } | |
1473 | } | |
1474 | ||
1475 | /** | |
1476 | * Add the given mapping to the stored set, unless already stored. | |
1477 | */ | |
1478 | 161 | public void registerMapping(AlignedCodonFrame acf) |
1479 | { | |
1480 | 161 | if (acf != null) |
1481 | { | |
1482 | 161 | if (!seqmappings.contains(acf)) |
1483 | { | |
1484 | 38 | seqmappings.add(acf); |
1485 | } | |
1486 | } | |
1487 | } | |
1488 | ||
1489 | /** | |
1490 | * Resets this object to its initial state by removing all registered | |
1491 | * listeners, codon mappings, PDB file mappings | |
1492 | */ | |
1493 | 237 | public void resetAll() |
1494 | { | |
1495 | 237 | if (mappings != null) |
1496 | { | |
1497 | 237 | mappings.clear(); |
1498 | } | |
1499 | 237 | if (seqmappings != null) |
1500 | { | |
1501 | 237 | seqmappings.clear(); |
1502 | } | |
1503 | 237 | if (sel_listeners != null) |
1504 | { | |
1505 | 237 | sel_listeners.clear(); |
1506 | } | |
1507 | 237 | if (listeners != null) |
1508 | { | |
1509 | 237 | listeners.clear(); |
1510 | } | |
1511 | 237 | if (commandListeners != null) |
1512 | { | |
1513 | 237 | commandListeners.clear(); |
1514 | } | |
1515 | 237 | if (view_listeners != null) |
1516 | { | |
1517 | 237 | view_listeners.clear(); |
1518 | } | |
1519 | 237 | if (pdbFileNameId != null) |
1520 | { | |
1521 | 237 | pdbFileNameId.clear(); |
1522 | } | |
1523 | 237 | if (pdbIdFileName != null) |
1524 | { | |
1525 | 237 | pdbIdFileName.clear(); |
1526 | } | |
1527 | } | |
1528 | ||
1529 | 478 | public void addSelectionListener(SelectionListener selecter) |
1530 | { | |
1531 | 478 | if (!sel_listeners.contains(selecter)) |
1532 | { | |
1533 | 478 | sel_listeners.add(selecter); |
1534 | } | |
1535 | } | |
1536 | ||
1537 | 506 | public void removeSelectionListener(SelectionListener toremove) |
1538 | { | |
1539 | 506 | if (sel_listeners.contains(toremove)) |
1540 | { | |
1541 | 253 | sel_listeners.remove(toremove); |
1542 | } | |
1543 | } | |
1544 | ||
1545 | 23 | public synchronized void sendSelection( |
1546 | jalview.datamodel.SequenceGroup selection, | |
1547 | jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden, | |
1548 | SelectionSource source) | |
1549 | { | |
1550 | 23 | for (SelectionListener slis : sel_listeners) |
1551 | { | |
1552 | 100 | if (slis != source) |
1553 | { | |
1554 | 100 | slis.selection(selection, colsel, hidden, source); |
1555 | } | |
1556 | } | |
1557 | } | |
1558 | ||
1559 | Vector<AlignmentViewPanelListener> view_listeners = new Vector<>(); | |
1560 | ||
1561 | 0 | public synchronized void sendViewPosition( |
1562 | jalview.api.AlignmentViewPanel source, int startRes, int endRes, | |
1563 | int startSeq, int endSeq) | |
1564 | { | |
1565 | ||
1566 | 0 | if (view_listeners != null && view_listeners.size() > 0) |
1567 | { | |
1568 | 0 | Enumeration<AlignmentViewPanelListener> listeners = view_listeners |
1569 | .elements(); | |
1570 | 0 | while (listeners.hasMoreElements()) |
1571 | { | |
1572 | 0 | AlignmentViewPanelListener slis = listeners.nextElement(); |
1573 | 0 | if (slis != source) |
1574 | { | |
1575 | 0 | slis.viewPosition(startRes, endRes, startSeq, endSeq, source); |
1576 | } | |
1577 | 0 | ; |
1578 | } | |
1579 | } | |
1580 | } | |
1581 | ||
1582 | /** | |
1583 | * release all references associated with this manager provider | |
1584 | * | |
1585 | * @param jalviewLite | |
1586 | */ | |
1587 | 0 | public static void release(StructureSelectionManagerProvider jalviewLite) |
1588 | { | |
1589 | // synchronized (instances) | |
1590 | { | |
1591 | 0 | if (instances == null) |
1592 | { | |
1593 | 0 | return; |
1594 | } | |
1595 | 0 | StructureSelectionManager mnger = (instances.get(jalviewLite)); |
1596 | 0 | if (mnger != null) |
1597 | { | |
1598 | 0 | instances.remove(jalviewLite); |
1599 | 0 | try |
1600 | { | |
1601 | /* bsoares 2019-03-20 finalize deprecated, no apparent external | |
1602 | * resources to close | |
1603 | */ | |
1604 | // mnger.finalize(); | |
1605 | } catch (Throwable x) | |
1606 | { | |
1607 | } | |
1608 | } | |
1609 | } | |
1610 | } | |
1611 | ||
1612 | 359 | public void registerPDBEntry(PDBEntry pdbentry) |
1613 | { | |
1614 | 359 | if (pdbentry.getFile() != null |
1615 | && pdbentry.getFile().trim().length() > 0) | |
1616 | { | |
1617 | 139 | registerPDBFile(pdbentry.getId(), pdbentry.getFile()); |
1618 | } | |
1619 | } | |
1620 | ||
1621 | 6 | public void addCommandListener(CommandListener cl) |
1622 | { | |
1623 | 6 | if (!commandListeners.contains(cl)) |
1624 | { | |
1625 | 6 | commandListeners.add(cl); |
1626 | } | |
1627 | } | |
1628 | ||
1629 | 0 | public boolean hasCommandListener(CommandListener cl) |
1630 | { | |
1631 | 0 | return this.commandListeners.contains(cl); |
1632 | } | |
1633 | ||
1634 | 253 | public boolean removeCommandListener(CommandListener l) |
1635 | { | |
1636 | 253 | return commandListeners.remove(l); |
1637 | } | |
1638 | ||
1639 | /** | |
1640 | * Forward a command to any command listeners (except for the command's | |
1641 | * source). | |
1642 | * | |
1643 | * @param command | |
1644 | * the command to be broadcast (in its form after being performed) | |
1645 | * @param undo | |
1646 | * if true, the command was being 'undone' | |
1647 | * @param source | |
1648 | */ | |
1649 | 1 | public void commandPerformed(CommandI command, boolean undo, |
1650 | VamsasSource source) | |
1651 | { | |
1652 | 1 | for (CommandListener listener : commandListeners) |
1653 | { | |
1654 | 0 | listener.mirrorCommand(command, undo, this, source); |
1655 | } | |
1656 | } | |
1657 | ||
1658 | /** | |
1659 | * Returns a new CommandI representing the given command as mapped to the | |
1660 | * given sequences. If no mapping could be made, or the command is not of a | |
1661 | * mappable kind, returns null. | |
1662 | * | |
1663 | * @param command | |
1664 | * @param undo | |
1665 | * @param mapTo | |
1666 | * @param gapChar | |
1667 | * @return | |
1668 | */ | |
1669 | 0 | public CommandI mapCommand(CommandI command, boolean undo, |
1670 | final AlignmentI mapTo, char gapChar) | |
1671 | { | |
1672 | 0 | if (command instanceof EditCommand) |
1673 | { | |
1674 | 0 | return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo, |
1675 | gapChar, seqmappings); | |
1676 | } | |
1677 | 0 | else if (command instanceof OrderCommand) |
1678 | { | |
1679 | 0 | return MappingUtils.mapOrderCommand((OrderCommand) command, undo, |
1680 | mapTo, seqmappings); | |
1681 | } | |
1682 | 0 | return null; |
1683 | } | |
1684 | ||
1685 | 15 | public List<AlignedCodonFrame> getSequenceMappings() |
1686 | { | |
1687 | 15 | return seqmappings; |
1688 | } | |
1689 | ||
1690 | /** | |
1691 | * quick and dirty route to just highlight all structure positions for a range | |
1692 | * of columns | |
1693 | * | |
1694 | * @param sequencesArray | |
1695 | * @param is | |
1696 | * start-end columns on sequencesArray | |
1697 | * @param source | |
1698 | * origin parent AlignmentPanel | |
1699 | */ | |
1700 | 8 | public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is, |
1701 | Object source) | |
1702 | { | |
1703 | 16 | for (int i = 0; i < listeners.size(); i++) |
1704 | { | |
1705 | 8 | Object listener = listeners.elementAt(i); |
1706 | 8 | if (listener == source) |
1707 | { | |
1708 | // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) | |
1709 | // Temporary fudge with SequenceListener.getVamsasSource() | |
1710 | 0 | continue; |
1711 | } | |
1712 | 8 | if (listener instanceof StructureListener) |
1713 | { | |
1714 | 0 | highlightStructureRegionsFor((StructureListener) listener, |
1715 | sequencesArray, is); | |
1716 | } | |
1717 | } | |
1718 | } | |
1719 | ||
1720 | 0 | public Map<String, String> getPdbFileNameIdMap() |
1721 | { | |
1722 | 0 | return pdbFileNameId; |
1723 | } | |
1724 | ||
1725 | 0 | public Map<String, String> getPdbIdFileNameMap() |
1726 | { | |
1727 | 0 | return pdbIdFileName; |
1728 | } | |
1729 | ||
1730 | 127 | public static void doConfigureStructurePrefs( |
1731 | StructureSelectionManager ssm) | |
1732 | { | |
1733 | 127 | doConfigureStructurePrefs(ssm, |
1734 | Cache.getDefault(Preferences.ADD_SS_ANN, true), | |
1735 | Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true), | |
1736 | Cache.getDefault(Preferences.STRUCT_FROM_PDB, true), | |
1737 | Cache.getDefault(Preferences.USE_RNAVIEW, false)); | |
1738 | } | |
1739 | ||
1740 | 127 | public static void doConfigureStructurePrefs( |
1741 | StructureSelectionManager ssm, boolean add_ss_ann, | |
1742 | boolean add_tempfact_ann, boolean struct_from_pdb, | |
1743 | boolean use_rnaview) | |
1744 | { | |
1745 | 127 | if (add_ss_ann) |
1746 | { | |
1747 | 127 | ssm.setAddTempFacAnnot(add_tempfact_ann); |
1748 | 127 | ssm.setProcessSecondaryStructure(struct_from_pdb); |
1749 | // JAL-3915 - RNAView is no longer an option so this has no effect | |
1750 | 127 | ssm.setSecStructServices(use_rnaview); |
1751 | } | |
1752 | else | |
1753 | { | |
1754 | 0 | ssm.setAddTempFacAnnot(false); |
1755 | 0 | ssm.setProcessSecondaryStructure(false); |
1756 | 0 | ssm.setSecStructServices(false); |
1757 | } | |
1758 | } | |
1759 | ||
1760 | } |