JAL-437 remove code duplicated by merge
[jalview.git] / src / jalview / gui / FeatureSettings.java
index fedfe3f..6957ea4 100644 (file)
  */
 package jalview.gui;
 
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.FILTER_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
-
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
-import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.gui.Help.HelpId;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.schemabinding.version2.Filter;
 import jalview.schemabinding.version2.JalviewUserColours;
+import jalview.schemabinding.version2.MatcherSet;
 import jalview.schemes.FeatureColour;
-import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.util.QuickSort;
-import jalview.util.matcher.KeyedMatcherSet;
-import jalview.util.matcher.KeyedMatcherSetI;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 import jalview.ws.DasSequenceFeatureFetcher;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 
@@ -70,6 +66,8 @@ import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
@@ -109,16 +107,26 @@ import javax.swing.table.TableColumn;
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
-  private static final int COLUMN_COUNT = 4;
+  private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
+          .getString("label.sequence_feature_colours");
+
+  /*
+   * column indices of fields in Feature Settings table
+   */
+  static final int TYPE_COLUMN = 0;
+
+  static final int COLOUR_COLUMN = 1;
+
+  static final int FILTER_COLUMN = 2;
+
+  static final int SHOW_COLUMN = 3;
 
-  private static final String COLON = ":";
+  private static final int COLUMN_COUNT = 4;
 
   private static final int MIN_WIDTH = 400;
 
   private static final int MIN_HEIGHT = 400;
 
-  private static final int MAX_TOOLTIP_LENGTH = 50;
-
   DasSourceBrowser dassourceBrowser;
 
   DasSequenceFeatureFetcher dasFeatureFetcher;
@@ -136,7 +144,7 @@ public class FeatureSettings extends JPanel
 
   private float originalTransparency;
 
-  private Map<String, KeyedMatcherSetI> originalFilters;
+  private Map<String, FeatureMatcherSetI> originalFilters;
 
   final JInternalFrame frame;
 
@@ -188,7 +196,7 @@ public class FeatureSettings extends JPanel
     int originalTransparencyAsPercent = (int) (originalTransparency * 100);
     transparency.setMaximum(100 - originalTransparencyAsPercent);
 
-    originalFilters = fr.getFeatureFilters();
+    originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
 
     try
     {
@@ -213,7 +221,7 @@ public class FeatureSettings extends JPanel
           break;
         case FILTER_COLUMN:
           int row = table.rowAtPoint(e.getPoint());
-          KeyedMatcherSet o = (KeyedMatcherSet) table.getValueAt(row,
+          FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
                   column);
           tip = o.isEmpty()
                   ? MessageManager.getString("label.filters_tooltip")
@@ -234,8 +242,8 @@ public class FeatureSettings extends JPanel
     table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
     table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
 
-    table.setDefaultEditor(KeyedMatcherSet.class, new FilterEditor(this));
-    table.setDefaultRenderer(KeyedMatcherSet.class, new FilterRenderer());
+    table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor(this));
+    table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
 
     TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
             new ColorRenderer(), new ColorEditor(this));
@@ -651,8 +659,7 @@ public class FeatureSettings extends JPanel
       }
     }
 
-    int columnCount = COLUMN_COUNT;
-    Object[][] data = new Object[displayableTypes.size()][columnCount];
+    Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
     int dataIndex = 0;
 
     if (fr.hasRenderOrder())
@@ -677,9 +684,9 @@ public class FeatureSettings extends JPanel
 
         data[dataIndex][TYPE_COLUMN] = type;
         data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
-        KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+        FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
         data[dataIndex][FILTER_COLUMN] = featureFilter == null
-                ? new KeyedMatcherSet()
+                ? new FeatureMatcherSet()
                 : featureFilter;
         data[dataIndex][SHOW_COLUMN] = new Boolean(
                 af.getViewport().getFeaturesDisplayed().isVisible(type));
@@ -704,9 +711,9 @@ public class FeatureSettings extends JPanel
         fr.clearRenderOrder();
         return;
       }
-      KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+      FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
       data[dataIndex][FILTER_COLUMN] = featureFilter == null
-              ? new KeyedMatcherSet()
+              ? new FeatureMatcherSet()
               : featureFilter;
       data[dataIndex][SHOW_COLUMN] = new Boolean(true);
       dataIndex++;
@@ -715,11 +722,10 @@ public class FeatureSettings extends JPanel
 
     if (originalData == null)
     {
-      int size = data[0].length;
-      originalData = new Object[data.length][size];
+      originalData = new Object[data.length][COLUMN_COUNT];
       for (int i = 0; i < data.length; i++)
       {
-        System.arraycopy(data[i], 0, originalData[i], 0, size);
+        System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
       }
     }
     else
@@ -790,11 +796,12 @@ public class FeatureSettings extends JPanel
         /*
          * new feature detected - add to original data (on top)
          */
-        int size = currentData[0].length;
-        Object[][] newData = new Object[originalData.length + 1][size];
+        Object[][] newData = new Object[originalData.length
+                + 1][COLUMN_COUNT];
         for (int i = 0; i < originalData.length; i++)
         {
-          System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
+          System.arraycopy(originalData[i], 0, newData[i + 1], 0,
+                  COLUMN_COUNT);
         }
         newData[0] = row;
         originalData = newData;
@@ -848,10 +855,14 @@ public class FeatureSettings extends JPanel
     }
   }
 
+  /**
+   * Offers a file chooser dialog, and then loads the feature colours and
+   * filters from file in XML format and unmarshals to Jalview feature settings
+   */
   void load()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.load_feature_colours"));
@@ -862,88 +873,78 @@ public class FeatureSettings extends JPanel
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       File file = chooser.getSelectedFile();
+      load(file);
+    }
+  }
 
-      try
-      {
-        InputStreamReader in = new InputStreamReader(
-                new FileInputStream(file), "UTF-8");
+  /**
+   * Loads feature colours and filters from XML stored in the given file
+   * 
+   * @param file
+   */
+  void load(File file)
+  {
+    try
+    {
+      InputStreamReader in = new InputStreamReader(
+              new FileInputStream(file), "UTF-8");
 
-        JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
+      JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
 
-        for (int i = jucs.getColourCount() - 1; i >= 0; i--)
-        {
-          String name;
-          jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
-          if (newcol.hasMax())
-          {
-            Color mincol = null, maxcol = null;
-            try
-            {
-              mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
-              maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
+      /*
+       * load feature colours
+       */
+      for (int i = jucs.getColourCount() - 1; i >= 0; i--)
+      {
+        jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
+        FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
+        fr.setColour(newcol.getName(), colour);
+        fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
+      }
 
-            } catch (Exception e)
-            {
-              Cache.log.warn("Couldn't parse out graduated feature color.",
-                      e);
-            }
-            FeatureColourI gcol = new FeatureColour(mincol, maxcol,
-                    newcol.getMin(), newcol.getMax());
-            if (newcol.hasAutoScale())
-            {
-              gcol.setAutoScaled(newcol.getAutoScale());
-            }
-            if (newcol.hasColourByLabel())
-            {
-              gcol.setColourByLabel(newcol.getColourByLabel());
-            }
-            if (newcol.hasThreshold())
-            {
-              gcol.setThreshold(newcol.getThreshold());
-            }
-            if (newcol.getThreshType().length() > 0)
-            {
-              String ttyp = newcol.getThreshType();
-              if (ttyp.equalsIgnoreCase("ABOVE"))
-              {
-                gcol.setAboveThreshold(true);
-              }
-              if (ttyp.equalsIgnoreCase("BELOW"))
-              {
-                gcol.setBelowThreshold(true);
-              }
-            }
-            fr.setColour(name = newcol.getName(), gcol);
-          }
-          else
-          {
-            Color color = new Color(
-                    Integer.parseInt(jucs.getColour(i).getRGB(), 16));
-            fr.setColour(name = jucs.getColour(i).getName(),
-                    new FeatureColour(color));
-          }
-          fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
-        }
-        if (table != null)
+      /*
+       * load feature filters; loaded filters will replace any that are
+       * currently defined, other defined filters are left unchanged 
+       */
+      for (int i = 0; i < jucs.getFilterCount(); i++)
+      {
+        jalview.schemabinding.version2.Filter filterModel = jucs
+                .getFilter(i);
+        String featureType = filterModel.getFeatureType();
+        FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
+                filterModel.getMatcherSet());
+        if (!filter.isEmpty())
         {
-          resetTable(null);
-          Object[][] data = ((FeatureTableModel) table.getModel())
-                  .getData();
-          ensureOrder(data);
-          updateFeatureRenderer(data, false);
-          table.repaint();
+          fr.setFeatureFilter(featureType, filter);
         }
-      } catch (Exception ex)
+      }
+
+      /*
+       * update feature settings table
+       */
+      if (table != null)
       {
-        System.out.println("Error loading User Colour File\n" + ex);
+        resetTable(null);
+        Object[][] data = ((FeatureTableModel) table.getModel())
+                .getData();
+        ensureOrder(data);
+        updateFeatureRenderer(data, false);
+        table.repaint();
       }
+    } catch (Exception ex)
+    {
+      System.out.println("Error loading User Colour File\n" + ex);
     }
   }
 
+  /**
+   * Offers a file chooser dialog, and then saves the current feature colours
+   * and any filters to the selected file in XML format
+   */
   void save()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.save_feature_colours"));
@@ -953,68 +954,87 @@ public class FeatureSettings extends JPanel
 
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
-      ucs.setSchemeName("Sequence Features");
-      try
-      {
-        PrintWriter out = new PrintWriter(new OutputStreamWriter(
-                new FileOutputStream(choice), "UTF-8"));
+      save(chooser.getSelectedFile());
+    }
+  }
+
+  /**
+   * Saves feature colours and filters to the given file
+   * 
+   * @param file
+   */
+  void save(File file)
+  {
+    JalviewUserColours ucs = new JalviewUserColours();
+    ucs.setSchemeName("Sequence Features");
+    try
+    {
+      PrintWriter out = new PrintWriter(new OutputStreamWriter(
+              new FileOutputStream(file), "UTF-8"));
 
-        Set<String> fr_colours = fr.getAllFeatureColours();
-        Iterator<String> e = fr_colours.iterator();
-        float[] sortOrder = new float[fr_colours.size()];
-        String[] sortTypes = new String[fr_colours.size()];
-        int i = 0;
-        while (e.hasNext())
+      /*
+       * sort feature types by colour order, from 0 (highest)
+       * to 1 (lowest)
+       */
+      Set<String> fr_colours = fr.getAllFeatureColours();
+      String[] sortedTypes = fr_colours
+              .toArray(new String[fr_colours.size()]);
+      Arrays.sort(sortedTypes, new Comparator<String>()
+      {
+        @Override
+        public int compare(String type1, String type2)
         {
-          sortTypes[i] = e.next();
-          sortOrder[i] = fr.getOrder(sortTypes[i]);
-          i++;
+          return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
         }
-        QuickSort.sort(sortOrder, sortTypes);
-        sortOrder = null;
-        for (i = 0; i < sortTypes.length; i++)
+      });
+
+      /*
+       * save feature colours
+       */
+      for (String featureType : sortedTypes)
+      {
+        FeatureColourI fcol = fr.getFeatureStyle(featureType);
+        jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
+                featureType, fcol);
+        ucs.addColour(col);
+      }
+
+      /*
+       * save any feature filters
+       */
+      for (String featureType : sortedTypes)
+      {
+        FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+        if (filter != null && !filter.isEmpty())
         {
-          jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
-          col.setName(sortTypes[i]);
-          FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
-          if (fcol.isSimpleColour())
-          {
-            col.setRGB(Format.getHexString(fcol.getColour()));
-          }
-          else
-          {
-            col.setRGB(Format.getHexString(fcol.getMaxColour()));
-            col.setMin(fcol.getMin());
-            col.setMax(fcol.getMax());
-            col.setMinRGB(
-                    jalview.util.Format.getHexString(fcol.getMinColour()));
-            col.setAutoScale(fcol.isAutoScaled());
-            col.setThreshold(fcol.getThreshold());
-            col.setColourByLabel(fcol.isColourByLabel());
-            col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
-                    : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
-          }
-          ucs.addColour(col);
+          Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
+          FeatureMatcherI firstMatcher = iterator.next();
+          MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
+                  filter.isAnded());
+          Filter filterModel = new Filter();
+          filterModel.setFeatureType(featureType);
+          filterModel.setMatcherSet(ms);
+          ucs.addFilter(filterModel);
         }
-        ucs.marshal(out);
-        out.close();
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
       }
+
+      ucs.marshal(out);
+      out.close();
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
     }
   }
 
   public void invertSelection()
   {
-    for (int i = 0; i < table.getRowCount(); i++)
+    Object[][] data = ((FeatureTableModel) table.getModel()).getData();
+    for (int i = 0; i < data.length; i++)
     {
-      Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
-
-      table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
+      data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
     }
+    updateFeatureRenderer(data, true);
+    table.repaint();
   }
 
   public void orderByAvWidth()
@@ -1105,12 +1125,32 @@ public class FeatureSettings extends JPanel
    */
   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
   {
-    if (fr.setFeaturePriority(data, visibleNew))
+    FeatureSettingsBean[] rowData = getTableAsBeans(data);
+
+    if (fr.setFeaturePriority(rowData, visibleNew))
     {
       af.alignPanel.paintAlignment(true, true);
     }
   }
 
+  /**
+   * Converts table data into an array of data beans
+   */
+  private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
+  {
+    FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
+    for (int i = 0; i < data.length; i++)
+    {
+      String type = (String) data[i][TYPE_COLUMN];
+      FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
+      FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
+      Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
+      rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
+              isShown);
+    }
+    return rowData;
+  }
+
   private void jbInit() throws Exception
   {
     this.setLayout(new BorderLayout());
@@ -1189,22 +1229,6 @@ public class FeatureSettings extends JPanel
         }
       }
     });
-    help.setFont(JvSwingUtils.getLabelFont());
-    help.setText(MessageManager.getString("action.help"));
-    help.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        try
-        {
-          Help.showHelpWindow(HelpId.SequenceFeatureSettings);
-        } catch (HelpSetException e1)
-        {
-          e1.printStackTrace();
-        }
-      }
-    });
 
     JButton cancel = new JButton(MessageManager.getString("action.cancel"));
     cancel.setFont(JvSwingUtils.getLabelFont());
@@ -1234,6 +1258,8 @@ public class FeatureSettings extends JPanel
     JButton loadColours = new JButton(
             MessageManager.getString("label.load_colours"));
     loadColours.setFont(JvSwingUtils.getLabelFont());
+    loadColours.setToolTipText(
+            MessageManager.getString("label.load_colours_tooltip"));
     loadColours.addActionListener(new ActionListener()
     {
       @Override
@@ -1246,6 +1272,8 @@ public class FeatureSettings extends JPanel
     JButton saveColours = new JButton(
             MessageManager.getString("label.save_colours"));
     saveColours.setFont(JvSwingUtils.getLabelFont());
+    saveColours.setToolTipText(
+            MessageManager.getString("label.save_colours_tooltip"));
     saveColours.addActionListener(new ActionListener()
     {
       @Override
@@ -1492,7 +1520,6 @@ public class FeatureSettings extends JPanel
   // ///////////////////////////////////////////////////////////////////////
   class FeatureTableModel extends AbstractTableModel
   {
-
     private String[] columnNames = {
         MessageManager.getString("label.feature_type"),
         MessageManager.getString("action.colour"),
@@ -1647,7 +1674,7 @@ public class FeatureSettings extends JPanel
             Object filter, boolean isSelected, boolean hasFocus, int row,
             int column)
     {
-      KeyedMatcherSetI theFilter = (KeyedMatcherSetI) filter;
+      FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
       setOpaque(true);
       String asText = theFilter.toString();
       setBackground(tbl.getBackground());
@@ -1859,10 +1886,10 @@ public class FeatureSettings extends JPanel
            * update table data without triggering updateFeatureRenderer
            */
           currentColor = fr.getFeatureColours().get(type);
-          KeyedMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
+          FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
           if (currentFilter == null)
           {
-            currentFilter = new KeyedMatcherSet();
+            currentFilter = new FeatureMatcherSet();
           }
           Object[] data = ((FeatureTableModel) table.getModel())
                   .getData()[rowSelected];
@@ -1921,7 +1948,7 @@ public class FeatureSettings extends JPanel
   {
     FeatureSettings me;
 
-    KeyedMatcherSetI currentFilter;
+    FeatureMatcherSetI currentFilter;
 
     Point lastLocation;
 
@@ -1976,7 +2003,7 @@ public class FeatureSettings extends JPanel
         currentFilter = me.fr.getFeatureFilter(type);
         if (currentFilter == null)
         {
-          currentFilter = new KeyedMatcherSet();
+          currentFilter = new FeatureMatcherSet();
         }
         Object[] data = ((FeatureTableModel) table.getModel())
                 .getData()[rowSelected];
@@ -1997,7 +2024,7 @@ public class FeatureSettings extends JPanel
     public Component getTableCellEditorComponent(JTable theTable, Object value,
             boolean isSelected, int row, int column)
     {
-      currentFilter = (KeyedMatcherSetI) value;
+      currentFilter = (FeatureMatcherSetI) value;
       this.rowSelected = row;
       type = me.table.getValueAt(row, TYPE_COLUMN).toString();
       button.setOpaque(true);