JAL-1620 version bump and release notes
[jalview.git] / src / jalview / io / SequenceAnnotationReport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 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.util.ArrayList;
24 import java.util.Hashtable;
25 import java.util.Vector;
26
27 import jalview.datamodel.DBRefEntry;
28 import jalview.datamodel.SequenceFeature;
29 import jalview.datamodel.SequenceI;
30 import jalview.util.UrlLink;
31
32 /**
33  * generate HTML reports for a sequence
34  * 
35  * @author jimp
36  */
37 public class SequenceAnnotationReport
38 {
39   final String linkImageURL;
40
41   public SequenceAnnotationReport(String linkImageURL)
42   {
43     this.linkImageURL = linkImageURL;
44   }
45
46   /**
47    * appends the features at rpos to the given stringbuffer ready for display in
48    * a tooltip
49    * 
50    * @param tooltipText2
51    * @param linkImageURL
52    * @param rpos
53    * @param features
54    *          TODO refactor to Jalview 'utilities' somehow.
55    */
56   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
57           SequenceFeature[] features)
58   {
59     appendFeatures(tooltipText2, rpos, features, null);
60   }
61
62   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
63           SequenceFeature[] features, Hashtable minmax)
64   {
65     String tmpString;
66     if (features != null)
67     {
68       for (int i = 0; i < features.length; i++)
69       {
70         if (features[i].getType().equals("disulfide bond"))
71         {
72           if (features[i].getBegin() == rpos
73                   || features[i].getEnd() == rpos)
74           {
75             if (tooltipText2.length() > 6)
76             {
77               tooltipText2.append("<br>");
78             }
79             tooltipText2.append("disulfide bond " + features[i].getBegin()
80                     + ":" + features[i].getEnd());
81           }
82         }
83         else
84         {
85           if (tooltipText2.length() > 6)
86           {
87             tooltipText2.append("<br>");
88           }
89           // TODO: remove this hack to display link only features
90           boolean linkOnly = features[i].getValue("linkonly") != null;
91           if (!linkOnly)
92           {
93             tooltipText2.append(features[i].getType() + " ");
94             if (rpos != 0)
95             {
96               // we are marking a positional feature
97               tooltipText2.append(features[i].begin);
98             }
99             if (features[i].begin != features[i].end)
100             {
101               tooltipText2.append(" " + features[i].end);
102             }
103
104             if (features[i].getDescription() != null
105                     && !features[i].description.equals(features[i]
106                             .getType()))
107             {
108               tmpString = features[i].getDescription();
109               String tmp2up = tmpString.toUpperCase();
110               int startTag = tmp2up.indexOf("<HTML>");
111               if (startTag > -1)
112               {
113                 tmpString = tmpString.substring(startTag + 6);
114                 tmp2up = tmp2up.substring(startTag + 6);
115               }
116               int endTag = tmp2up.indexOf("</BODY>");
117               if (endTag > -1)
118               {
119                 tmpString = tmpString.substring(0, endTag);
120                 tmp2up = tmp2up.substring(0, endTag);
121               }
122               endTag = tmp2up.indexOf("</HTML>");
123               if (endTag > -1)
124               {
125                 tmpString = tmpString.substring(0, endTag);
126               }
127
128               if (startTag > -1)
129               {
130                 tooltipText2.append("; " + tmpString);
131               }
132               else
133               {
134                 if (tmpString.indexOf("<") > -1
135                         || tmpString.indexOf(">") > -1)
136                 {
137                   // The description does not specify html is to
138                   // be used, so we must remove < > symbols
139                   tmpString = tmpString.replaceAll("<", "&lt;");
140                   tmpString = tmpString.replaceAll(">", "&gt;");
141
142                   tooltipText2.append("; ");
143                   tooltipText2.append(tmpString);
144
145                 }
146                 else
147                 {
148                   tooltipText2.append("; " + tmpString);
149                 }
150               }
151             }
152             // check score should be shown
153             if (features[i].getScore() != Float.NaN)
154             {
155               float[][] rng = (minmax == null) ? null : ((float[][]) minmax
156                       .get(features[i].getType()));
157               if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
158               {
159                 tooltipText2.append(" Score=" + features[i].getScore());
160               }
161             }
162             if (features[i].getValue("status") != null)
163             {
164               String status = features[i].getValue("status").toString();
165               if (status.length() > 0)
166               {
167                 tooltipText2.append("; (" + features[i].getValue("status")
168                         + ")");
169               }
170             }
171           }
172         }
173         if (features[i].links != null)
174         {
175           if (linkImageURL != null)
176           {
177             tooltipText2.append(" <img src=\"" + linkImageURL + "\">");
178           }
179           else
180           {
181             for (String urlstring : (Vector<String>) features[i].links)
182             {
183               try
184               {
185                 for (String[] urllink : createLinksFrom(null, urlstring))
186                 {
187                   tooltipText2.append("<br/> <a href=\""
188                           + urllink[3]
189                           + "\" target=\""
190                           + urllink[0]
191                           + "\">"
192                           + (urllink[0].toLowerCase().equals(
193                                   urllink[1].toLowerCase()) ? urllink[0]
194                                   : (urllink[0] + ":" + urllink[1]))
195                           + "</a></br>");
196                 }
197               } catch (Exception x)
198               {
199                 System.err.println("problem when creating links from "
200                         + urlstring);
201                 x.printStackTrace();
202               }
203             }
204           }
205
206         }
207       }
208     }
209   }
210
211   /**
212    * 
213    * @param seq
214    * @param link
215    * @return String[][] { String[] { link target, link label, dynamic component
216    *         inserted (if any), url }}
217    */
218   public String[][] createLinksFrom(SequenceI seq, String link)
219   {
220     ArrayList<String[]> urlSets = new ArrayList<String[]>();
221     ArrayList<String> uniques = new ArrayList<String>();
222     UrlLink urlLink = new UrlLink(link);
223     if (!urlLink.isValid())
224     {
225       System.err.println(urlLink.getInvalidMessage());
226       return null;
227     }
228     final String target = urlLink.getTarget(); // link.substring(0,
229     // link.indexOf("|"));
230     final String label = urlLink.getLabel();
231     if (seq != null && urlLink.isDynamic())
232     {
233
234       // collect matching db-refs
235       DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRef(),
236               new String[]
237               { target });
238       // collect id string too
239       String id = seq.getName();
240       String descr = seq.getDescription();
241       if (descr != null && descr.length() < 1)
242       {
243         descr = null;
244       }
245       if (dbr != null)
246       {
247         for (int r = 0; r < dbr.length; r++)
248         {
249           if (id != null && dbr[r].getAccessionId().equals(id))
250           {
251             // suppress duplicate link creation for the bare sequence ID
252             // string with this link
253             id = null;
254           }
255           // create Bare ID link for this RUL
256           String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
257           if (urls != null)
258           {
259             for (int u = 0; u < urls.length; u += 2)
260             {
261               String unq = urls[u] + "|" + urls[u + 1];
262               if (!uniques.contains(unq))
263               {
264                 urlSets.add(new String[]
265                 { target, label, urls[u], urls[u + 1] });
266                 uniques.add(unq);
267               }
268             }
269           }
270         }
271       }
272       if (id != null)
273       {
274         // create Bare ID link for this RUL
275         String[] urls = urlLink.makeUrls(id, true);
276         if (urls != null)
277         {
278           for (int u = 0; u < urls.length; u += 2)
279           {
280             String unq = urls[u] + "|" + urls[u + 1];
281             if (!uniques.contains(unq))
282             {
283               urlSets.add(new String[]
284               { target, label, urls[u], urls[u + 1] });
285               uniques.add(unq);
286             }
287           }
288         }
289       }
290       if (descr != null && urlLink.getRegexReplace() != null)
291       {
292         // create link for this URL from description only if regex matches
293         String[] urls = urlLink.makeUrls(descr, true);
294         if (urls != null)
295         {
296           for (int u = 0; u < urls.length; u += 2)
297           {
298             String unq = urls[u] + "|" + urls[u + 1];
299             if (!uniques.contains(unq))
300             {
301               urlSets.add(new String[]
302               { target, label, urls[u], urls[u + 1] });
303               uniques.add(unq);
304             }
305           }
306         }
307       }
308
309     }
310     else
311     {
312       String unq = label + "|" + urlLink.getUrl_prefix();
313       if (!uniques.contains(unq))
314       {
315         uniques.add(unq);
316         // Add a non-dynamic link
317         urlSets.add(new String[]
318         { target, label, null, urlLink.getUrl_prefix() });
319       }
320     }
321
322     return urlSets.toArray(new String[][]
323     {});
324   }
325
326   public void createSequenceAnnotationReport(final StringBuffer tip,
327           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
328           Hashtable minmax)
329   {
330     createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats,
331             true, minmax);
332   }
333
334   public void createSequenceAnnotationReport(final StringBuffer tip,
335           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
336           boolean tableWrap, Hashtable minmax)
337   {
338     String tmp;
339     tip.append("<i>");
340
341     int maxWidth = 0;
342     if (sequence.getDescription() != null)
343     {
344       tmp = sequence.getDescription();
345       tip.append("<br>" + tmp);
346       maxWidth = Math.max(maxWidth, tmp.length());
347     }
348     SequenceI ds = sequence;
349     while (ds.getDatasetSequence() != null)
350     {
351       ds = ds.getDatasetSequence();
352     }
353     DBRefEntry[] dbrefs = ds.getDBRef();
354     if (showDbRefs && dbrefs != null)
355     {
356       for (int i = 0; i < dbrefs.length; i++)
357       {
358         tip.append("<br>");
359         tmp = dbrefs[i].getSource() + " " + dbrefs[i].getAccessionId();
360         tip.append(tmp);
361         maxWidth = Math.max(maxWidth, tmp.length());
362       }
363     }
364
365     // ADD NON POSITIONAL SEQUENCE INFO
366     SequenceFeature[] features = ds.getSequenceFeatures();
367     SequenceFeature[] tfeat = new SequenceFeature[1];
368     if (showNpFeats && features != null)
369     {
370       for (int i = 0; i < features.length; i++)
371       {
372         if (features[i].begin == 0 && features[i].end == 0)
373         {
374           int sz = -tip.length();
375           tfeat[0] = features[i];
376           appendFeatures(tip, 0, tfeat, minmax);
377           sz += tip.length();
378           maxWidth = Math.max(maxWidth, sz);
379         }
380       }
381     }
382
383     if (tableWrap && maxWidth > 60)
384     {
385       tip.insert(0, "<table width=350 border=0><tr><td><i>");
386       tip.append("</i></td></tr></table>");
387     }
388
389   }
390 }