Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.gui

File StructureViewer.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
43% of files have more coverage

Code metrics

62
133
20
2
473
326
60
0.45
6.65
10
3

Classes

Class Line # Actions
StructureViewer 48 125 57
0.4158415841.6%
StructureViewer.ViewerType 69 8 3
0.923076992.3%
 

Contributing tests

This file is covered by 43 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.gui;
22   
23    import java.util.ArrayList;
24    import java.util.EnumSet;
25    import java.util.HashMap;
26    import java.util.LinkedHashMap;
27    import java.util.List;
28    import java.util.Locale;
29    import java.util.Map;
30    import java.util.Map.Entry;
31   
32    import jalview.api.structures.JalviewStructureDisplayI;
33    import jalview.bin.Cache;
34    import jalview.bin.Console;
35    import jalview.datamodel.PDBEntry;
36    import jalview.datamodel.SequenceI;
37    import jalview.datamodel.StructureViewerModel;
38    import jalview.structure.StructureSelectionManager;
39   
40    /**
41    * A proxy for handling structure viewers, that orchestrates adding selected
42    * structures, associated with sequences in Jalview, to an existing viewer, or
43    * opening a new one. Currently supports either Jmol or Chimera as the structure
44    * viewer.
45    *
46    * @author jprocter
47    */
 
48    public class StructureViewer
49    {
50    private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
51   
52    StructureSelectionManager ssm;
53   
54    /**
55    * decide if new structures are aligned to existing ones
56    */
57    private boolean superposeAdded = true;
58   
59    /**
60    * whether to open structures in their own thread or not
61    */
62    private boolean async = true;
63   
 
64  19 toggle public void setAsync(boolean b)
65    {
66  19 async = b;
67    }
68   
 
69    public enum ViewerType
70    {
71    JMOL, CHIMERA, CHIMERAX, PYMOL;
72   
 
73  42 toggle public static ViewerType getFromString(String viewerString)
74    {
75  42 ViewerType viewerType = null;
76  42 if (!"none".equals(viewerString))
77    {
78  33 for (ViewerType v : EnumSet.allOf(ViewerType.class))
79    {
80  33 String name = v.name().toLowerCase(Locale.ROOT).replaceAll(" ",
81    "");
82  33 if (viewerString.equals(name))
83    {
84  33 viewerType = v;
85  33 break;
86    }
87    }
88    }
89  42 return viewerType;
90    }
91   
92    };
93   
94    /**
95    * Constructor
96    *
97    * @param structureSelectionManager
98    */
 
99  39 toggle public StructureViewer(
100    StructureSelectionManager structureSelectionManager)
101    {
102  39 ssm = structureSelectionManager;
103    }
104   
105    /**
106    * Factory to create a proxy for modifying existing structure viewer
107    *
108    */
 
109  0 toggle public static StructureViewer reconfigure(
110    JalviewStructureDisplayI display)
111    {
112  0 StructureViewer sv = new StructureViewer(display.getBinding().getSsm());
113  0 sv.sview = display;
114  0 return sv;
115    }
116   
 
117  0 toggle @Override
118    public String toString()
119    {
120  0 if (sview != null)
121    {
122  0 return sview.toString();
123    }
124  0 return "New View";
125    }
126   
127    /**
128    *
129    * @return ViewerType for currently configured structure viewer
130    */
 
131  57 toggle public static ViewerType getViewerType()
132    {
133  57 String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
134    ViewerType.JMOL.name());
135  57 return ViewerType.valueOf(viewType);
136    }
137   
 
138  2 toggle public void setViewerType(ViewerType type)
139    {
140  2 Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name());
141    }
142   
143    /**
144    * View multiple PDB entries, each with associated sequences
145    *
146    * @param pdbs
147    * @param seqs
148    * @param ap
149    * @return
150    */
 
151  0 toggle public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
152    SequenceI[] seqs, AlignmentPanel ap)
153    {
154  0 return viewStructures(pdbs, seqs, ap, null);
155    }
156   
 
157  0 toggle public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
158    SequenceI[] seqs, AlignmentPanel ap, ViewerType viewerType)
159    {
160  0 JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap);
161  0 if (viewer != null)
162    {
163    /*
164    * user added structure to an existing viewer - all done
165    */
166  0 return viewer;
167    }
168   
169  0 if (viewerType == null)
170  0 viewerType = getViewerType();
171   
172  0 Map<PDBEntry, SequenceI[]> seqsForPdbs = getSequencesForPdbs(pdbs,
173    seqs);
174  0 PDBEntry[] pdbsForFile = seqsForPdbs.keySet()
175    .toArray(new PDBEntry[seqsForPdbs.size()]);
176  0 SequenceI[][] theSeqs = seqsForPdbs.values()
177    .toArray(new SequenceI[seqsForPdbs.size()][]);
178  0 if (sview != null)
179    {
180  0 sview.setAlignAddedStructures(superposeAdded);
181   
182  0 Runnable viewRunnable = new Runnable()
183    {
 
184  0 toggle @Override
185    public void run()
186    {
187   
188  0 for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++)
189    {
190  0 PDBEntry pdb = pdbsForFile[pdbep];
191  0 if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap,
192    pdb.getId()))
193    {
194  0 sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap,
195    pdb.getId());
196    }
197    }
198   
199  0 sview.updateTitleAndMenus();
200    }
201    };
202  0 if (async)
203    {
204  0 new Thread(viewRunnable).start();
205    }
206    else
207    {
208  0 viewRunnable.run();
209    }
210  0 return sview;
211    }
212   
213  0 if (viewerType.equals(ViewerType.JMOL))
214    {
215  0 sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs);
216    }
217  0 else if (viewerType.equals(ViewerType.CHIMERA))
218    {
219  0 sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
220    ap);
221    }
222  0 else if (viewerType.equals(ViewerType.CHIMERAX))
223    {
224  0 sview = new ChimeraXViewFrame(pdbsForFile, superposeAdded, theSeqs,
225    ap);
226    }
227  0 else if (viewerType.equals(ViewerType.PYMOL))
228    {
229  0 sview = new PymolViewer(pdbsForFile, superposeAdded, theSeqs, ap);
230    }
231    else
232    {
233  0 Console.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
234    }
235  0 return sview;
236    }
237   
238    /**
239    * Converts the list of selected PDB entries (possibly including duplicates
240    * for multiple chains), and corresponding sequences, into a map of sequences
241    * for each distinct PDB file. Returns null if either argument is null, or
242    * their lengths do not match.
243    *
244    * @param pdbs
245    * @param seqs
246    * @return
247    */
 
248  2 toggle Map<PDBEntry, SequenceI[]> getSequencesForPdbs(PDBEntry[] pdbs,
249    SequenceI[] seqs)
250    {
251  2 if (pdbs == null || seqs == null || pdbs.length != seqs.length)
252    {
253  1 return null;
254    }
255   
256    /*
257    * we want only one 'representative' PDBEntry per distinct file name
258    * (there may be entries for distinct chains)
259    */
260  1 Map<String, PDBEntry> pdbsSeen = new HashMap<>();
261   
262    /*
263    * LinkedHashMap preserves order of PDB entries (significant if they
264    * will get superimposed to the first structure)
265    */
266  1 Map<PDBEntry, List<SequenceI>> pdbSeqs = new LinkedHashMap<>();
267  8 for (int i = 0; i < pdbs.length; i++)
268    {
269  7 PDBEntry pdb = pdbs[i];
270  7 SequenceI seq = seqs[i];
271  7 String pdbFile = pdb.getFile();
272  7 if (pdbFile == null || pdbFile.length() == 0)
273    {
274  3 pdbFile = pdb.getId();
275    }
276  7 if (!pdbsSeen.containsKey(pdbFile))
277    {
278  4 pdbsSeen.put(pdbFile, pdb);
279  4 pdbSeqs.put(pdb, new ArrayList<SequenceI>());
280    }
281    else
282    {
283  3 pdb = pdbsSeen.get(pdbFile);
284    }
285  7 List<SequenceI> seqsForPdb = pdbSeqs.get(pdb);
286  7 if (!seqsForPdb.contains(seq))
287    {
288  7 seqsForPdb.add(seq);
289    }
290    }
291   
292    /*
293    * convert to Map<PDBEntry, SequenceI[]>
294    */
295  1 Map<PDBEntry, SequenceI[]> result = new LinkedHashMap<>();
296  1 for (Entry<PDBEntry, List<SequenceI>> entry : pdbSeqs.entrySet())
297    {
298  4 List<SequenceI> theSeqs = entry.getValue();
299  4 result.put(entry.getKey(),
300    theSeqs.toArray(new SequenceI[theSeqs.size()]));
301    }
302   
303  1 return result;
304    }
305   
306    /**
307    * A strictly temporary method pending JAL-1761 refactoring. Determines if all
308    * the passed PDB entries are the same (this is the case if selected sequences
309    * to view structure for are chains of the same structure). If so, calls the
310    * single-pdb version of viewStructures and returns the viewer, else returns
311    * null.
312    *
313    * @param pdbs
314    * @param seqsForPdbs
315    * @param ap
316    * @return
317    */
 
318  0 toggle private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs,
319    SequenceI[] seqsForPdbs, AlignmentPanel ap)
320    {
321  0 List<SequenceI> seqs = new ArrayList<>();
322  0 if (pdbs == null || pdbs.length == 0)
323    {
324  0 return null;
325    }
326  0 int i = 0;
327  0 String firstFile = pdbs[0].getFile();
328  0 for (PDBEntry pdb : pdbs)
329    {
330  0 String pdbFile = pdb.getFile();
331  0 if (pdbFile == null || !pdbFile.equals(firstFile))
332    {
333  0 return null;
334    }
335  0 SequenceI pdbseq = seqsForPdbs[i++];
336  0 if (pdbseq != null)
337    {
338  0 seqs.add(pdbseq);
339    }
340    }
341  0 return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]),
342    ap);
343    }
344   
345    JalviewStructureDisplayI sview = null;
346   
 
347  46 toggle public JalviewStructureDisplayI getJalviewStructureDisplay()
348    {
349  46 return sview;
350    }
351   
 
352  3 toggle public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
353    SequenceI[] seqsForPdb, AlignmentPanel ap)
354    {
355  3 return viewStructures(pdb, seqsForPdb, ap, null);
356    }
357   
 
358  39 toggle public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
359    SequenceI[] seqsForPdb, AlignmentPanel ap, ViewerType viewerType)
360    {
361  39 if (sview != null)
362    {
363  1 sview.setAlignAddedStructures(superposeAdded);
364  1 String pdbId = pdb.getId();
365  1 if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId))
366    {
367  1 sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId);
368    }
369  1 sview.updateTitleAndMenus();
370  1 sview.raiseViewer();
371  1 return sview;
372    }
373  38 if (viewerType == null)
374  2 viewerType = getViewerType();
375  38 if (viewerType.equals(ViewerType.JMOL))
376    {
377  38 sview = new AppJmol(pdb, seqsForPdb, null, ap);
378    }
379  0 else if (viewerType.equals(ViewerType.CHIMERA))
380    {
381  0 sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap);
382    }
383  0 else if (viewerType.equals(ViewerType.CHIMERAX))
384    {
385  0 sview = new ChimeraXViewFrame(pdb, seqsForPdb, null, ap);
386    }
387  0 else if (viewerType.equals(ViewerType.PYMOL))
388    {
389  0 sview = new PymolViewer(pdb, seqsForPdb, null, ap);
390    }
391    else
392    {
393  0 Console.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
394    }
395  38 return sview;
396    }
397   
398    /**
399    * Creates a new panel controlling a structure viewer
400    *
401    * @param type
402    * @param alignPanel
403    * @param viewerData
404    * @param sessionFile
405    * @param vid
406    * @return
407    */
 
408  7 toggle public static JalviewStructureDisplayI createView(ViewerType type,
409    AlignmentPanel alignPanel, StructureViewerModel viewerData,
410    String sessionFile, String vid)
411    {
412  7 JalviewStructureDisplayI viewer = null;
413  7 switch (type)
414    {
415  7 case JMOL:
416  7 viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid);
417    // todo or construct and then openSession(sessionFile)?
418  7 break;
419  0 case CHIMERA:
420  0 viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile,
421    vid);
422  0 break;
423  0 case CHIMERAX:
424  0 viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile,
425    vid);
426  0 break;
427  0 case PYMOL:
428  0 viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid);
429  0 break;
430  0 default:
431  0 Console.error(UNKNOWN_VIEWER_TYPE + type.toString());
432    }
433  7 return viewer;
434    }
435   
 
436  553 toggle public boolean isBusy()
437    {
438  553 if (sview != null)
439    {
440  553 if (!sview.hasMapping())
441    {
442  487 return true;
443    }
444    }
445  66 return false;
446    }
447   
448    /**
449    *
450    * @param pDBid
451    * @return true if view is already showing PDBid
452    */
 
453  0 toggle public boolean hasPdbId(String pDBid)
454    {
455  0 if (sview == null)
456    {
457  0 return false;
458    }
459   
460  0 return sview.getBinding().hasPdbId(pDBid);
461    }
462   
 
463  1 toggle public boolean isVisible()
464    {
465  1 return sview != null && sview.isVisible();
466    }
467   
 
468  36 toggle public void setSuperpose(boolean alignAddedStructures)
469    {
470  36 superposeAdded = alignAddedStructures;
471    }
472   
473    }