JAL-722 updated from 2.11.2 develop branch - needs further work before release features/JAL-722alignmentProperties
authorJim Procter <j.procter@dundee.ac.uk>
Mon, 7 Feb 2022 12:13:26 +0000 (12:13 +0000)
committerJim Procter <j.procter@dundee.ac.uk>
Mon, 7 Feb 2022 12:13:26 +0000 (12:13 +0000)
 Conflicts:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/datamodel/Alignment.java
src/jalview/gui/AlignFrame.java
src/jalview/io/AlignFile.java
src/jalview/project/Jalview2XML.java

14 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/gui/AlignFrame.java
src/jalview/io/AlignFile.java
src/jalview/io/AlignmentProperties.java
src/jalview/io/AnnotationFile.java
src/jalview/io/StockholmFile.java
src/jalview/jbgui/GUserDefinedColours.java
src/jalview/project/Jalview2XML.java
test/jalview/io/AlignmentPropertiesTest.java [new file with mode: 0644]

index b448b4c..075cb18 100644 (file)
@@ -317,7 +317,6 @@ label.removed_columns = Removed {0} columns.
 label.removed_empty_columns = Removed {0} empty columns.
 label.paste_newick_tree_file = Paste your Newick tree file here.
 label.order_by_params = Order by {0}
-label.html_content = <html>{0}</html>
 label.paste_pdb_file= Paste your PDB file here.
 label.paste_pdb_file_for_sequence = Paste PDB file for sequence {0}
 label.could_not_parse_newick_file  = Could not parse Newick file\!\n {0}
@@ -948,6 +947,7 @@ error.implementation_error_old_jalview_object_not_bound =IMPLEMENTATION ERROR: o
 error.implementation_error_vamsas_doc_class_should_bind_to_type = Implementation Error: Vamsas Document Class {0} should bind to a {1} (found a {2})
 error.invalid_vamsas_rangetype_cannot_resolve_lists = Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!
 error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key
+error.implementation_error_maplist_is_null = Implementation error. MapList is null for initMapType.
 error.implementation_error_null_fileparse = Implementation error. Null FileParse in copy constructor
 error.implementation_error_structure_selection_manager_null = Implementation error. Structure selection manager's context is 'null'
 exception.ssm_context_is_null = SSM context is null
index 49e05e3..404a157 100644 (file)
@@ -282,7 +282,6 @@ label.removed_columns = {0} columnas eliminadas.
 label.removed_empty_columns = {0} columnas vacías eliminadas.
 label.paste_newick_tree_file = Pegar su fichero árbol Newick aquí. 
 label.order_by_params = Ordenar por {0}
-label.html_content = <html>{0}</html>
 label.paste_pdb_file= Pegar tu fichero PDB aquí.
 label.paste_pdb_file_for_sequence = Pegar fichero PDB para la secuencia {0}
 label.could_not_parse_newick_file  = No se pudo analizar el fichero Newick\!\n {0}
@@ -866,6 +865,7 @@ error.implementation_error_old_jalview_object_not_bound =Error de implementaci
 error.implementation_error_vamsas_doc_class_should_bind_to_type = Error de implementación: la clase de documento VAMSAS {0} debe enlazar a {1} (pero se ha encontrado que lo está a {2})
 error.invalid_vamsas_rangetype_cannot_resolve_lists = RangeType VAMSAS no válido - ¡no es posible resolver ambas listas de Pos y Seg con los valores elegidos!
 error.implementation_error_cannot_have_null_alignment = Error de implementación: no es posible tener una clave nula en el alineamiento
+error.implementation_error_maplist_is_null = Error de implementación. MapList es nulo en initMapType.
 error.implementation_error_null_fileparse = Error de implementación. FileParse nulo en el construictor de copia
 error.implementation_error_structure_selection_manager_null = Error de implementación. El contexto structure selection manager's es nulo
 exception.ssm_context_is_null = El contexto SSM es nulo
index 80db11d..713ae02 100644 (file)
@@ -911,9 +911,7 @@ public class APopupMenu extends java.awt.PopupMenu
                     ? sequences[0].getDisplayId(true)
                     : "Selection"),
             600, 500);
-    cap.setText(
-            MessageManager.formatMessage("label.html_content", new Object[]
-            { contents.toString() }));
+    cap.setText("<html>" + contents.toString() + "</html>");
   }
 
   void editName()
index b6f8929..eb6b863 100644 (file)
@@ -51,6 +51,7 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.io.AlignmentProperties;
 import jalview.io.AnnotationFile;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
@@ -1208,7 +1209,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (source == alProperties)
     {
-      StringBuffer contents = new jalview.io.AlignmentProperties(
+      StringBuilder contents = new AlignmentProperties(
               viewport.getAlignment()).formatAsString();
       CutAndPasteTransfer cap = new CutAndPasteTransfer(false, this);
       cap.setText(contents.toString());
index c4098e2..310847b 100755 (executable)
@@ -32,8 +32,8 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -67,7 +67,7 @@ public class Alignment implements AlignmentI, AutoCloseable
 
   HiddenColumns hiddenCols;
 
-  public Hashtable alignmentProperties;
+  public Map<Object, Object> alignmentProperties;
 
   private List<AlignedCodonFrame> codonFrameList;
 
@@ -1371,7 +1371,7 @@ public class Alignment implements AlignmentI, AutoCloseable
   {
     if (alignmentProperties == null)
     {
-      alignmentProperties = new Hashtable();
+      alignmentProperties = new HashMap<>();
     }
 
     alignmentProperties.put(key, value);
@@ -1391,7 +1391,7 @@ public class Alignment implements AlignmentI, AutoCloseable
   }
 
   @Override
-  public Hashtable getProperties()
+  public Map<Object, Object> getProperties()
   {
     return alignmentProperties;
   }
@@ -1559,10 +1559,10 @@ public class Alignment implements AlignmentI, AutoCloseable
     {
       // we really can't do very much here - just try to concatenate strings
       // where property collisions occur.
-      Enumeration key = toappend.getProperties().keys();
-      while (key.hasMoreElements())
+      Iterator<Object> key = toappend.getProperties().keySet().iterator();
+      while (key.hasNext())
       {
-        Object k = key.nextElement();
+        Object k = key.next();
         Object ourval = this.getProperty(k);
         Object toapprop = toappend.getProperty(k);
         if (ourval != null)
index 93a2456..4b1220b 100755 (executable)
@@ -20,7 +20,6 @@
  */
 package jalview.datamodel;
 
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -403,7 +402,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return hashtable of alignment properties (or null if none are defined)
    */
-  Hashtable getProperties();
+  Map<Object, Object> getProperties();
 
   /**
    * add a reference to a frame of aligned codons for this alignment
index 8deff01..a36c186 100644 (file)
@@ -3379,49 +3379,42 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   @Override
-  public void alignmentProperties()
-  {
-    JComponent pane;
-    StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
-
-            .formatAsHtml();
-    String content = MessageManager.formatMessage("label.html_content",
-            new Object[]
-            { contents.toString() });
-    contents = null;
-
-    if (Platform.isJS())
-    {
-      JLabel textLabel = new JLabel();
-      textLabel.setText(content);
-      textLabel.setBackground(Color.WHITE);
-
-      pane = new JPanel(new BorderLayout());
-      ((JPanel) pane).setOpaque(true);
-      pane.setBackground(Color.WHITE);
-      ((JPanel) pane).add(textLabel, BorderLayout.NORTH);
-    }
-    else
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      JEditorPane editPane = new JEditorPane("text/html", "");
-      editPane.setEditable(false);
-      editPane.setText(content);
-      pane = editPane;
-    }
-
-    JInternalFrame frame = new JInternalFrame();
-
-    frame.getContentPane().add(new JScrollPane(pane));
-
-    Desktop.addInternalFrame(frame, MessageManager
-            .formatMessage("label.alignment_properties", new Object[]
-            { getTitle() }), 500, 400);
-  }
+       public void alignmentProperties() {
+               JComponent pane;
+               StringBuilder contents = new StringBuilder(128);
+               contents.append("<html><body>");
+               contents.append(new AlignmentProperties(viewport.getAlignment()).formatAsHtml());
+               contents.append("</body></html>");
+
+               String content = contents.toString();
+
+               JInternalFrame frame = null;
+               if (Platform.isJS()) {
+                       JLabel textLabel = new JLabel();
+                       textLabel.setText(content);
+                       textLabel.setBackground(Color.WHITE);
+
+                       pane = new JPanel(new BorderLayout());
+                       ((JPanel) pane).setOpaque(true);
+                       pane.setBackground(Color.WHITE);
+                       ((JPanel) pane).add(textLabel, BorderLayout.NORTH);
+                       frame = new JInternalFrame();
+                       frame.getContentPane().add(new JScrollPane(pane));
+               } else
+               /**
+                * Java only
+                * 
+                * @j2sIgnore
+                */
+               {
+                       CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
+                       cap.setText(content);
+                       frame = cap;
+               }
+
+               Desktop.addInternalFrame(frame,
+                               MessageManager.formatMessage("label.alignment_properties", new Object[] { getTitle() }), 500, 400);
+       }
 
   /**
    * DOCUMENT ME!
index 3202ac9..48f65cf 100755 (executable)
@@ -22,9 +22,10 @@ package jalview.io;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Vector;
 
 import jalview.datamodel.AlignmentAnnotation;
@@ -66,7 +67,7 @@ public abstract class AlignFile extends FileParse
   /**
    * Properties to be added to generated alignment object
    */
-  private Hashtable properties;
+  private Map<String, String> properties;
 
   long start;
 
@@ -288,13 +289,11 @@ public abstract class AlignFile extends FileParse
    */
   public void addProperties(AlignmentI al)
   {
-    if (properties != null && properties.size() > 0)
+    if (properties != null)
     {
-      Enumeration keys = properties.keys();
-      Enumeration vals = properties.elements();
-      while (keys.hasMoreElements())
+      for (Entry<String, String> prop : properties.entrySet())
       {
-        al.setProperty(keys.nextElement(), vals.nextElement());
+        al.setProperty(prop.getKey(), prop.getValue());
       }
     }
   }
@@ -309,12 +308,12 @@ public abstract class AlignFile extends FileParse
    * @param value
    *          - non-null value
    */
-  protected void setAlignmentProperty(Object key, Object value)
+  protected void setAlignmentProperty(String key, String value)
   {
     if (key == null)
     {
-      throw new Error(MessageManager.getString(
-              "error.implementation_error_cannot_have_null_alignment"));
+      throw new Error(
+              "Implementation error: Cannot have null alignment property key.");
     }
     if (value == null)
     {
@@ -322,12 +321,12 @@ public abstract class AlignFile extends FileParse
     }
     if (properties == null)
     {
-      properties = new Hashtable();
+      properties = new Hashtable<>();
     }
     properties.put(key, value);
   }
 
-  protected Object getAlignmentProperty(Object key)
+  protected String getAlignmentProperty(String key)
   {
     if (properties != null && key != null)
     {
@@ -341,9 +340,9 @@ public abstract class AlignFile extends FileParse
    */
   protected void initData()
   {
-    seqs = new Vector<SequenceI>();
-    annotations = new Vector<AlignmentAnnotation>();
-    seqGroups = new ArrayList<SequenceGroup>();
+    seqs = new Vector<>();
+    annotations = new Vector<>();
+    seqGroups = new ArrayList<>();
     parseCalled = false;
   }
 
@@ -356,7 +355,7 @@ public abstract class AlignFile extends FileParse
   @Override
   public void setSeqs(SequenceI[] s)
   {
-    seqs = new Vector<SequenceI>();
+    seqs = new Vector<>();
 
     for (int i = 0; i < s.length; i++)
     {
@@ -427,7 +426,7 @@ public abstract class AlignFile extends FileParse
   {
     if (newickStrings == null)
     {
-      newickStrings = new Vector<String[]>();
+      newickStrings = new Vector<>();
     }
     newickStrings.addElement(new String[] { treeName, newickString });
   }
index f2a6150..69bd658 100644 (file)
  */
 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
@@ -37,6 +36,12 @@ import java.util.Hashtable;
  */
 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)
@@ -47,100 +52,172 @@ public class AlignmentProperties
   /**
    * 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);
   }
 
   /**
@@ -148,9 +225,9 @@ public class AlignmentProperties
    * 
    * @return
    */
-  public StringBuffer formatAsHtml()
+  public StringBuilder formatAsHtml()
   {
-    return formatReport(true);
+    return writeProperties(true);
   }
 
 }
index 934be41..64204b4 100755 (executable)
@@ -134,8 +134,8 @@ public class AnnotationFile
    * @return annotation file
    */
   public String printAnnotations(AlignmentAnnotation[] annotations,
-          List<SequenceGroup> list, Hashtable properties, HiddenColumns cs,
-          AlignmentI al, ViewDef view)
+          List<SequenceGroup> list, Map<Object, Object> properties,
+          HiddenColumns cs, AlignmentI al, ViewDef view)
   {
     if (view != null)
     {
@@ -439,14 +439,13 @@ public class AnnotationFile
       text.append(newline);
       text.append(newline);
       text.append("ALIGNMENT");
-      Enumeration en = properties.keys();
-      while (en.hasMoreElements())
+      for (Object ko : properties.keySet())
       {
-        String key = en.nextElement().toString();
+        String key = ko.toString();
         text.append("\t");
         text.append(key);
         text.append("=");
-        text.append(properties.get(key));
+        text.append(properties.get(ko));
       }
       // TODO: output alignment visualization settings here if required
       // iterate through one or more views, defining, marking columns and rows
index c8c9c8a..9ca86f2 100644 (file)
@@ -34,6 +34,7 @@ import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Vector;
 
 import com.stevesoft.pat.Regex;
@@ -1009,15 +1010,11 @@ public class StockholmFile extends AlignFile
     // output database type
     if (al.getProperties() != null)
     {
-      if (!al.getProperties().isEmpty())
+      for (Entry<Object, Object> prop : al.getProperties().entrySet())
       {
-        Enumeration key = al.getProperties().keys();
-        Enumeration val = al.getProperties().elements();
-        while (key.hasMoreElements())
-        {
-          out.append("#=GF " + key.nextElement() + " " + val.nextElement());
-          out.append(newline);
-        }
+        out.append("#=GF " + prop.getKey().toString() + " "
+                + prop.getValue().toString());
+        out.append(newline);
       }
     }
 
index 359f787..5c000dd 100755 (executable)
@@ -208,10 +208,9 @@ public class GUserDefinedColours extends JPanel
     label.setFont(new java.awt.Font("Verdana", Font.ITALIC, 10));
     label.setOpaque(false);
     label.setPreferredSize(new Dimension(260, 34));
-    label.setText(
-            MessageManager.formatMessage("label.html_content", new String[]
-            { MessageManager.getString(
-                    "label.save_colour_scheme_with_unique_name_added_to_colour_menu") }));
+    label.setText("<html>" + MessageManager.getString(
+            "label.save_colour_scheme_with_unique_name_added_to_colour_menu")
+            + "</html>");
     caseSensitive.setText(MessageManager.getString("label.case_sensitive"));
     caseSensitive.addActionListener(new ActionListener()
     {
@@ -260,7 +259,7 @@ public class GUserDefinedColours extends JPanel
               { choosers[0] });
     }
 
-    selectedButtons = new ArrayList<JButton>();
+    selectedButtons = new ArrayList<>();
   }
 
   /**
index acfeb2a..985aece 100644 (file)
@@ -914,13 +914,12 @@ public class Jalview2XML
     }
     if (jal.getProperties() != null)
     {
-      Enumeration en = jal.getProperties().keys();
-      while (en.hasMoreElements())
+      for (Entry<Object, Object> prop : jal.getProperties().entrySet())
       {
-        String key = en.nextElement().toString();
+        String key = prop.getKey().toString();
         SequenceSetProperties ssp = new SequenceSetProperties();
         ssp.setKey(key);
-        ssp.setValue(jal.getProperties().get(key).toString());
+        ssp.setValue(prop.getValue().toString());
         // vamsasSet.addSequenceSetProperties(ssp);
         vamsasSet.getSequenceSetProperties().add(ssp);
       }
diff --git a/test/jalview/io/AlignmentPropertiesTest.java b/test/jalview/io/AlignmentPropertiesTest.java
new file mode 100644 (file)
index 0000000..a3f58ce
--- /dev/null
@@ -0,0 +1,36 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import org.testng.annotations.Test;
+
+public class AlignmentPropertiesTest
+{
+  @Test(groups="Functional")
+  public void testWriteProperties_asHtml()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "--ABC-DEFGH-"); // 8 residues
+    SequenceI seq2 = new Sequence("Seq2", "---BC-DE-KH-"); // 6
+    SequenceI seq3 = new Sequence("Seq3", "-RABCFDE-KH-"); // 9
+    AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2, seq3 });
+
+    AlignmentProperties ap = new AlignmentProperties(al);
+    StringBuilder sb = ap.writeProperties(true);
+    String expected = "<table border=\"1\">"
+            + "<tr><td>Sequences</td><td>3</td></tr>"
+            + "<tr><td>Alignment width</td><td>12</td></tr>"
+            + "<tr><td>Minimum Sequence Length</td><td>6</td></tr>"
+            + "<tr><td>Maximum Sequence Length</td><td>9</td></tr>"
+            + "<tr><td>Average Length</td><td>7</td></tr>"
+            + "<tr><td>Minimum (sequence length / width)</td><td>50.0%</td></tr>" // 6/12
+            + "<tr><td>Maximum (sequence length / width)</td><td>75.0%</td></tr>" // 9/12
+            + "<tr><td>Residue density</td><td>63.9%</td></tr>" // 23/36
+            + "<tr><td>Gap density</td><td>36.1%</td></tr>" + "</table>";
+    assertEquals(sb.toString(), expected);
+  }
+}