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