JAL-629 Now showing temp fac and secondary structure for File opened structures and...
[jalview.git] / src / jalview / io / StructureFile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io;
22
23 import java.awt.Color;
24 import java.io.IOException;
25 import java.lang.reflect.Constructor;
26 import java.net.MalformedURLException;
27 import java.util.List;
28 import java.util.Vector;
29
30 import jalview.analysis.AlignSeq;
31 import jalview.api.FeatureSettingsModelI;
32 import jalview.datamodel.Alignment;
33 import jalview.datamodel.AlignmentAnnotation;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.DBRefEntry;
36 import jalview.datamodel.DBRefSource;
37 import jalview.datamodel.PDBEntry;
38 import jalview.datamodel.PDBEntry.Type;
39 import jalview.datamodel.SequenceI;
40 import jalview.ext.jmol.JmolParser;
41 import jalview.structure.StructureImportSettings;
42 import jalview.structure.StructureImportSettings.TFType;
43 import mc_view.PDBChain;
44
45 public abstract class StructureFile extends AlignFile
46 {
47   private String id;
48
49   private PDBEntry.Type dbRefType;
50
51   /**
52    * set to true to add derived sequence annotations (temp factor read from
53    * file, or computed secondary structure) to the alignment
54    */
55   protected boolean visibleChainAnnotation = false;
56
57   /**
58    * Set true to predict secondary structure (using JMol for protein, Annotate3D
59    * for RNA)
60    */
61   protected boolean predictSecondaryStructure = false;
62
63   /**
64    * Set true (with predictSecondaryStructure=true) to predict secondary
65    * structure using an external service (currently Annotate3D for RNA only)
66    */
67   protected boolean externalSecondaryStructure = false;
68
69   private Vector<PDBChain> chains;
70
71   private boolean pdbIdAvailable;
72
73   private StructureImportSettings.TFType temperatureFactorType = TFType.DEFAULT;
74
75   private String paeMatrix = null;
76
77   private boolean alphaFoldModel;
78
79   public void setPAEMatrix(String paeFilename)
80   {
81     paeMatrix = paeFilename;
82   }
83
84   public String getPAEMatrix()
85   {
86     return paeMatrix;
87   }
88
89   public boolean hasPAEMatrix()
90   {
91     return paeMatrix != null;
92   }
93
94   public void setTemperatureFactorType(StructureImportSettings.TFType t)
95   {
96     this.temperatureFactorType = t;
97   }
98
99   public StructureImportSettings.TFType getTemperatureFactorType()
100   {
101     return temperatureFactorType;
102   }
103
104   public void setAlphafoldModel(boolean afm)
105   {
106     alphaFoldModel = afm;
107   }
108
109   public boolean isAlphafoldModel()
110   {
111     return alphaFoldModel;
112   }
113
114   public StructureFile(Object inFile, DataSourceType sourceType)
115           throws IOException
116   {
117     this(inFile, sourceType, null);
118   }
119
120   public StructureFile(Object inFile, DataSourceType sourceType,
121           StructureImportSettings.TFType tempfacType) throws IOException
122   {
123     super(false, inFile, sourceType);
124     this.setTemperatureFactorType(tempfacType);
125     doParse();
126   }
127
128   public StructureFile(FileParse fp) throws IOException
129   {
130     this(fp, true);
131   }
132
133   public StructureFile(FileParse fp, boolean doXferSettings)
134           throws IOException
135   {
136     super(fp, doXferSettings);
137   }
138
139   public void addSettings(boolean addAlignmentAnnotations,
140           boolean predictSecondaryStructure, boolean externalSecStr)
141   {
142     this.visibleChainAnnotation = addAlignmentAnnotations;
143     this.predictSecondaryStructure = predictSecondaryStructure;
144     this.externalSecondaryStructure = externalSecStr;
145   }
146
147   public void xferSettings()
148   {
149     if (this.getDoXferSettings())
150     {
151       this.visibleChainAnnotation = StructureImportSettings
152               .isVisibleChainAnnotation();
153       this.predictSecondaryStructure = StructureImportSettings
154               .isProcessSecondaryStructure();
155       this.externalSecondaryStructure = StructureImportSettings
156               .isExternalSecondaryStructure();
157       this.temperatureFactorType = StructureImportSettings
158               .getTemperatureFactorType();
159     }
160   }
161
162   public StructureFile(boolean parseImmediately, Object dataObject,
163           DataSourceType sourceType) throws IOException
164   {
165     super(parseImmediately, dataObject, sourceType);
166   }
167
168   public StructureFile(boolean a, FileParse fp) throws IOException
169   {
170     super(a, fp);
171   }
172
173   public StructureFile()
174   {
175   }
176
177   protected SequenceI postProcessChain(PDBChain chain)
178   {
179     SequenceI pdbSequence = chain.sequence;
180     pdbSequence.setName(getId() + "|" + pdbSequence.getName());
181     PDBEntry entry = new PDBEntry();
182     entry.setId(getId());
183     entry.setFakedPDBId(!isPPDBIdAvailable());
184     entry.setType(getStructureFileType());
185     if (chain.id != null)
186     {
187       entry.setChainCode(chain.id);
188     }
189     if (inFile != null)
190     {
191       entry.setFile(inFile.getAbsolutePath());
192     }
193     else
194     {
195       entry.setFile(getDataName());
196     }
197
198     DBRefEntry sourceDBRef = new DBRefEntry();
199     sourceDBRef.setAccessionId(getId());
200     sourceDBRef.setSource(DBRefSource.PDB);
201     // TODO: specify version for 'PDB' database ref if it is read from a file.
202     // TODO: decide if jalview.io should be creating primary refs!
203     sourceDBRef.setVersion("");
204     pdbSequence.addPDBId(entry);
205     pdbSequence.addDBRef(sourceDBRef);
206     SequenceI chainseq = pdbSequence;
207     seqs.addElement(chainseq);
208     AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
209
210     if (chainannot != null && visibleChainAnnotation)
211     {
212       for (int ai = 0; ai < chainannot.length; ai++)
213       {
214         chainannot[ai].visible = visibleChainAnnotation;
215         annotations.addElement(chainannot[ai]);
216       }
217     }
218     return chainseq;
219   }
220
221   /**
222    * filetype of structure file - default is PDB
223    */
224   String structureFileType = PDBEntry.Type.PDB.toString();
225
226   protected void setStructureFileType(String structureFileType)
227   {
228     this.structureFileType = structureFileType;
229   }
230
231   /**
232    * filetype of last file processed
233    * 
234    * @return
235    */
236   public String getStructureFileType()
237   {
238     return structureFileType;
239   }
240
241   @SuppressWarnings({ "unchecked", "rawtypes" })
242   protected void processPdbFileWithAnnotate3d(List<SequenceI> rna)
243           throws Exception
244   {
245     // System.out.println("this is a PDB format and RNA sequence");
246     // note: we use reflection here so that the applet can compile and run
247     // without the HTTPClient bits and pieces needed for accessing Annotate3D
248     // web service
249     try
250     {
251       Class cl = Class.forName("jalview.ws.jws1.Annotate3D");
252       if (cl != null)
253       {
254         // TODO: use the PDB ID of the structure if one is available, to save
255         // bandwidth and avoid uploading the whole structure to the service
256         Object annotate3d = cl.getConstructor(new Class[] {})
257                 .newInstance(new Object[] {});
258         AlignmentI al = ((AlignmentI) cl
259                 .getMethod("getRNAMLFor", new Class[]
260                 { FileParse.class })
261                 .invoke(annotate3d, new Object[]
262                 { new FileParse(getDataName(), dataSourceType) }));
263         for (SequenceI sq : al.getSequences())
264         {
265           if (sq.getDatasetSequence() != null)
266           {
267             if (sq.getDatasetSequence().getAllPDBEntries() != null)
268             {
269               sq.getDatasetSequence().getAllPDBEntries().clear();
270             }
271           }
272           else
273           {
274             if (sq.getAllPDBEntries() != null)
275             {
276               sq.getAllPDBEntries().clear();
277             }
278           }
279         }
280         replaceAndUpdateChains(rna, al, AlignSeq.DNA, false);
281       }
282     } catch (ClassNotFoundException x)
283     {
284       // ignore classnotfounds - occurs in applet
285     }
286   }
287
288   @SuppressWarnings("unchecked")
289   protected void replaceAndUpdateChains(List<SequenceI> prot, AlignmentI al,
290           String pep, boolean b)
291   {
292     List<List<? extends Object>> replaced = AlignSeq
293             .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
294                     false);
295     for (PDBChain ch : getChains())
296     {
297       int p = 0;
298       for (SequenceI sq : (List<SequenceI>) replaced.get(0))
299       {
300         p++;
301         if (sq == ch.sequence || sq.getDatasetSequence() == ch.sequence)
302         {
303           p = -p;
304           break;
305         }
306       }
307       if (p < 0)
308       {
309         p = -p - 1;
310         // set shadow entry for chains
311         ch.shadow = (SequenceI) replaced.get(1).get(p);
312         ch.shadowMap = ((AlignSeq) replaced.get(2).get(p))
313                 .getMappingFromS1(false);
314       }
315     }
316   }
317
318   /**
319    * Predict secondary structure for RNA and/or protein sequences and add as
320    * annotations
321    * 
322    * @param rnaSequences
323    * @param proteinSequences
324    */
325   protected void addSecondaryStructure(List<SequenceI> rnaSequences,
326           List<SequenceI> proteinSequences)
327   {
328     /*
329      * Currently using Annotate3D for RNA, but only if the 'use external
330      * prediction' flag is set
331      */
332     if (externalSecondaryStructure && rnaSequences.size() > 0)
333     {
334       try
335       {
336         processPdbFileWithAnnotate3d(rnaSequences);
337       } catch (Exception x)
338       {
339         System.err.println("Exceptions when dealing with RNA in pdb file");
340         x.printStackTrace();
341
342       }
343     }
344
345     /*
346      * Currently using JMol PDB parser for peptide
347      */
348     if (proteinSequences.size() > 0)
349     {
350       try
351       {
352         processWithJmolParser(proteinSequences, true);
353       } catch (Exception x)
354       {
355         System.err.println(
356                 "Exceptions from Jmol when processing data in pdb file");
357         x.printStackTrace();
358       }
359     }
360   }
361
362   @SuppressWarnings({ "unchecked", "rawtypes" })
363   private void NOTprocessWithJmolParser(List<SequenceI> prot)
364           throws Exception
365   {
366     try
367     {
368
369       Class cl = Class.forName("jalview.ext.jmol.JmolParser");
370       if (cl != null)
371       {
372         final Constructor constructor = cl
373                 .getConstructor(new Class[]
374                 { FileParse.class });
375         final Object[] args = new Object[] {
376             new FileParse(getDataName(), dataSourceType) };
377
378         StructureImportSettings.setShowSeqFeatures(false);
379         StructureImportSettings.setVisibleChainAnnotation(false);
380         StructureImportSettings
381                 .setProcessSecondaryStructure(predictSecondaryStructure);
382         StructureImportSettings
383                 .setExternalSecondaryStructure(externalSecondaryStructure);
384         StructureImportSettings
385                 .setTemperatureFactorType(temperatureFactorType);
386         Object jmf = constructor.newInstance(args);
387         AlignmentI al = new Alignment((SequenceI[]) cl
388                 .getMethod("getSeqsAsArray", new Class[] {}).invoke(jmf));
389         cl.getMethod("addAnnotations", new Class[] { AlignmentI.class })
390                 .invoke(jmf, al);
391         for (SequenceI sq : al.getSequences())
392         {
393           if (sq.getDatasetSequence() != null)
394           {
395             sq.getDatasetSequence().getAllPDBEntries().clear();
396           }
397           else
398           {
399             sq.getAllPDBEntries().clear();
400           }
401         }
402         replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
403       }
404     } catch (ClassNotFoundException q)
405     {
406     }
407     StructureImportSettings.setShowSeqFeatures(true);
408   }
409
410   private void processWithJmolParser(List<SequenceI> prot,
411           boolean doXferSettings) throws MalformedURLException, IOException
412   {
413     FileParse fp = new FileParse(getDataName(), dataSourceType);
414
415     StructureImportSettings.setShowSeqFeatures(false);
416     StructureImportSettings.setVisibleChainAnnotation(false);
417     StructureImportSettings
418             .setProcessSecondaryStructure(predictSecondaryStructure);
419     StructureImportSettings
420             .setExternalSecondaryStructure(externalSecondaryStructure);
421     StructureImportSettings.setTemperatureFactorType(temperatureFactorType);
422     JmolParser jmf = new JmolParser(fp, doXferSettings);
423     AlignmentI al = new Alignment((SequenceI[]) jmf.getSeqsAsArray());
424     jmf.addAnnotations(al);
425     for (SequenceI sq : al.getSequences())
426     {
427       if (sq.getDatasetSequence() != null)
428       {
429         sq.getDatasetSequence().getAllPDBEntries().clear();
430       }
431       else
432       {
433         sq.getAllPDBEntries().clear();
434       }
435     }
436     replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
437     StructureImportSettings.setShowSeqFeatures(true);
438   }
439
440   /**
441    * Answers the first PDBChain found matching the given id, or null if none is
442    * found
443    * 
444    * @param id
445    * @return
446    */
447   public PDBChain findChain(String id)
448   {
449     for (PDBChain chain : getChains())
450     {
451       if (chain.id.equals(id))
452       {
453         return chain;
454       }
455     }
456     return null;
457   }
458
459   public void makeResidueList()
460   {
461     for (PDBChain chain : getChains())
462     {
463       chain.makeResidueList(visibleChainAnnotation);
464     }
465   }
466
467   public void makeCaBondList()
468   {
469     for (PDBChain chain : getChains())
470     {
471       chain.makeCaBondList();
472     }
473   }
474
475   public void setChargeColours()
476   {
477     for (PDBChain chain : getChains())
478     {
479       chain.setChargeColours();
480     }
481   }
482
483   public void setColours(jalview.schemes.ColourSchemeI cs)
484   {
485     for (PDBChain chain : getChains())
486     {
487       chain.setChainColours(cs);
488     }
489   }
490
491   public void setChainColours()
492   {
493     int i = 0;
494     for (PDBChain chain : getChains())
495     {
496       chain.setChainColours(Color.getHSBColor(1.0f / i++, .4f, 1.0f));
497     }
498   }
499
500   public static boolean isRNA(SequenceI seq)
501   {
502     int length = seq.getLength();
503     for (int i = 0; i < length; i++)
504     {
505       char c = seq.getCharAt(i);
506       if ((c != 'A') && (c != 'C') && (c != 'G') && (c != 'U'))
507       {
508         return false;
509       }
510     }
511     return true;
512   }
513
514   /**
515    * make a friendly ID string.
516    * 
517    * @param dataName
518    * @return truncated dataName to after last '/' and pruned .extension if
519    *         present
520    */
521   protected String safeName(String dataName)
522   {
523     int p = 0;
524     while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
525     {
526       dataName = dataName.substring(p + 1);
527     }
528     if (dataName.indexOf(".") > -1)
529     {
530       dataName = dataName.substring(0, dataName.lastIndexOf("."));
531     }
532     return dataName;
533   }
534
535   public String getId()
536   {
537     return id;
538   }
539
540   public void setId(String id)
541   {
542     this.id = id;
543   }
544
545   public Vector<PDBChain> getChains()
546   {
547     return chains;
548   }
549
550   public void setChains(Vector<PDBChain> chains)
551   {
552     this.chains = chains;
553   }
554
555   public Type getDbRefType()
556   {
557     return dbRefType;
558   }
559
560   public void setDbRefType(String dbRefType)
561   {
562     this.dbRefType = Type.getType(dbRefType);
563   }
564
565   public void setDbRefType(Type dbRefType)
566   {
567     this.dbRefType = dbRefType;
568   }
569
570   /**
571    * Returns a descriptor for suitable feature display settings with
572    * <ul>
573    * <li>ResNums or insertions features visible</li>
574    * <li>insertions features coloured red</li>
575    * <li>ResNum features coloured by label</li>
576    * <li>Insertions displayed above (on top of) ResNums</li>
577    * </ul>
578    */
579   @Override
580   public FeatureSettingsModelI getFeatureColourScheme()
581   {
582     return new PDBFeatureSettings();
583   }
584
585   /**
586    * Answers true if the structure file has a PDBId
587    * 
588    * @return
589    */
590   public boolean isPPDBIdAvailable()
591   {
592     return pdbIdAvailable;
593   }
594
595   public void setPDBIdAvailable(boolean pdbIdAvailable)
596   {
597     this.pdbIdAvailable = pdbIdAvailable;
598   }
599 }