/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b1)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
*/
package jalview.io;
-import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Enumeration;
-import java.util.Hashtable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
/**
* Render associated attributes of an alignment. The heart of this code was
*/
public class AlignmentProperties
{
+ private static final String BR_TAG = "<br>";
+
+ private static final String NEWLINE = System.getProperty("line.separator");
+
+ private static final String PCT_FORMAT = "%.1f%%";
+
AlignmentI alignment;
public AlignmentProperties(AlignmentI alignment)
/**
* render the alignment's properties report as text or an HTML fragment
*
- * @param pw
* @param html
*/
- public void writeProperties(PrintWriter pw, boolean html)
+ protected StringBuilder writeProperties(boolean html)
{
- final String nl = html ? "<br>" : System.getProperty("line.separator");
- float avg = 0;
- int min = Integer.MAX_VALUE, max = 0;
- for (int i = 0; i < alignment.getHeight(); i++)
+ StringBuilder sb = new StringBuilder(256);
+ final String nl = html ? BR_TAG : NEWLINE;
+ int totalLength = 0;
+ int totalGaps = 0;
+ int minLength = Integer.MAX_VALUE;
+ int maxLength = 0;
+ float maxUngapped = 0f;
+ float minUngapped = Float.MAX_VALUE;
+
+ final int height = alignment.getHeight();
+ final int width = alignment.getWidth();
+
+ for (int i = 0; i < height; i++)
{
- int size = 1 + alignment.getSequenceAt(i).getEnd()
- - alignment.getSequenceAt(i).getStart();
- avg += size;
- if (size > max)
- {
- max = size;
- }
- if (size < min)
- {
- min = size;
- }
+ SequenceI seq = alignment.getSequenceAt(i);
+ // sequence length including gaps:
+ int seqWidth = seq.getLength();
+ // sequence length excluding gaps:
+ int seqLength = 1 + seq.getEnd() - seq.getStart();
+ int gapCount = seqWidth - seqLength; // includes padding
+ totalLength += seqLength;
+ totalGaps += gapCount;
+ maxLength = Math.max(maxLength, seqLength);
+ minLength = Math.min(minLength, seqLength);
+
+ /*
+ * proportion of aligned sequence that is ungapped
+ * (note: normalised by alignment width here)
+ */
+ float ungapped = (seqWidth - gapCount) / (float) seqWidth;
+ maxUngapped = Math.max(maxUngapped, ungapped);
+ minUngapped = Math.min(minUngapped, ungapped);
}
- avg = avg / alignment.getHeight();
- pw.print(nl);
- pw.print("Sequences: " + alignment.getHeight());
- pw.print(nl);
- pw.print("Minimum Sequence Length: " + min);
- pw.print(nl);
- pw.print("Maximum Sequence Length: " + max);
- pw.print(nl);
- pw.print("Average Length: " + (int) avg);
-
- if (((Alignment) alignment).alignmentProperties != null)
+ float avgLength = totalLength / (float) height;
+
+ sb.append(html ? "<table border=\"1\">" : nl);
+ appendRow(sb, "Sequences", String.valueOf(height), html);
+ appendRow(sb, "Alignment width", String.valueOf(width),
+ html);
+ appendRow(sb, "Minimum Sequence Length", String.valueOf(minLength),
+ html);
+ appendRow(sb, "Maximum Sequence Length", String.valueOf(maxLength),
+ html);
+ appendRow(sb, "Average Length", String.valueOf((int) avgLength), html);
+ appendRow(sb, "Minimum (sequence length / width)",
+ String.format(PCT_FORMAT, minUngapped * 100f), html);
+ appendRow(sb, "Maximum (sequence length / width)",
+ String.format(PCT_FORMAT, maxUngapped * 100f), html);
+ appendRow(sb, "Residue density", String.format(PCT_FORMAT,
+ (height * width - totalGaps) * 100f / (height * width)), html);
+ appendRow(sb, "Gap density",
+ String.format(PCT_FORMAT, totalGaps * 100f / (height * width)),
+ html);
+ sb.append(html ? "</table>" : nl);
+
+ Map<Object, Object> props = alignment.getProperties();
+ if (props != null && !props.isEmpty())
{
- pw.print(nl);
- pw.print(nl);
+ sb.append(nl);
+ sb.append(nl);
if (html)
{
- pw.print("<table border=\"1\">");
+ sb.append("<table border=\"1\">");
}
- Hashtable props = ((Alignment) alignment).alignmentProperties;
- Enumeration en = props.keys();
- while (en.hasMoreElements())
+
+ /*
+ * sort keys alphabetically for ease of reading the output
+ */
+ Object[] keys = props.keySet().toArray(new Object[props.size()]);
+ Arrays.sort(keys, new Comparator<Object>()
{
- String key = en.nextElement().toString();
- String vals = props.get(key).toString();
- if (html)
+ @Override
+ public int compare(Object o1, Object o2)
{
- // wrap the text in the table
- StringBuffer val = new StringBuffer();
- int pos = 0, npos;
- do
- {
- npos = vals.indexOf("\n", pos);
- if (npos == -1)
- {
- val.append(vals.substring(pos));
- }
- else
- {
- val.append(vals.substring(pos, npos));
- val.append("<br>");
- }
- pos = npos + 1;
- } while (npos != -1);
- pw.print("<tr><td>" + key + "</td><td>" + val + "</td></tr>");
+ return String.CASE_INSENSITIVE_ORDER.compare(o1.toString(),
+ o2.toString());
}
- else
+ });
+ for (Object key : keys)
+ {
+ String value = props.get(key).toString();
+ if (html)
{
- pw.print(nl + key + "\t" + vals);
+ value = value.replaceAll("\\R", value); // Java 8 newline matcher
+ value = formatHrefs(value);
}
+ appendRow(sb, key.toString(), value, html);
}
if (html)
{
- pw.print("</table>");
+ sb.append("</table>");
}
}
+ return sb;
}
/**
- * generate a report as plain text
+ * Helper method to change any token starting with http into an html href
*
+ * @param value
* @return
*/
- public StringBuffer formatAsString()
+ private String formatHrefs(String value)
+ {
+ if (!value.contains("http"))
+ {
+ return value;
+ }
+
+ StringBuilder sb = new StringBuilder(value.length() * 3);
+ String[] tokens = value.split("\\s");
+ boolean found = false;
+ boolean first = true;
+ for (String token : tokens)
+ {
+ if (token.startsWith("http"))
+ {
+ token = "<a href=\"" + token + "\">" + token + "</a>";
+ found = true;
+ }
+ if (!first)
+ {
+ sb.append(" ");
+ }
+ sb.append(token);
+ first = false;
+ }
+ return found ? sb.toString() : value;
+ }
+
+ /**
+ * A helper method to add one key-value row, optionally in HTML table entry
+ * format
+ *
+ * @param sb
+ * @param key
+ * @param value
+ * @param html
+ */
+ private void appendRow(StringBuilder sb, String key, String value,
+ boolean html)
{
- return formatReport(false);
+ if (html)
+ {
+ sb.append("<tr><td>").append(key).append("</td><td>").append(value)
+ .append("</td></tr>");
+ }
+ else
+ {
+ sb.append(html ? BR_TAG : NEWLINE).append(key).append("\t")
+ .append(value);
+ }
}
- protected StringBuffer formatReport(boolean html)
+ /**
+ * generate a report as plain text
+ *
+ * @return
+ */
+ public StringBuilder formatAsString()
{
- StringWriter content = new StringWriter();
- writeProperties(new PrintWriter(content), html);
- return content.getBuffer();
+ return writeProperties(false);
}
/**
*
* @return
*/
- public StringBuffer formatAsHtml()
+ public StringBuilder formatAsHtml()
{
- return formatReport(true);
+ return writeProperties(true);
}
}