JAL-2282 Refactor SequenceAnnotationReport to use refactored UrlLink
[jalview.git] / src / jalview / io / SequenceAnnotationReport.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 jalview.datamodel.DBRefEntry;
24 import jalview.datamodel.SequenceFeature;
25 import jalview.datamodel.SequenceI;
26 import jalview.io.gff.GffConstants;
27 import jalview.util.UrlLink;
28
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 /**
36  * generate HTML reports for a sequence
37  * 
38  * @author jimp
39  */
40 public class SequenceAnnotationReport
41 {
42   final String linkImageURL;
43
44   public SequenceAnnotationReport(String linkImageURL)
45   {
46     this.linkImageURL = linkImageURL;
47   }
48
49   /**
50    * Append text for the list of features to the tooltip
51    * 
52    * @param tooltipText2
53    * @param rpos
54    * @param features
55    * @param minmax
56    */
57   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
58           List<SequenceFeature> features, Map<String, float[][]> minmax)
59   {
60     if (features != null)
61     {
62       for (SequenceFeature feature : features)
63       {
64         appendFeature(tooltipText2, rpos, minmax, feature);
65       }
66     }
67   }
68
69   /**
70    * Appends text for one sequence feature to the string buffer
71    * 
72    * @param sb
73    * @param rpos
74    * @param minmax
75    *          {{min, max}, {min, max}} positional and non-positional feature
76    *          scores for this type
77    * @param feature
78    */
79   void appendFeature(final StringBuffer sb, int rpos,
80           Map<String, float[][]> minmax, SequenceFeature feature)
81   {
82     if ("disulfide bond".equals(feature.getType()))
83     {
84       if (feature.getBegin() == rpos || feature.getEnd() == rpos)
85       {
86         if (sb.length() > 6)
87         {
88           sb.append("<br>");
89         }
90         sb.append("disulfide bond ").append(feature.getBegin()).append(":")
91                 .append(feature.getEnd());
92       }
93     }
94     else
95     {
96       if (sb.length() > 6)
97       {
98         sb.append("<br>");
99       }
100       // TODO: remove this hack to display link only features
101       boolean linkOnly = feature.getValue("linkonly") != null;
102       if (!linkOnly)
103       {
104         sb.append(feature.getType()).append(" ");
105         if (rpos != 0)
106         {
107           // we are marking a positional feature
108           sb.append(feature.begin);
109         }
110         if (feature.begin != feature.end)
111         {
112           sb.append(" " + feature.end);
113         }
114
115         if (feature.getDescription() != null
116                 && !feature.description.equals(feature.getType()))
117         {
118           String tmpString = feature.getDescription();
119           String tmp2up = tmpString.toUpperCase();
120           final int startTag = tmp2up.indexOf("<HTML>");
121           if (startTag > -1)
122           {
123             tmpString = tmpString.substring(startTag + 6);
124             tmp2up = tmp2up.substring(startTag + 6);
125           }
126           // TODO strips off </body> but not <body> - is that intended?
127           int endTag = tmp2up.indexOf("</BODY>");
128           if (endTag > -1)
129           {
130             tmpString = tmpString.substring(0, endTag);
131             tmp2up = tmp2up.substring(0, endTag);
132           }
133           endTag = tmp2up.indexOf("</HTML>");
134           if (endTag > -1)
135           {
136             tmpString = tmpString.substring(0, endTag);
137           }
138
139           if (startTag > -1)
140           {
141             sb.append("; ").append(tmpString);
142           }
143           else
144           {
145             if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1)
146             {
147               // The description does not specify html is to
148               // be used, so we must remove < > symbols
149               tmpString = tmpString.replaceAll("<", "&lt;");
150               tmpString = tmpString.replaceAll(">", "&gt;");
151               sb.append("; ").append(tmpString);
152             }
153             else
154             {
155               sb.append("; ").append(tmpString);
156             }
157           }
158         }
159
160         /*
161          * score should be shown if there is one, and min != max
162          * for this feature type (e.g. not all 0)
163          */
164         if (!Float.isNaN(feature.getScore()))
165         {
166           float[][] rng = (minmax == null) ? null : minmax.get(feature
167                   .getType());
168           if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
169           {
170             sb.append(" Score=").append(String.valueOf(feature.getScore()));
171           }
172         }
173         String status = (String) feature.getValue("status");
174         if (status != null && status.length() > 0)
175         {
176           sb.append("; (").append(status).append(")");
177         }
178         String clinSig = (String) feature
179                 .getValue(GffConstants.CLINICAL_SIGNIFICANCE);
180         if (clinSig != null)
181         {
182           sb.append("; ").append(clinSig);
183         }
184       }
185     }
186     appendLinks(sb, feature);
187   }
188
189   /**
190    * Format and appends any hyperlinks for the sequence feature to the string
191    * buffer
192    * 
193    * @param sb
194    * @param feature
195    */
196   void appendLinks(final StringBuffer sb, SequenceFeature feature)
197   {
198     if (feature.links != null)
199     {
200       if (linkImageURL != null)
201       {
202         sb.append(" <img src=\"" + linkImageURL + "\">");
203       }
204       else
205       {
206         for (String urlstring : feature.links)
207         {
208           try
209           {
210             for (List<String> urllink : createLinksFrom(null, urlstring))
211             {
212               sb.append("<br/> <a href=\""
213                       + urllink.get(3)
214                       + "\" target=\""
215                       + urllink.get(0)
216                       + "\">"
217                       + (urllink.get(0).toLowerCase()
218                               .equals(urllink.get(1).toLowerCase()) ? urllink
219                               .get(0) : (urllink.get(0) + ":" + urllink
220                               .get(1)))
221                       + "</a></br>");
222             }
223           } catch (Exception x)
224           {
225             System.err.println("problem when creating links from "
226                     + urlstring);
227             x.printStackTrace();
228           }
229         }
230       }
231
232     }
233   }
234
235   /**
236    * 
237    * @param seq
238    * @param link
239    * @return Collection< List<String> > { List<String> { link target, link
240    *         label, dynamic component inserted (if any), url }}
241    */
242   Collection<List<String>> createLinksFrom(SequenceI seq, String link)
243   {
244     Map<String, List<String>> urlSets = new LinkedHashMap<String, List<String>>();
245     UrlLink urlLink = new UrlLink(link);
246     if (!urlLink.isValid())
247     {
248       System.err.println(urlLink.getInvalidMessage());
249       return null;
250     }
251
252     urlLink.createLinksFromSeq(seq, urlSets);
253
254     return urlSets.values();
255   }
256
257   public void createSequenceAnnotationReport(final StringBuffer tip,
258           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
259           Map<String, float[][]> minmax)
260   {
261     createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats,
262             true, minmax);
263   }
264
265   public void createSequenceAnnotationReport(final StringBuffer tip,
266           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
267           boolean tableWrap, Map<String, float[][]> minmax)
268   {
269     String tmp;
270     tip.append("<i>");
271
272     int maxWidth = 0;
273     if (sequence.getDescription() != null)
274     {
275       tmp = sequence.getDescription();
276       tip.append("<br>" + tmp);
277       maxWidth = Math.max(maxWidth, tmp.length());
278     }
279     SequenceI ds = sequence;
280     while (ds.getDatasetSequence() != null)
281     {
282       ds = ds.getDatasetSequence();
283     }
284     DBRefEntry[] dbrefs = ds.getDBRefs();
285     if (showDbRefs && dbrefs != null)
286     {
287       for (int i = 0; i < dbrefs.length; i++)
288       {
289         tip.append("<br>");
290         tmp = dbrefs[i].getSource() + " " + dbrefs[i].getAccessionId();
291         tip.append(tmp);
292         maxWidth = Math.max(maxWidth, tmp.length());
293       }
294     }
295
296     // ADD NON POSITIONAL SEQUENCE INFO
297     SequenceFeature[] features = sequence.getSequenceFeatures();
298     if (showNpFeats && features != null)
299     {
300       for (int i = 0; i < features.length; i++)
301       {
302         if (features[i].begin == 0 && features[i].end == 0)
303         {
304           int sz = -tip.length();
305           List<SequenceFeature> tfeat = new ArrayList<SequenceFeature>();
306           tfeat.add(features[i]);
307           appendFeatures(tip, 0, tfeat, minmax);
308           sz += tip.length();
309           maxWidth = Math.max(maxWidth, sz);
310         }
311       }
312     }
313
314     if (tableWrap && maxWidth > 60)
315     {
316       tip.insert(0, "<table width=350 border=0><tr><td><i>");
317       tip.append("</i></td></tr></table>");
318     }
319
320   }
321 }