2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
23 import jalview.datamodel.DBRefEntry;
24 import jalview.datamodel.DBRefSource;
25 import jalview.datamodel.DynamicData;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.SequenceFeature;
28 import jalview.datamodel.SequenceI;
29 import jalview.io.gff.GffConstants;
30 import jalview.util.MessageManager;
31 import jalview.util.UrlLink;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.Comparator;
36 import java.util.LinkedHashMap;
37 import java.util.List;
41 * generate HTML reports for a sequence
45 public class SequenceAnnotationReport
47 private static final String COMMA = ",";
49 private static final String ELLIPSIS = "...";
51 private static final int MAX_REFS_PER_SOURCE = 4;
53 private static final int MAX_SOURCES = 40;
55 private static final String[][] PRIMARY_SOURCES = new String[][] {
56 DBRefSource.CODINGDBS, DBRefSource.DNACODINGDBS,
57 DBRefSource.PROTEINDBS };
59 final String linkImageURL;
62 * Comparator to order DBRefEntry by Source + accession id (case-insensitive)
64 private static Comparator<DBRefEntry> comparator = new Comparator<DBRefEntry>()
68 public int compare(DBRefEntry ref1, DBRefEntry ref2)
70 String s1 = ref1.getSource();
71 String s2 = ref2.getSource();
72 boolean s1Primary = isPrimarySource(s1);
73 boolean s2Primary = isPrimarySource(s2);
74 if (s1Primary && !s2Primary)
78 if (!s1Primary && s2Primary)
82 int comp = s1 == null ? -1 : (s2 == null ? 1 : s1
83 .compareToIgnoreCase(s2));
86 String a1 = ref1.getAccessionId();
87 String a2 = ref2.getAccessionId();
88 comp = a1 == null ? -1 : (a2 == null ? 1 : a1
89 .compareToIgnoreCase(a2));
94 private boolean isPrimarySource(String source)
96 for (String[] primary : PRIMARY_SOURCES)
98 for (String s : primary)
100 if (source.equals(s))
110 public SequenceAnnotationReport(String linkImageURL)
112 this.linkImageURL = linkImageURL;
116 * Append text for the list of features to the tooltip
123 public void appendFeatures(final StringBuilder sb, int rpos,
124 List<SequenceFeature> features, Map<String, float[][]> minmax)
126 if (features != null)
128 for (SequenceFeature feature : features)
130 appendFeature(sb, rpos, minmax, feature);
136 * Appends the feature at rpos to the given buffer
143 void appendFeature(final StringBuilder sb, int rpos,
144 Map<String, float[][]> minmax, SequenceFeature feature)
146 if (feature.isContactFeature())
148 if (feature.getBegin() == rpos || feature.getEnd() == rpos)
154 sb.append(feature.getType()).append(" ").append(feature.getBegin())
156 .append(feature.getEnd());
165 // TODO: remove this hack to display link only features
166 boolean linkOnly = feature.getValue("linkonly") != null;
169 sb.append(feature.getType()).append(" ");
172 // we are marking a positional feature
173 sb.append(feature.begin);
175 if (feature.begin != feature.end)
177 sb.append(" ").append(feature.end);
180 if (feature.getDescription() != null
181 && !feature.description.equals(feature.getType()))
183 String tmpString = feature.getDescription();
184 String tmp2up = tmpString.toUpperCase();
185 int startTag = tmp2up.indexOf("<HTML>");
188 tmpString = tmpString.substring(startTag + 6);
189 tmp2up = tmp2up.substring(startTag + 6);
191 int endTag = tmp2up.indexOf("</BODY>");
194 tmpString = tmpString.substring(0, endTag);
195 tmp2up = tmp2up.substring(0, endTag);
197 endTag = tmp2up.indexOf("</HTML>");
200 tmpString = tmpString.substring(0, endTag);
205 sb.append("; ").append(tmpString);
209 if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1)
211 // The description does not specify html is to
212 // be used, so we must remove < > symbols
213 tmpString = tmpString.replaceAll("<", "<");
214 tmpString = tmpString.replaceAll(">", ">");
217 sb.append(tmpString);
221 sb.append("; ").append(tmpString);
225 // check score should be shown
226 if (!Float.isNaN(feature.getScore()))
228 float[][] rng = (minmax == null) ? null : minmax.get(feature
230 if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
232 sb.append(" Score=").append(String.valueOf(feature.getScore()));
235 String status = (String) feature.getValue("status");
236 if (status != null && status.length() > 0)
238 sb.append("; (").append(status).append(")");
240 String clinSig = (String) feature
241 .getValue(GffConstants.CLINICAL_SIGNIFICANCE);
244 sb.append("; ").append(clinSig);
251 * Format and appends any hyperlinks for the sequence feature to the string
257 void appendLinks(final StringBuffer sb, SequenceFeature feature)
259 if (feature.links != null)
261 if (linkImageURL != null)
263 sb.append(" <img src=\"" + linkImageURL + "\">");
267 for (String urlstring : feature.links)
271 for (List<String> urllink : createLinksFrom(null, urlstring))
273 sb.append("<br/> <a href=\""
278 + (urllink.get(0).toLowerCase()
279 .equals(urllink.get(1).toLowerCase()) ? urllink
280 .get(0) : (urllink.get(0) + ":" + urllink
284 } catch (Exception x)
286 System.err.println("problem when creating links from "
300 * @return Collection< List<String> > { List<String> { link target, link
301 * label, dynamic component inserted (if any), url }}
303 Collection<List<String>> createLinksFrom(SequenceI seq, String link)
305 Map<String, List<String>> urlSets = new LinkedHashMap<String, List<String>>();
306 UrlLink urlLink = new UrlLink(link);
307 if (!urlLink.isValid())
309 System.err.println(urlLink.getInvalidMessage());
313 urlLink.createLinksFromSeq(seq, urlSets);
315 return urlSets.values();
318 public void createSequenceAnnotationReport(final StringBuilder tip,
319 SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
320 Map<String, float[][]> minmax)
322 createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats,
327 * Builds an html formatted report of sequence details and appends it to the
331 * buffer to append report to
333 * the sequence the report is for
335 * whether to include database references for the sequence
337 * whether to include non-positional sequence features
342 int createSequenceAnnotationReport(final StringBuilder sb,
343 SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
344 Map<String, float[][]> minmax, boolean summary)
350 if (sequence.getDescription() != null)
352 tmp = sequence.getDescription();
353 sb.append("<br>").append(tmp);
354 maxWidth = Math.max(maxWidth, tmp.length());
356 SequenceI ds = sequence;
357 while (ds.getDatasetSequence() != null)
359 ds = ds.getDatasetSequence();
361 DBRefEntry[] dbrefs = ds.getDBRefs();
362 if (showDbRefs && dbrefs != null)
364 // note this sorts the refs held on the sequence!
365 Arrays.sort(dbrefs, comparator);
366 boolean ellipsis = false;
367 String source = null;
368 String lastSource = null;
369 int countForSource = 0;
371 boolean moreSources = false;
374 for (DBRefEntry ref : dbrefs)
376 source = ref.getSource();
382 boolean sourceChanged = !source.equals(lastSource);
389 if (sourceCount > MAX_SOURCES && summary)
397 if (countForSource == 1 || !summary)
401 if (countForSource <= MAX_REFS_PER_SOURCE || !summary)
403 String accessionId = ref.getAccessionId();
404 lineLength += accessionId.length() + 1;
405 if (countForSource > 1 && summary)
407 sb.append(", ").append(accessionId);
412 sb.append(source).append(" ").append(accessionId);
413 lineLength += source.length();
415 maxWidth = Math.max(maxWidth, lineLength);
417 if (countForSource == MAX_REFS_PER_SOURCE && summary)
419 sb.append(COMMA).append(ELLIPSIS);
425 sb.append("<br>").append(ELLIPSIS).append(COMMA).append(source)
426 .append(COMMA).append(ELLIPSIS);
431 sb.append(MessageManager.getString("label.output_seq_details"));
437 * add non-positional features if wanted
439 SequenceFeature[] features = sequence.getSequenceFeatures();
440 if (showNpFeats && features != null)
442 for (int i = 0; i < features.length; i++)
444 if (features[i].begin == 0 && features[i].end == 0)
446 int sz = -sb.length();
447 appendFeature(sb, 0, minmax, features[i]);
449 maxWidth = Math.max(maxWidth, sz);
454 List<PDBEntry> pdbEntries = ds.getAllPDBEntries();
455 sb.append(getToolTipTextFromPDBEntries(pdbEntries));
459 private String getToolTipTextFromPDBEntries(List<PDBEntry> pdbEntries)
462 if (pdbEntries.isEmpty())
466 if (pdbEntries.size() > 1)
469 PDBEntry bestRanked = null;
470 for (PDBEntry pdb : pdbEntries)
472 if (pdb.getProperty("DYNAMIC_DATA_PHYRE2") != null)
476 // best ranked entry must be from a Phyre
477 if (x > 0 && bestRanked == null)
482 tooltip = (x > 0) ? "<table border=\"1\" width=100%><tr><td>Contains <b>"
484 + "</b> Phyre2 model structure(s)</td></tr>"
485 + "<tr><td>Best ranked Phyre2 model is <b>"
486 + bestRanked.getId() + "</b></td></tr></table>"
491 PDBEntry pdb = pdbEntries.iterator().next();
492 if (pdb.getProperty("DYNAMIC_DATA_PHYRE2") != null)
494 tooltip = getPhyreToolTipFromDynamicData((List<DynamicData>) pdb
495 .getProperty("DYNAMIC_DATA_PHYRE2"));
501 private String getPhyreToolTipFromDynamicData(
502 List<DynamicData> dynamicDataList)
504 StringBuilder phyre2InfoBuilder = new StringBuilder();
506 .append("<html><table border=\"1\" width=100%>")
507 .append("<tr><td colspan=\"2\"><strong>Phyre2 Template Info</strong></td></tr>");
508 for (DynamicData data : dynamicDataList)
510 if (data.isDisplay())
512 phyre2InfoBuilder.append("<tr><td>").append(data.getFieldTitle())
513 .append("</td><td>").append(data.getFieldValue())
514 .append("</td></tr>");
517 phyre2InfoBuilder.append("</table></html>");
518 return phyre2InfoBuilder.toString();
521 public void createTooltipAnnotationReport(final StringBuilder tip,
522 SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
523 Map<String, float[][]> minmax)
525 int maxWidth = createSequenceAnnotationReport(tip, sequence,
526 showDbRefs, showNpFeats, minmax, true);
530 // ? not sure this serves any useful purpose
531 // tip.insert(0, "<table width=350 border=0><tr><td>");
532 // tip.append("</td></tr></table>");