X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FSequenceAnnotationReport.java;h=4933cd6ca9295d404b37cb83ed1e0976b85ac719;hb=refs%2Fheads%2FJAL-3253-applet-SwingJS-omnibus;hp=c3b076cb20ffe774079f5e8b24e154d75b88bbea;hpb=136c0793b90b72b928c4d77dc109dd5c644e00d3;p=jalview.git diff --git a/src/jalview/io/SequenceAnnotationReport.java b/src/jalview/io/SequenceAnnotationReport.java index c3b076c..4933cd6 100644 --- a/src/jalview/io/SequenceAnnotationReport.java +++ b/src/jalview/io/SequenceAnnotationReport.java @@ -20,13 +20,17 @@ */ package jalview.io; +import jalview.api.FeatureColourI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; +import jalview.datamodel.GeneLociI; +import jalview.datamodel.MappedFeatures; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; -import jalview.io.gff.GffConstants; import jalview.util.MessageManager; +import jalview.util.StringUtils; import jalview.util.UrlLink; +import jalview.viewmodel.seqfeatures.FeatureRendererModel; import java.util.Arrays; import java.util.Collection; @@ -42,6 +46,8 @@ import java.util.Map; */ public class SequenceAnnotationReport { + private static final int MAX_DESCRIPTION_LENGTH = 40; + private static final String COMMA = ","; private static final String ELLIPSIS = "..."; @@ -50,15 +56,13 @@ public class SequenceAnnotationReport private static final int MAX_SOURCES = 40; - private static final String[][] PRIMARY_SOURCES = new String[][] { - DBRefSource.CODINGDBS, DBRefSource.DNACODINGDBS, - DBRefSource.PROTEINDBS }; + // public static final String[][] PRIMARY_SOURCES moved to DBRefSource.java final String linkImageURL; /* * Comparator to order DBRefEntry by Source + accession id (case-insensitive), - * with 'Primary' sources placed before others + * with 'Primary' sources placed before others, and 'chromosome' first of all */ private static Comparator comparator = new Comparator() { @@ -66,10 +70,18 @@ public class SequenceAnnotationReport @Override public int compare(DBRefEntry ref1, DBRefEntry ref2) { + if (ref1 instanceof GeneLociI) + { + return -1; + } + if (ref2 instanceof GeneLociI) + { + return 1; + } String s1 = ref1.getSource(); String s2 = ref2.getSource(); - boolean s1Primary = isPrimarySource(s1); - boolean s2Primary = isPrimarySource(s2); + boolean s1Primary = DBRefSource.isPrimarySource(s1); + boolean s2Primary = DBRefSource.isPrimarySource(s2); if (s1Primary && !s2Primary) { return -1; @@ -90,45 +102,87 @@ public class SequenceAnnotationReport return comp; } - private boolean isPrimarySource(String source) +// private boolean isPrimarySource(String source) +// { +// for (String[] primary : DBRefSource.PRIMARY_SOURCES) +// { +// for (String s : primary) +// { +// if (source.equals(s)) +// { +// return true; +// } +// } +// } +// return false; +// } + }; + + public SequenceAnnotationReport(String linkURL) + { + this.linkImageURL = linkURL; + } + + /** + * Append text for the list of features to the tooltip Returns number of + * features left if maxlength limit is (or would have been) reached + * + * @param sb + * @param residuePos + * @param features + * @param minmax + * @param maxlength + */ + public int appendFeaturesLengthLimit(final StringBuilder sb, + int residuePos, List features, + FeatureRendererModel fr, int maxlength) + { + for (int i = 0; i < features.size(); i++) { - for (String[] primary : PRIMARY_SOURCES) + SequenceFeature feature = features.get(i); + if (appendFeature(sb, residuePos, fr, feature, null, maxlength)) { - for (String s : primary) - { - if (source.equals(s)) - { - return true; - } - } + return features.size() - i; } - return false; } - }; + return 0; + } - public SequenceAnnotationReport(String linkImageURL) + public void appendFeatures(final StringBuilder sb, int residuePos, + List features, FeatureRendererModel fr) { - this.linkImageURL = linkImageURL; + appendFeaturesLengthLimit(sb, residuePos, features, fr, 0); } /** - * Append text for the list of features to the tooltip + * Appends text for mapped features (e.g. CDS feature for peptide or vice versa) + * Returns number of features left if maxlength limit is (or would have been) + * reached * * @param sb - * @param rpos - * @param features - * @param minmax + * @param residuePos + * @param mf + * @param fr + * @param maxlength */ - public void appendFeatures(final StringBuilder sb, int rpos, - List features, Map minmax) + public int appendFeaturesLengthLimit(StringBuilder sb, int residuePos, + MappedFeatures mf, FeatureRendererModel fr, int maxlength) { - if (features != null) + for (int i = 0; i < mf.features.size(); i++) { - for (SequenceFeature feature : features) + SequenceFeature feature = mf.features.get(i); + if (appendFeature(sb, residuePos, fr, feature, mf, maxlength)) { - appendFeature(sb, rpos, minmax, feature); + return mf.features.size() - i; } } + return 0; + } + + public void appendFeatures(StringBuilder sb, int residuePos, + MappedFeatures mf, FeatureRendererModel fr) + { + appendFeaturesLengthLimit(sb, residuePos, mf, fr, 0); } /** @@ -139,111 +193,149 @@ public class SequenceAnnotationReport * @param minmax * @param feature */ - void appendFeature(final StringBuilder sb, int rpos, - Map minmax, SequenceFeature feature) + boolean appendFeature(final StringBuilder sb0, int rpos, + FeatureRendererModel fr, SequenceFeature feature, + MappedFeatures mf, int maxlength) { + StringBuilder sb = new StringBuilder(); if (feature.isContactFeature()) { if (feature.getBegin() == rpos || feature.getEnd() == rpos) { - if (sb.length() > 6) + if (sb0.length() > 6) { - sb.append("
"); + sb.append("
"); } sb.append(feature.getType()).append(" ").append(feature.getBegin()) - .append(":") - .append(feature.getEnd()); + .append(":").append(feature.getEnd()); } + return appendTextMaxLengthReached(sb0, sb, maxlength); + } + + if (sb0.length() > 6) + { + sb.append("
"); } - else + // TODO: remove this hack to display link only features + boolean linkOnly = feature.getValue("linkonly") != null; + if (!linkOnly) { - if (sb.length() > 6) + sb.append(feature.getType()).append(" "); + if (rpos != 0) { - sb.append("
"); + // we are marking a positional feature + sb.append(feature.begin); } - // TODO: remove this hack to display link only features - boolean linkOnly = feature.getValue("linkonly") != null; - if (!linkOnly) + if (feature.begin != feature.end) { - sb.append(feature.getType()).append(" "); - if (rpos != 0) - { - // we are marking a positional feature - sb.append(feature.begin); - } - if (feature.begin != feature.end) + sb.append(" ").append(feature.end); + } + + String description = feature.getDescription(); + if (description != null && !description.equals(feature.getType())) + { + description = StringUtils.stripHtmlTags(description); + + /* + * truncate overlong descriptions unless they contain an href + * before the truncation point (as truncation could leave corrupted html) + */ + int linkindex = description.toLowerCase().indexOf(" -1 + && linkindex < MAX_DESCRIPTION_LENGTH; + if (description.length() > MAX_DESCRIPTION_LENGTH && !hasLink) { - sb.append(" ").append(feature.end); + description = description.substring(0, MAX_DESCRIPTION_LENGTH) + + ELLIPSIS; } - if (feature.getDescription() != null - && !feature.description.equals(feature.getType())) - { - String tmpString = feature.getDescription(); - String tmp2up = tmpString.toUpperCase(); - int startTag = tmp2up.indexOf(""); - if (startTag > -1) - { - tmpString = tmpString.substring(startTag + 6); - tmp2up = tmp2up.substring(startTag + 6); - } - int endTag = tmp2up.indexOf(""); - if (endTag > -1) - { - tmpString = tmpString.substring(0, endTag); - tmp2up = tmp2up.substring(0, endTag); - } - endTag = tmp2up.indexOf(""); - if (endTag > -1) - { - tmpString = tmpString.substring(0, endTag); - } + sb.append("; ").append(description); + } - if (startTag > -1) - { - sb.append("; ").append(tmpString); - } - else - { - if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1) - { - // The description does not specify html is to - // be used, so we must remove < > symbols - tmpString = tmpString.replaceAll("<", "<"); - tmpString = tmpString.replaceAll(">", ">"); + if (showScore(feature, fr)) + { + sb.append(" Score=").append(String.valueOf(feature.getScore())); + } + String status = (String) feature.getValue("status"); + if (status != null && status.length() > 0) + { + sb.append("; (").append(status).append(")"); + } - sb.append("; "); - sb.append(tmpString); - } - else - { - sb.append("; ").append(tmpString); - } - } - } - // check score should be shown - if (!Float.isNaN(feature.getScore())) + /* + * add attribute value if coloured by attribute + */ + if (fr != null) + { + FeatureColourI fc = fr.getFeatureColours().get(feature.getType()); + if (fc != null && fc.isColourByAttribute()) { - float[][] rng = (minmax == null) ? null : minmax.get(feature - .getType()); - if (rng != null && rng[0] != null && rng[0][0] != rng[0][1]) + String[] attName = fc.getAttributeName(); + String attVal = feature.getValueAsString(attName); + if (attVal != null) { - sb.append(" Score=").append(String.valueOf(feature.getScore())); + sb.append("; ").append(String.join(":", attName)).append("=") + .append(attVal); } } - String status = (String) feature.getValue("status"); - if (status != null && status.length() > 0) - { - sb.append("; (").append(status).append(")"); - } - String clinSig = (String) feature - .getValue(GffConstants.CLINICAL_SIGNIFICANCE); - if (clinSig != null) + } + + if (mf != null) + { + String variants = mf.findProteinVariants(feature); + if (!variants.isEmpty()) { - sb.append("; ").append(clinSig); + sb.append(" ").append(variants); } } } + return appendTextMaxLengthReached(sb0, sb, maxlength); + } + + void appendFeature(final StringBuilder sb, int rpos, + FeatureRendererModel fr, SequenceFeature feature, + MappedFeatures mf) + { + appendFeature(sb, rpos, fr, feature, mf, 0); + } + + private static boolean appendTextMaxLengthReached(StringBuilder sb0, + StringBuilder sb, int maxlength) + { + boolean ret = false; + if (maxlength == 0 || sb0.length() + sb.length() < maxlength) + { + sb0.append(sb); + return false; + } else { + return true; + } + } + + /** + * Answers true if score should be shown, else false. Score is shown if it is + * not NaN, and the feature type has a non-trivial min-max score range + */ + boolean showScore(SequenceFeature feature, FeatureRendererModel fr) + { + if (Float.isNaN(feature.getScore())) + { + return false; + } + if (fr == null) + { + return true; + } + float[][] minMax = fr.getMinMax().get(feature.getType()); + + /* + * minMax[0] is the [min, max] score range for positional features + */ + if (minMax == null || minMax[0] == null || minMax[0][0] == minMax[0][1]) + { + return false; + } + return true; } /** @@ -277,8 +369,8 @@ public class SequenceAnnotationReport + (urllink.get(0).toLowerCase() .equals(urllink.get(1).toLowerCase()) ? urllink .get(0) : (urllink.get(0) + ":" + urllink - .get(1))) - + "
"); + .get(1))) + + "
"); } } catch (Exception x) { @@ -301,7 +393,7 @@ public class SequenceAnnotationReport */ Collection> createLinksFrom(SequenceI seq, String link) { - Map> urlSets = new LinkedHashMap>(); + Map> urlSets = new LinkedHashMap<>(); UrlLink urlLink = new UrlLink(link); if (!urlLink.isValid()) { @@ -316,10 +408,10 @@ public class SequenceAnnotationReport public void createSequenceAnnotationReport(final StringBuilder tip, SequenceI sequence, boolean showDbRefs, boolean showNpFeats, - Map minmax) + FeatureRendererModel fr) { createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats, - minmax, false); + fr, false); } /** @@ -334,13 +426,13 @@ public class SequenceAnnotationReport * whether to include database references for the sequence * @param showNpFeats * whether to include non-positional sequence features - * @param minmax + * @param fr * @param summary * @return */ int createSequenceAnnotationReport(final StringBuilder sb, SequenceI sequence, boolean showDbRefs, boolean showNpFeats, - Map minmax, boolean summary) + FeatureRendererModel fr, boolean summary) { String tmp; sb.append(""); @@ -349,7 +441,7 @@ public class SequenceAnnotationReport if (sequence.getDescription() != null) { tmp = sequence.getDescription(); - sb.append("
").append(tmp); + sb.append(tmp); maxWidth = Math.max(maxWidth, tmp.length()); } SequenceI ds = sequence; @@ -357,7 +449,7 @@ public class SequenceAnnotationReport { ds = ds.getDatasetSequence(); } - + if (showDbRefs) { maxWidth = Math.max(maxWidth, appendDbRefs(sb, ds, summary)); @@ -372,7 +464,7 @@ public class SequenceAnnotationReport .getNonPositionalFeatures()) { int sz = -sb.length(); - appendFeature(sb, 0, minmax, sf); + appendFeature(sb, 0, fr, sf, null); sz += sb.length(); maxWidth = Math.max(maxWidth, sz); } @@ -393,14 +485,14 @@ public class SequenceAnnotationReport protected int appendDbRefs(final StringBuilder sb, SequenceI ds, boolean summary) { - DBRefEntry[] dbrefs = ds.getDBRefs(); + List dbrefs = ds.getDBRefs(); if (dbrefs == null) { return 0; } // note this sorts the refs held on the sequence! - Arrays.sort(dbrefs, comparator); + dbrefs.sort(comparator); boolean ellipsis = false; String source = null; String lastSource = null; @@ -435,7 +527,7 @@ public class SequenceAnnotationReport countForSource++; if (countForSource == 1 || !summary) { - sb.append("
"); + sb.append("
"); } if (countForSource <= MAX_REFS_PER_SOURCE || !summary) { @@ -461,12 +553,11 @@ public class SequenceAnnotationReport } if (moreSources) { - sb.append("
").append(source) - .append(COMMA).append(ELLIPSIS); + sb.append("
").append(source).append(COMMA).append(ELLIPSIS); } if (ellipsis) { - sb.append("
("); + sb.append("
("); sb.append(MessageManager.getString("label.output_seq_details")); sb.append(")"); } @@ -476,10 +567,10 @@ public class SequenceAnnotationReport public void createTooltipAnnotationReport(final StringBuilder tip, SequenceI sequence, boolean showDbRefs, boolean showNpFeats, - Map minmax) + FeatureRendererModel fr) { int maxWidth = createSequenceAnnotationReport(tip, sequence, - showDbRefs, showNpFeats, minmax, true); + showDbRefs, showNpFeats, fr, true); if (maxWidth > 60) {