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