JAL-852 clickable links in Alignment Properties report; alignmentProperties now Map...
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 31 Jan 2018 16:22:01 +0000 (16:22 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 31 Jan 2018 16:22:01 +0000 (16:22 +0000)
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/AlignFrame.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/Jalview2XML.java
src/jalview/io/AlignFile.java
src/jalview/io/AlignmentProperties.java
src/jalview/io/AnnotationFile.java
src/jalview/io/StockholmFile.java

index 05f7a57..0056580 100644 (file)
@@ -967,7 +967,6 @@ 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_maplist_is_null = Implementation error. MapList is null for initMapType.
-error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key
 error.implementation_error_null_fileparse = Implementation error. Null FileParse in copy constructor
 error.implementation_error_cannot_map_alignment_sequences = IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.
 error.implementation_error_structure_selection_manager_null = Implementation error. Structure selection manager's context is 'null'
index 1c62648..abc3612 100644 (file)
@@ -892,7 +892,6 @@ 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_maplist_is_null = Error de implementación. MapList es nulo en initMapType.
-error.implementation_error_cannot_have_null_alignment = Error de implementación: no es posible tener una clave nula en el alineamiento
 error.implementation_error_null_fileparse = Error de implementación. FileParse nulo en el construictor de copia
 error.implementation_error_cannot_map_alignment_sequences = Error de implementación: no es posible maper un alineamiento de secuencias desde distintos conjuntos de datos en un único alineamiento en el documento VAMSAS.
 error.implementation_error_structure_selection_manager_null = Error de implementación. El contexto structure selection manager's es nulo
index ef87671..37e9b11 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;
@@ -1206,7 +1207,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 f268d37..1265923 100755 (executable)
@@ -31,8 +31,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 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;
 import java.util.Set;
@@ -65,7 +66,7 @@ public class Alignment implements AlignmentI
 
   HiddenColumns hiddenCols;
 
-  public Hashtable alignmentProperties;
+  public Map<Object, Object> alignmentProperties;
 
   private List<AlignedCodonFrame> codonFrameList;
 
@@ -1353,7 +1354,7 @@ public class Alignment implements AlignmentI
   {
     if (alignmentProperties == null)
     {
-      alignmentProperties = new Hashtable();
+      alignmentProperties = new HashMap<>();
     }
 
     alignmentProperties.put(key, value);
@@ -1373,7 +1374,7 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public Hashtable getProperties()
+  public Map<Object, Object> getProperties()
   {
     return alignmentProperties;
   }
@@ -1541,10 +1542,10 @@ public class Alignment implements AlignmentI
     {
       // 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 084b80e..757dad1 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;
@@ -389,7 +388,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 da11536..bb81a4e 100644 (file)
@@ -3221,7 +3221,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void alignmentProperties()
   {
     CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
-    StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
+    StringBuilder contents = new AlignmentProperties(
+            viewport.getAlignment())
             .formatAsHtml();
     cap.setText("<html>" + contents.toString() + "</html>");
 
index 4a15024..26a11de 100644 (file)
@@ -768,13 +768,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);
       }
     }
@@ -5333,7 +5332,7 @@ public class Jalview2XML
 
     if (this.frefedSequence == null)
     {
-      frefedSequence = new Vector<SeqFref>();
+      frefedSequence = new Vector<>();
     }
 
     viewportsAdded.clear();
index 2340283..22ffcae 100755 (executable)
@@ -25,13 +25,13 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.util.MessageManager;
 
 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;
 
 /**
@@ -66,7 +66,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;
 
@@ -251,13 +251,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());
       }
     }
   }
@@ -272,12 +270,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)
     {
@@ -285,12 +283,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)
     {
@@ -304,9 +302,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;
   }
 
@@ -319,7 +317,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++)
     {
@@ -390,7 +388,7 @@ public abstract class AlignFile extends FileParse
   {
     if (newickStrings == null)
     {
-      newickStrings = new Vector<String[]>();
+      newickStrings = new Vector<>();
     }
     newickStrings.addElement(new String[] { treeName, newickString });
   }
index 6a7d1c1..c1d6129 100644 (file)
  */
 package jalview.io;
 
-import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 
-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 +35,8 @@ import java.util.Hashtable;
  */
 public class AlignmentProperties
 {
+  private static final String BR_TAG = "<br>";
+  private static final String NEWLINE = System.getProperty("line.separator");
   AlignmentI alignment;
 
   public AlignmentProperties(AlignmentI alignment)
@@ -47,12 +47,12 @@ public class AlignmentProperties
   /**
    * render the alignment's properties report as text or an HTML fragment
    * 
-   * @param pw
    * @param html
    */
-  protected void writeProperties(PrintWriter pw, boolean html)
+  protected StringBuilder writeProperties(boolean html)
   {
-    final String nl = html ? "<br>" : System.getProperty("line.separator");
+    StringBuilder sb = new StringBuilder(256);
+    final String nl = html ? BR_TAG : NEWLINE;
     float avg = 0;
     int min = Integer.MAX_VALUE, max = 0;
     for (int i = 0; i < alignment.getHeight(); i++)
@@ -70,77 +70,122 @@ public class AlignmentProperties
       }
     }
     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);
+    sb.append(nl);
+    sb.append("Sequences: " + alignment.getHeight());
+    sb.append(nl);
+    sb.append("Minimum Sequence Length: " + min);
+    sb.append(nl);
+    sb.append("Maximum Sequence Length: " + max);
+    sb.append(nl);
+    sb.append("Average Length: " + (int) avg);
 
-    if (((Alignment) alignment).alignmentProperties != null)
+    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 +193,9 @@ public class AlignmentProperties
    * 
    * @return
    */
-  public StringBuffer formatAsHtml()
+  public StringBuilder formatAsHtml()
   {
-    return formatReport(true);
+    return writeProperties(true);
   }
 
 }
index 00476d6..c6b4bc7 100755 (executable)
@@ -141,8 +141,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)
     {
@@ -446,14 +446,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 f5b5177..654d593 100644 (file)
@@ -46,6 +46,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;
@@ -197,7 +198,7 @@ public class StockholmFile extends AlignFile
     String version;
     // String id;
     Hashtable seqAnn = new Hashtable(); // Sequence related annotations
-    LinkedHashMap<String, String> seqs = new LinkedHashMap<String, String>();
+    LinkedHashMap<String, String> seqs = new LinkedHashMap<>();
     Regex p, r, rend, s, x;
     // Temporary line for processing RNA annotation
     // String RNAannot = "";
@@ -658,7 +659,7 @@ public class StockholmFile extends AlignFile
               strucAnn = new Hashtable();
             }
 
-            Vector<AlignmentAnnotation> newStruc = new Vector<AlignmentAnnotation>();
+            Vector<AlignmentAnnotation> newStruc = new Vector<>();
             parseAnnotationRow(newStruc, type, ns);
             for (AlignmentAnnotation alan : newStruc)
             {
@@ -710,7 +711,7 @@ public class StockholmFile extends AlignFile
   private void guessDatabaseFor(Sequence seqO, String dbr, String dbsource)
   {
     DBRefEntry dbrf = null;
-    List<DBRefEntry> dbrs = new ArrayList<DBRefEntry>();
+    List<DBRefEntry> dbrs = new ArrayList<>();
     String seqdb = "Unknown", sdbac = "" + dbr;
     int st = -1, en = -1, p;
     if ((st = sdbac.indexOf("/")) > -1)
@@ -961,15 +962,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);
       }
     }