Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
33% of files have more coverage

Code metrics

202
387
54
1
1,444
995
187
0.48
7.17
54
3.46

Classes

Class Line # Actions
StructureSelectionManager 64 387 187
0.595645459.6%
 

Contributing tests

This file is covered by 130 tests. .

Source view

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