6ec029837bc7add748a442247f6ca19efb319161
[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 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(TFType t)
95   {
96     this.temperatureFactorType = t;
97   }
98
99   public 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           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   private void processWithJmolParser(List<SequenceI> prot,
363           boolean doXferSettings) throws MalformedURLException, IOException
364   {
365     FileParse fp = new FileParse(getDataName(), dataSourceType);
366
367     StructureImportSettings.setShowSeqFeatures(false);
368     StructureImportSettings.setVisibleChainAnnotation(false);
369     StructureImportSettings
370             .setProcessSecondaryStructure(predictSecondaryStructure);
371     StructureImportSettings
372             .setExternalSecondaryStructure(externalSecondaryStructure);
373     StructureImportSettings.setTemperatureFactorType(temperatureFactorType);
374     JmolParser jmf = new JmolParser(fp, doXferSettings);
375     AlignmentI al = new Alignment((SequenceI[]) jmf.getSeqsAsArray());
376     jmf.addAnnotations(al);
377     for (SequenceI sq : al.getSequences())
378     {
379       if (sq.getDatasetSequence() != null)
380       {
381         sq.getDatasetSequence().getAllPDBEntries().clear();
382       }
383       else
384       {
385         sq.getAllPDBEntries().clear();
386       }
387     }
388     replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
389     StructureImportSettings.setShowSeqFeatures(true);
390   }
391
392   /**
393    * Answers the first PDBChain found matching the given id, or null if none is
394    * found
395    * 
396    * @param id
397    * @return
398    */
399   public PDBChain findChain(String id)
400   {
401     for (PDBChain chain : getChains())
402     {
403       if (chain.id.equals(id))
404       {
405         return chain;
406       }
407     }
408     return null;
409   }
410
411   public void makeResidueList()
412   {
413     for (PDBChain chain : getChains())
414     {
415       chain.makeResidueList(visibleChainAnnotation);
416     }
417   }
418
419   public void makeCaBondList()
420   {
421     for (PDBChain chain : getChains())
422     {
423       chain.makeCaBondList();
424     }
425   }
426
427   public void setChargeColours()
428   {
429     for (PDBChain chain : getChains())
430     {
431       chain.setChargeColours();
432     }
433   }
434
435   public void setColours(jalview.schemes.ColourSchemeI cs)
436   {
437     for (PDBChain chain : getChains())
438     {
439       chain.setChainColours(cs);
440     }
441   }
442
443   public void setChainColours()
444   {
445     int i = 0;
446     for (PDBChain chain : getChains())
447     {
448       chain.setChainColours(Color.getHSBColor(1.0f / i++, .4f, 1.0f));
449     }
450   }
451
452   public static boolean isRNA(SequenceI seq)
453   {
454     int length = seq.getLength();
455     for (int i = 0; i < length; i++)
456     {
457       char c = seq.getCharAt(i);
458       if ((c != 'A') && (c != 'C') && (c != 'G') && (c != 'U'))
459       {
460         return false;
461       }
462     }
463     return true;
464   }
465
466   /**
467    * make a friendly ID string.
468    * 
469    * @param dataName
470    * @return truncated dataName to after last '/' and pruned .extension if
471    *         present
472    */
473   protected String safeName(String dataName)
474   {
475     int p = 0;
476     while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
477     {
478       dataName = dataName.substring(p + 1);
479     }
480     if (dataName.indexOf(".") > -1)
481     {
482       dataName = dataName.substring(0, dataName.lastIndexOf("."));
483     }
484     return dataName;
485   }
486
487   public String getId()
488   {
489     return id;
490   }
491
492   public void setId(String id)
493   {
494     this.id = id;
495   }
496
497   public Vector<PDBChain> getChains()
498   {
499     return chains;
500   }
501
502   public void setChains(Vector<PDBChain> chains)
503   {
504     this.chains = chains;
505   }
506
507   public Type getDbRefType()
508   {
509     return dbRefType;
510   }
511
512   public void setDbRefType(String dbRefType)
513   {
514     this.dbRefType = Type.getType(dbRefType);
515   }
516
517   public void setDbRefType(Type dbRefType)
518   {
519     this.dbRefType = dbRefType;
520   }
521
522   /**
523    * Returns a descriptor for suitable feature display settings with
524    * <ul>
525    * <li>ResNums or insertions features visible</li>
526    * <li>insertions features coloured red</li>
527    * <li>ResNum features coloured by label</li>
528    * <li>Insertions displayed above (on top of) ResNums</li>
529    * </ul>
530    */
531   @Override
532   public FeatureSettingsModelI getFeatureColourScheme()
533   {
534     return new PDBFeatureSettings();
535   }
536
537   /**
538    * Answers true if the structure file has a PDBId
539    * 
540    * @return
541    */
542   public boolean isPPDBIdAvailable()
543   {
544     return pdbIdAvailable;
545   }
546
547   public void setPDBIdAvailable(boolean pdbIdAvailable)
548   {
549     this.pdbIdAvailable = pdbIdAvailable;
550   }
551 }