Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.structures.models

File AAStructureBindingModel.java

 

Coverage histogram

../../../img/srcFileCovDistChart8.png
19% of files have more coverage

Code metrics

114
207
33
2
826
530
111
0.54
6.27
16.5
3.36

Classes

Class Line # Actions
AAStructureBindingModel 56 206 110 84
0.761363676.1%
AAStructureBindingModel.SuperposeData 100 1 1 0
1.0100%
 

Contributing tests

This file is covered by 18 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.structures.models;
22   
23    import jalview.api.AlignmentViewPanel;
24    import jalview.api.SequenceRenderer;
25    import jalview.api.StructureSelectionManagerProvider;
26    import jalview.api.structures.JalviewStructureDisplayI;
27    import jalview.datamodel.AlignmentI;
28    import jalview.datamodel.HiddenColumns;
29    import jalview.datamodel.PDBEntry;
30    import jalview.datamodel.SequenceI;
31    import jalview.io.DataSourceType;
32    import jalview.schemes.ColourSchemeI;
33    import jalview.structure.AtomSpec;
34    import jalview.structure.StructureListener;
35    import jalview.structure.StructureMapping;
36    import jalview.structure.StructureMappingcommandSet;
37    import jalview.structure.StructureSelectionManager;
38    import jalview.util.Comparison;
39    import jalview.util.MessageManager;
40   
41    import java.awt.Color;
42    import java.util.ArrayList;
43    import java.util.Arrays;
44    import java.util.BitSet;
45    import java.util.List;
46   
47    /**
48    *
49    * A base class to hold common function for protein structure model binding.
50    * Initial version created by refactoring JMol and Chimera binding models, but
51    * other structure viewers could in principle be accommodated in future.
52    *
53    * @author gmcarstairs
54    *
55    */
 
56    public abstract class AAStructureBindingModel
57    extends SequenceStructureBindingModel
58    implements StructureListener, StructureSelectionManagerProvider
59    {
60   
61    private StructureSelectionManager ssm;
62   
63    /*
64    * distinct PDB entries (pdb files) associated
65    * with sequences
66    */
67    private PDBEntry[] pdbEntry;
68   
69    /*
70    * sequences mapped to each pdbentry
71    */
72    private SequenceI[][] sequence;
73   
74    /*
75    * array of target chains for sequences - tied to pdbentry and sequence[]
76    */
77    private String[][] chains;
78   
79    /*
80    * datasource protocol for access to PDBEntrylatest
81    */
82    DataSourceType protocol = null;
83   
84    protected boolean colourBySequence = true;
85   
86    private boolean nucleotide;
87   
88    private boolean finishedInit = false;
89   
90    /**
91    * current set of model filenames loaded in the Jmol instance
92    */
93    protected String[] modelFileNames = null;
94   
95    public String fileLoadingError;
96   
97    /**
98    * Data bean class to simplify parameterisation in superposeStructures
99    */
 
100    protected class SuperposeData
101    {
102    /**
103    * Constructor with alignment width argument
104    *
105    * @param width
106    */
 
107  9 toggle public SuperposeData(int width)
108    {
109  9 pdbResNo = new int[width];
110    }
111   
112    public String filename;
113   
114    public String pdbId;
115   
116    public String chain = "";
117   
118    public boolean isRna;
119   
120    /*
121    * The pdb residue number (if any) mapped to each column of the alignment
122    */
123    public int[] pdbResNo;
124    }
125   
126    /**
127    * Constructor
128    *
129    * @param ssm
130    * @param seqs
131    */
 
132  0 toggle public AAStructureBindingModel(StructureSelectionManager ssm,
133    SequenceI[][] seqs)
134    {
135  0 this.ssm = ssm;
136  0 this.sequence = seqs;
137    }
138   
139    /**
140    * Constructor
141    *
142    * @param ssm
143    * @param pdbentry
144    * @param sequenceIs
145    * @param protocol
146    */
 
147  14 toggle public AAStructureBindingModel(StructureSelectionManager ssm,
148    PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
149    DataSourceType protocol)
150    {
151  14 this.ssm = ssm;
152  14 this.sequence = sequenceIs;
153  14 this.nucleotide = Comparison.isNucleotide(sequenceIs);
154  14 this.pdbEntry = pdbentry;
155  14 this.protocol = protocol;
156  14 resolveChains();
157    }
158   
 
159  14 toggle private boolean resolveChains()
160    {
161    /**
162    * final count of chain mappings discovered
163    */
164  14 int chainmaps = 0;
165    // JBPNote: JAL-2693 - this should be a list of chain mappings per
166    // [pdbentry][sequence]
167  14 String[][] newchains = new String[pdbEntry.length][];
168  14 int pe = 0;
169  14 for (PDBEntry pdb : pdbEntry)
170    {
171  20 SequenceI[] seqsForPdb = sequence[pe];
172  20 if (seqsForPdb != null)
173    {
174  20 newchains[pe] = new String[seqsForPdb.length];
175  20 int se = 0;
176  20 for (SequenceI asq : seqsForPdb)
177    {
178  32 String chain = (chains != null && chains[pe] != null)
179    ? chains[pe][se]
180    : null;
181  32 SequenceI sq = (asq.getDatasetSequence() == null) ? asq
182    : asq.getDatasetSequence();
183  32 if (sq.getAllPDBEntries() != null)
184    {
185  32 for (PDBEntry pdbentry : sq.getAllPDBEntries())
186    {
187  32 if (pdb.getFile() != null && pdbentry.getFile() != null
188    && pdb.getFile().equals(pdbentry.getFile()))
189    {
190  20 String chaincode = pdbentry.getChainCode();
191  20 if (chaincode != null && chaincode.length() > 0)
192    {
193  8 chain = chaincode;
194  8 chainmaps++;
195  8 break;
196    }
197    }
198    }
199    }
200  32 newchains[pe][se] = chain;
201  32 se++;
202    }
203  20 pe++;
204    }
205    }
206   
207  14 chains = newchains;
208  14 return chainmaps > 0;
209    }
 
210  117 toggle public StructureSelectionManager getSsm()
211    {
212  117 return ssm;
213    }
214   
215    /**
216    * Returns the i'th PDBEntry (or null)
217    *
218    * @param i
219    * @return
220    */
 
221  141 toggle public PDBEntry getPdbEntry(int i)
222    {
223  141 return (pdbEntry != null && pdbEntry.length > i) ? pdbEntry[i] : null;
224    }
225   
226    /**
227    * Answers true if this binding includes the given PDB id, else false
228    *
229    * @param pdbId
230    * @return
231    */
 
232  1 toggle public boolean hasPdbId(String pdbId)
233    {
234  1 if (pdbEntry != null)
235    {
236  1 for (PDBEntry pdb : pdbEntry)
237    {
238  1 if (pdb.getId().equals(pdbId))
239    {
240  0 return true;
241    }
242    }
243    }
244  1 return false;
245    }
246   
247    /**
248    * Returns the number of modelled PDB file entries.
249    *
250    * @return
251    */
 
252  306 toggle public int getPdbCount()
253    {
254  306 return pdbEntry == null ? 0 : pdbEntry.length;
255    }
256   
 
257  610 toggle public SequenceI[][] getSequence()
258    {
259  610 return sequence;
260    }
261   
 
262  363 toggle public String[][] getChains()
263    {
264  363 return chains;
265    }
266   
 
267  0 toggle public DataSourceType getProtocol()
268    {
269  0 return protocol;
270    }
271   
272    // TODO may remove this if calling methods can be pulled up here
 
273  1 toggle protected void setPdbentry(PDBEntry[] pdbentry)
274    {
275  1 this.pdbEntry = pdbentry;
276    }
277   
 
278  1 toggle protected void setSequence(SequenceI[][] sequence)
279    {
280  1 this.sequence = sequence;
281    }
282   
 
283  1 toggle protected void setChains(String[][] chains)
284    {
285  1 this.chains = chains;
286    }
287   
288    /**
289    * Construct a title string for the viewer window based on the data Jalview
290    * knows about
291    *
292    * @param viewerName
293    * TODO
294    * @param verbose
295    *
296    * @return
297    */
 
298  21 toggle public String getViewerTitle(String viewerName, boolean verbose)
299    {
300  21 if (getSequence() == null || getSequence().length < 1
301    || getPdbCount() < 1 || getSequence()[0].length < 1)
302    {
303  0 return ("Jalview " + viewerName + " Window");
304    }
305    // TODO: give a more informative title when multiple structures are
306    // displayed.
307  21 StringBuilder title = new StringBuilder(64);
308  21 final PDBEntry pdbe = getPdbEntry(0);
309  21 title.append(viewerName + " view for " + getSequence()[0][0].getName()
310    + ":" + pdbe.getId());
311   
312  21 if (verbose)
313    {
314  21 String method = (String) pdbe.getProperty("method");
315  21 if (method != null)
316    {
317  0 title.append(" Method: ").append(method);
318    }
319  21 String chain = (String) pdbe.getProperty("chains");
320  21 if (chain != null)
321    {
322  0 title.append(" Chain:").append(chain);
323    }
324    }
325  21 return title.toString();
326    }
327   
328    /**
329    * Called by after closeViewer is called, to release any resources and
330    * references so they can be garbage collected. Override if needed.
331    */
 
332  0 toggle protected void releaseUIResources()
333    {
334   
335    }
336   
 
337  51 toggle public boolean isColourBySequence()
338    {
339  51 return colourBySequence;
340    }
341   
 
342  33 toggle public void setColourBySequence(boolean colourBySequence)
343    {
344  33 this.colourBySequence = colourBySequence;
345    }
346   
 
347  47 toggle protected void addSequenceAndChain(int pe, SequenceI[] seq,
348    String[] tchain)
349    {
350  47 if (pe < 0 || pe >= getPdbCount())
351    {
352  0 throw new Error(MessageManager.formatMessage(
353    "error.implementation_error_no_pdbentry_from_index",
354    new Object[]
355    { Integer.valueOf(pe).toString() }));
356    }
357  47 final String nullChain = "TheNullChain";
358  47 List<SequenceI> s = new ArrayList<SequenceI>();
359  47 List<String> c = new ArrayList<String>();
360  47 if (getChains() == null)
361    {
362  0 setChains(new String[getPdbCount()][]);
363    }
364  47 if (getSequence()[pe] != null)
365    {
366  132 for (int i = 0; i < getSequence()[pe].length; i++)
367    {
368  86 s.add(getSequence()[pe][i]);
369  86 if (getChains()[pe] != null)
370    {
371  84 if (i < getChains()[pe].length)
372    {
373  84 c.add(getChains()[pe][i]);
374    }
375    else
376    {
377  0 c.add(nullChain);
378    }
379    }
380    else
381    {
382  2 if (tchain != null && tchain.length > 0)
383    {
384  0 c.add(nullChain);
385    }
386    }
387    }
388    }
389  134 for (int i = 0; i < seq.length; i++)
390    {
391  87 if (!s.contains(seq[i]))
392    {
393  1 s.add(seq[i]);
394  1 if (tchain != null && i < tchain.length)
395    {
396  0 c.add(tchain[i] == null ? nullChain : tchain[i]);
397    }
398    }
399    }
400  47 SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
401  47 getSequence()[pe] = tmp;
402  47 if (c.size() > 0)
403    {
404  44 String[] tch = c.toArray(new String[c.size()]);
405  128 for (int i = 0; i < tch.length; i++)
406    {
407  84 if (tch[i] == nullChain)
408    {
409  0 tch[i] = null;
410    }
411    }
412  44 getChains()[pe] = tch;
413    }
414    else
415    {
416  3 getChains()[pe] = null;
417    }
418    }
419   
420    /**
421    * add structures and any known sequence associations
422    *
423    * @returns the pdb entries added to the current set.
424    */
 
425  1 toggle public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
426    SequenceI[][] seq, String[][] chns)
427    {
428  1 List<PDBEntry> v = new ArrayList<PDBEntry>();
429  1 List<int[]> rtn = new ArrayList<int[]>();
430  2 for (int i = 0; i < getPdbCount(); i++)
431    {
432  1 v.add(getPdbEntry(i));
433    }
434  2 for (int i = 0; i < pdbe.length; i++)
435    {
436  1 int r = v.indexOf(pdbe[i]);
437  1 if (r == -1 || r >= getPdbCount())
438    {
439  1 rtn.add(new int[] { v.size(), i });
440  1 v.add(pdbe[i]);
441    }
442    else
443    {
444    // just make sure the sequence/chain entries are all up to date
445  0 addSequenceAndChain(r, seq[i], chns[i]);
446    }
447    }
448  1 pdbe = v.toArray(new PDBEntry[v.size()]);
449  1 setPdbentry(pdbe);
450  1 if (rtn.size() > 0)
451    {
452    // expand the tied sequence[] and string[] arrays
453  1 SequenceI[][] sqs = new SequenceI[getPdbCount()][];
454  1 String[][] sch = new String[getPdbCount()][];
455  1 System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length);
456  1 System.arraycopy(getChains(), 0, sch, 0, this.getChains().length);
457  1 setSequence(sqs);
458  1 setChains(sch);
459  1 pdbe = new PDBEntry[rtn.size()];
460  2 for (int r = 0; r < pdbe.length; r++)
461    {
462  1 int[] stri = (rtn.get(r));
463    // record the pdb file as a new addition
464  1 pdbe[r] = getPdbEntry(stri[0]);
465    // and add the new sequence/chain entries
466  1 addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
467    }
468    }
469    else
470    {
471  0 pdbe = null;
472    }
473  1 return pdbe;
474    }
475   
476    /**
477    * Add sequences to the pe'th pdbentry's sequence set.
478    *
479    * @param pe
480    * @param seq
481    */
 
482  46 toggle public void addSequence(int pe, SequenceI[] seq)
483    {
484  46 addSequenceAndChain(pe, seq, null);
485    }
486   
487    /**
488    * add the given sequences to the mapping scope for the given pdb file handle
489    *
490    * @param pdbFile
491    * - pdbFile identifier
492    * @param seq
493    * - set of sequences it can be mapped to
494    */
 
495  52 toggle public void addSequenceForStructFile(String pdbFile, SequenceI[] seq)
496    {
497  104 for (int pe = 0; pe < getPdbCount(); pe++)
498    {
499  52 if (getPdbEntry(pe).getFile().equals(pdbFile))
500    {
501  32 addSequence(pe, seq);
502    }
503    }
504    }
505   
506    @Override
507    public abstract void highlightAtoms(List<AtomSpec> atoms);
508   
 
509  0 toggle protected boolean isNucleotide()
510    {
511  0 return this.nucleotide;
512    }
513   
514    /**
515    * Returns a readable description of all mappings for the wrapped pdbfile to
516    * any mapped sequences
517    *
518    * @param pdbfile
519    * @param seqs
520    * @return
521    */
 
522  0 toggle public String printMappings()
523    {
524  0 if (pdbEntry == null)
525    {
526  0 return "";
527    }
528  0 StringBuilder sb = new StringBuilder(128);
529  0 for (int pdbe = 0; pdbe < getPdbCount(); pdbe++)
530    {
531  0 String pdbfile = getPdbEntry(pdbe).getFile();
532  0 List<SequenceI> seqs = Arrays.asList(getSequence()[pdbe]);
533  0 sb.append(getSsm().printMappings(pdbfile, seqs));
534    }
535  0 return sb.toString();
536    }
537   
538    /**
539    * Returns the mapped structure position for a given aligned column of a given
540    * sequence, or -1 if the column is gapped, beyond the end of the sequence, or
541    * not mapped to structure.
542    *
543    * @param seq
544    * @param alignedPos
545    * @param mapping
546    * @return
547    */
 
548  620 toggle protected int getMappedPosition(SequenceI seq, int alignedPos,
549    StructureMapping mapping)
550    {
551  620 if (alignedPos >= seq.getLength())
552    {
553  1 return -1;
554    }
555   
556  619 if (Comparison.isGap(seq.getCharAt(alignedPos)))
557    {
558  6 return -1;
559    }
560  613 int seqPos = seq.findPosition(alignedPos);
561  613 int pos = mapping.getPDBResNum(seqPos);
562  613 return pos;
563    }
564   
565    /**
566    * Helper method to identify residues that can participate in a structure
567    * superposition command. For each structure, identify a sequence in the
568    * alignment which is mapped to the structure. Identify non-gapped columns in
569    * the sequence which have a mapping to a residue in the structure. Returns
570    * the index of the first structure that has a mapping to the alignment.
571    *
572    * @param alignment
573    * the sequence alignment which is the basis of structure
574    * superposition
575    * @param matched
576    * a BitSet, where bit j is set to indicate that every structure has
577    * a mapped residue present in column j (so the column can
578    * participate in structure alignment)
579    * @param structures
580    * an array of data beans corresponding to pdb file index
581    * @return
582    */
 
583  3 toggle protected int findSuperposableResidues(AlignmentI alignment,
584    BitSet matched, SuperposeData[] structures)
585    {
586  3 int refStructure = -1;
587  3 String[] files = getStructureFiles();
588  3 if (files == null)
589    {
590  0 return -1;
591    }
592  11 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
593    {
594  8 StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
595  8 int lastPos = -1;
596   
597    /*
598    * Find the first mapped sequence (if any) for this PDB entry which is in
599    * the alignment
600    */
601  8 final int seqCountForPdbFile = getSequence()[pdbfnum].length;
602  16 for (int s = 0; s < seqCountForPdbFile; s++)
603    {
604  8 for (StructureMapping mapping : mappings)
605    {
606  8 final SequenceI theSequence = getSequence()[pdbfnum][s];
607  8 if (mapping.getSequence() == theSequence
608    && alignment.findIndex(theSequence) > -1)
609    {
610  8 if (refStructure < 0)
611    {
612  3 refStructure = pdbfnum;
613    }
614  638 for (int r = 0; r < alignment.getWidth(); r++)
615    {
616  630 if (!matched.get(r))
617    {
618  10 continue;
619    }
620  620 int pos = getMappedPosition(theSequence, r, mapping);
621  620 if (pos < 1 || pos == lastPos)
622    {
623  7 matched.clear(r);
624  7 continue;
625    }
626  613 lastPos = pos;
627  613 structures[pdbfnum].pdbResNo[r] = pos;
628    }
629  8 String chain = mapping.getChain();
630  8 if (chain != null && chain.trim().length() > 0)
631    {
632  8 structures[pdbfnum].chain = chain;
633    }
634  8 structures[pdbfnum].pdbId = mapping.getPdbId();
635  8 structures[pdbfnum].isRna = theSequence.getRNA() != null;
636   
637    /*
638    * move on to next pdb file (ignore sequences for other chains
639    * for the same structure)
640    */
641  8 s = seqCountForPdbFile;
642  8 break;
643    }
644    }
645    }
646    }
647  3 return refStructure;
648    }
649   
650    /**
651    * Returns true if the structure viewer has loaded all of the files of
652    * interest (identified by the file mapping having been set up), or false if
653    * any are still not loaded after a timeout interval.
654    *
655    * @param files
656    */
 
657  1 toggle protected boolean waitForFileLoad(String[] files)
658    {
659    /*
660    * give up after 10 secs plus 1 sec per file
661    */
662  1 long starttime = System.currentTimeMillis();
663  1 long endTime = 10000 + 1000 * files.length + starttime;
664  1 String notLoaded = null;
665   
666  1 boolean waiting = true;
667  2 while (waiting && System.currentTimeMillis() < endTime)
668    {
669  1 waiting = false;
670  1 for (String file : files)
671    {
672  2 notLoaded = file;
673  2 if (file == null)
674    {
675  0 continue;
676    }
677  2 try
678    {
679  2 StructureMapping[] sm = getSsm().getMapping(file);
680  2 if (sm == null || sm.length == 0)
681    {
682  0 waiting = true;
683    }
684    } catch (Throwable x)
685    {
686  0 waiting = true;
687    }
688    }
689    }
690   
691  1 if (waiting)
692    {
693  0 System.err.println(
694    "Timed out waiting for structure viewer to load file "
695    + notLoaded);
696  0 return false;
697    }
698  1 return true;
699    }
700   
 
701  0 toggle @Override
702    public boolean isListeningFor(SequenceI seq)
703    {
704  0 if (sequence != null)
705    {
706  0 for (SequenceI[] seqs : sequence)
707    {
708  0 if (seqs != null)
709    {
710  0 for (SequenceI s : seqs)
711    {
712  0 if (s == seq || (s.getDatasetSequence() != null
713    && s.getDatasetSequence() == seq.getDatasetSequence()))
714    {
715  0 return true;
716    }
717    }
718    }
719    }
720    }
721  0 return false;
722    }
723   
 
724  180 toggle public boolean isFinishedInit()
725    {
726  180 return finishedInit;
727    }
728   
 
729  20 toggle public void setFinishedInit(boolean fi)
730    {
731  20 this.finishedInit = fi;
732    }
733   
734    /**
735    * Returns a list of chains mapped in this viewer.
736    *
737    * @return
738    */
739    public abstract List<String> getChainNames();
740   
741    /**
742    * Returns the Jalview panel hosting the structure viewer (if any)
743    *
744    * @return
745    */
 
746  0 toggle public JalviewStructureDisplayI getViewer()
747    {
748  0 return null;
749    }
750   
751    public abstract void setJalviewColourScheme(ColourSchemeI cs);
752   
753    /**
754    * Constructs and sends a command to align structures against a reference
755    * structure, based on one or more sequence alignments. May optionally return
756    * an error or warning message for the alignment command.
757    *
758    * @param alignments
759    * an array of alignments to process
760    * @param structureIndices
761    * an array of corresponding reference structures (index into pdb
762    * file array); if a negative value is passed, the first PDB file
763    * mapped to an alignment sequence is used as the reference for
764    * superposition
765    * @param hiddenCols
766    * an array of corresponding hidden columns for each alignment
767    * @return
768    */
769    public abstract String superposeStructures(AlignmentI[] alignments,
770    int[] structureIndices, HiddenColumns[] hiddenCols);
771   
772    public abstract void setBackgroundColour(Color col);
773   
774    protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
775    String[] files, SequenceRenderer sr, AlignmentViewPanel avp);
776   
777    /**
778    * returns the current sequenceRenderer that should be used to colour the
779    * structures
780    *
781    * @param alignment
782    *
783    * @return
784    */
785    public abstract SequenceRenderer getSequenceRenderer(
786    AlignmentViewPanel alignment);
787   
788    protected abstract void colourBySequence(
789    StructureMappingcommandSet[] colourBySequenceCommands);
790   
791    public abstract void colourByChain();
792   
793    public abstract void colourByCharge();
794   
795    /**
796    * colour any structures associated with sequences in the given alignment
797    * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
798    * if colourBySequence is enabled.
799    */
 
800  22 toggle public void colourBySequence(AlignmentViewPanel alignmentv)
801    {
802  22 if (!colourBySequence || !isLoadingFinished())
803    {
804  13 return;
805    }
806  9 if (getSsm() == null)
807    {
808  0 return;
809    }
810  9 String[] files = getStructureFiles();
811   
812  9 SequenceRenderer sr = getSequenceRenderer(alignmentv);
813   
814  9 StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands(
815    files, sr, alignmentv);
816  9 colourBySequence(colourBySequenceCommands);
817    }
818   
 
819  183 toggle public boolean hasFileLoadingError()
820    {
821  183 return fileLoadingError != null && fileLoadingError.length() > 0;
822    }
823   
824    public abstract jalview.api.FeatureRenderer getFeatureRenderer(
825    AlignmentViewPanel alignment);
826    }