JAL-3929 URLencode pdbid to remove problematic characters - propagate urlEncoded...
[jalview.git] / src / jalview / ws / dbsources / EBIAlfaFold.java
1
2 /*
3  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
4  * Copyright (C) $$Year-Rel$$ The Jalview Authors
5  * 
6  * This file is part of Jalview.
7  * 
8  * Jalview is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License 
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *  
13  * Jalview is distributed in the hope that it will be useful, but 
14  * WITHOUT ANY WARRANTY; without even the implied warranty 
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
16  * PURPOSE.  See the GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
20  * The Jalview Authors are detailed in the 'AUTHORS' file.
21  */
22 package jalview.ws.dbsources;
23
24 import jalview.api.FeatureSettingsModelI;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.DBRefEntry;
28 import jalview.datamodel.DBRefSource;
29 import jalview.datamodel.PDBEntry;
30 import jalview.datamodel.PDBEntry.Type;
31 import jalview.datamodel.SequenceFeature;
32 import jalview.datamodel.SequenceI;
33 import jalview.datamodel.features.SequenceFeaturesI;
34 import jalview.io.DataSourceType;
35 import jalview.io.FileFormat;
36 import jalview.io.FileFormatI;
37 import jalview.io.FormatAdapter;
38 import jalview.io.PDBFeatureSettings;
39 import jalview.structure.StructureImportSettings;
40 import jalview.util.HttpUtils;
41 import jalview.util.MessageManager;
42 import jalview.ws.ebi.EBIFetchClient;
43 import jalview.ws.utils.UrlDownloadClient;
44
45 import java.io.File;
46 import java.util.ArrayList;
47 import java.util.List;
48
49 import com.stevesoft.pat.Regex;
50
51 /**
52  * @author JimP
53  * 
54  */
55 public class EBIAlfaFold extends EbiFileRetrievedProxy
56 {
57   private static final String SEPARATOR = "|";
58
59   private static final String COLON = ":";
60
61   private static final int PDB_ID_LENGTH = 4;
62
63   public EBIAlfaFold()
64   {
65     super();
66   }
67
68   /*
69    * (non-Javadoc)
70    * 
71    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
72    */
73   @Override
74   public String getAccessionSeparator()
75   {
76     return null;
77   }
78
79   /*
80    * (non-Javadoc)
81    * 
82    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
83    */
84   @Override
85   public Regex getAccessionValidator()
86   {
87     Regex validator =  new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
88     validator.setIgnoreCase(true);
89     return validator;
90   }
91
92   /*
93    * (non-Javadoc)
94    * 
95    * @see jalview.ws.DbSourceProxy#getDbSource()
96    */
97   @Override
98   public String getDbSource()
99   {
100     return "ALPHAFOLD";
101   }
102
103   /*
104    * (non-Javadoc)
105    * 
106    * @see jalview.ws.DbSourceProxy#getDbVersion()
107    */
108   @Override
109   public String getDbVersion()
110   {
111     return "1";
112   }
113
114   public static String getAlphaFoldCifDownloadUrl(String id)
115   {
116     return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif";
117   }
118
119   /*
120    * (non-Javadoc)
121    * 
122    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
123    */
124   @Override
125   public AlignmentI getSequenceRecords(String queries) throws Exception
126   {
127     AlignmentI pdbAlignment = null;
128     String chain = null;
129     String id = null;
130     if (queries.indexOf(COLON) > -1)
131     {
132       chain = queries.substring(queries.indexOf(COLON) + 1);
133       id = queries.substring(0, queries.indexOf(COLON));
134     }
135     else
136     {
137       id = queries;
138     }
139
140     if (!isValidReference(id))
141     {
142       System.err.println(
143               "(AFClient) Ignoring invalid pdb query: '" + id + "'");
144       stopQuery();
145       return null;
146     }
147     String alphaFoldCif = getAlphaFoldCifDownloadUrl(id);
148
149     try
150     {
151       File tmpFile = File.createTempFile(id, ".cif");
152       UrlDownloadClient.download(alphaFoldCif, tmpFile);
153       
154       // may not need this check ?
155       file = tmpFile.getAbsolutePath();
156       if (file == null)
157       {
158         return null;
159       }
160
161       pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, id, chain, getDbSource(),getDbVersion());
162       
163
164       if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
165       {
166         throw new Exception(MessageManager.formatMessage(
167                 "exception.no_pdb_records_for_chain", new String[]
168                 { id, ((chain == null) ? "' '" : chain) }));
169       }
170
171     } catch (Exception ex) // Problem parsing PDB file
172     {
173       stopQuery();
174       throw (ex);
175     }
176     return pdbAlignment;
177   }
178
179   /**
180    * general purpose structure importer - designed to yield alignment useful for transfer of annotation to associated sequences
181    * @param alphaFoldCif
182    * @param tmpFile
183    * @param id
184    * @param chain
185    * @param dbSource
186    * @param dbVersion
187    * @return
188    * @throws Exception
189    */
190   public static AlignmentI importDownloadedStructureFromUrl(String alphaFoldCif,
191           File tmpFile, String id, String chain, String dbSource, String dbVersion) throws Exception
192   {
193     String file = tmpFile.getAbsolutePath();
194     // todo get rid of Type and use FileFormatI instead?
195     FileFormatI fileFormat = FileFormat.MMCif;
196     AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile,
197             DataSourceType.FILE, fileFormat);
198     if (pdbAlignment != null)
199     {
200       List<SequenceI> toremove = new ArrayList<SequenceI>();
201       for (SequenceI pdbcs : pdbAlignment.getSequences())
202       {
203         String chid = null;
204         // Mapping map=null;
205         for (PDBEntry pid : pdbcs.getAllPDBEntries())
206         {
207           if (pid.getFile() == file)
208           {
209             chid = pid.getChainCode();
210
211           }
212         }
213         if (chain == null || (chid != null && (chid.equals(chain)
214                 || chid.trim().equals(chain.trim())
215                 || (chain.trim().length() == 0 && chid.equals("_")))))
216         {
217           // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant.
218           // TODO: suggest simplify naming to 1qip|A as default name defined
219           pdbcs.setName(id + SEPARATOR + pdbcs.getName());
220           // Might need to add more metadata to the PDBEntry object
221           // like below
222           /*
223            * PDBEntry entry = new PDBEntry(); // Construct the PDBEntry
224            * entry.setId(id); if (entry.getProperty() == null)
225            * entry.setProperty(new Hashtable());
226            * entry.getProperty().put("chains", pdbchain.id + "=" +
227            * sq.getStart() + "-" + sq.getEnd());
228            * sq.getDatasetSequence().addPDBId(entry);
229            */
230           // Add PDB DB Refs
231           // We make a DBRefEtntry because we have obtained the PDB file from
232           // a
233           // verifiable source
234           // JBPNote - PDB DBRefEntry should also carry the chain and mapping
235           // information
236           if (dbSource != null)
237           {
238             DBRefEntry dbentry = new DBRefEntry(dbSource,
239
240                     dbVersion, (chid == null ? id : id + chid));
241             // dbentry.setMap()
242             pdbcs.addDBRef(dbentry);
243             // update any feature groups
244             List<SequenceFeature> allsf = pdbcs.getFeatures().getAllFeatures();
245             List<SequenceFeature> newsf = new ArrayList<SequenceFeature>();
246             if (allsf!=null && allsf.size()>0)
247             {
248               for (SequenceFeature f:allsf)
249               {
250                 if (file.equals(f.getFeatureGroup()))
251                 {
252                   f = new SequenceFeature(f, f.type, f.begin, f.end, id, f.score);
253                 }
254                 newsf.add(f);
255               }
256               pdbcs.setSequenceFeatures(newsf);
257             }
258           }
259         }
260         else
261         {
262           // mark this sequence to be removed from the alignment
263           // - since it's not from the right chain
264           toremove.add(pdbcs);
265         }
266       }
267       // now remove marked sequences
268       for (SequenceI pdbcs : toremove)
269       {
270         pdbAlignment.deleteSequence(pdbcs);
271         if (pdbcs.getAnnotation() != null)
272         {
273           for (AlignmentAnnotation aa : pdbcs.getAnnotation())
274           {
275             pdbAlignment.deleteAnnotation(aa);
276           }
277         }
278       }
279     }
280     return pdbAlignment;
281   }
282
283   /*
284    * (non-Javadoc)
285    * 
286    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
287    */
288   @Override
289   public boolean isValidReference(String accession)
290   {
291     Regex r = getAccessionValidator();
292     return r.search(accession.trim());
293   }
294
295   /**
296    * human glyoxalase
297    */
298   @Override
299   public String getTestQuery()
300   {
301     return "1QIP";
302   }
303
304   @Override
305   public String getDbName()
306   {
307     return "PDB"; // getDbSource();
308   }
309
310   @Override
311   public int getTier()
312   {
313     return 0;
314   }
315
316   /**
317    * Returns a descriptor for suitable feature display settings with
318    * <ul>
319    * <li>ResNums or insertions features visible</li>
320    * <li>insertions features coloured red</li>
321    * <li>ResNum features coloured by label</li>
322    * <li>Insertions displayed above (on top of) ResNums</li>
323    * </ul>
324    */
325   @Override
326   public FeatureSettingsModelI getFeatureColourScheme()
327   {
328     return new PDBFeatureSettings();
329   }
330 }