JAL-3116 parse EMBL XML with JAXB (todo: update unit tests)
[jalview.git] / src / jalview / gui / Jalview2XML.java
index c7ec757..87b8d87 100644 (file)
@@ -37,6 +37,10 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
 import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.ext.varna.RnaModel;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
@@ -48,6 +52,8 @@ import jalview.schemabinding.version2.Annotation;
 import jalview.schemabinding.version2.AnnotationColours;
 import jalview.schemabinding.version2.AnnotationElement;
 import jalview.schemabinding.version2.CalcIdParam;
+import jalview.schemabinding.version2.Colour;
+import jalview.schemabinding.version2.CompoundMatcher;
 import jalview.schemabinding.version2.DBRef;
 import jalview.schemabinding.version2.Features;
 import jalview.schemabinding.version2.Group;
@@ -60,6 +66,8 @@ import jalview.schemabinding.version2.MapListFrom;
 import jalview.schemabinding.version2.MapListTo;
 import jalview.schemabinding.version2.Mapping;
 import jalview.schemabinding.version2.MappingChoice;
+import jalview.schemabinding.version2.MatchCondition;
+import jalview.schemabinding.version2.MatcherSet;
 import jalview.schemabinding.version2.OtherData;
 import jalview.schemabinding.version2.PdbentryItem;
 import jalview.schemabinding.version2.Pdbids;
@@ -75,6 +83,9 @@ import jalview.schemabinding.version2.ThresholdLine;
 import jalview.schemabinding.version2.Tree;
 import jalview.schemabinding.version2.UserColours;
 import jalview.schemabinding.version2.Viewport;
+import jalview.schemabinding.version2.types.ColourThreshTypeType;
+import jalview.schemabinding.version2.types.FeatureMatcherByType;
+import jalview.schemabinding.version2.types.NoValueColour;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
@@ -83,10 +94,12 @@ import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.util.jarInputStreamProvider;
+import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
@@ -115,6 +128,7 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -879,15 +893,33 @@ public class Jalview2XML
         }
         if (sf.otherDetails != null)
         {
-          String key;
-          Iterator<String> keys = sf.otherDetails.keySet().iterator();
-          while (keys.hasNext())
+          /*
+           * save feature attributes, which may be simple strings or
+           * map valued (have sub-attributes)
+           */
+          for (Entry<String, Object> entry : sf.otherDetails.entrySet())
           {
-            key = keys.next();
-            OtherData keyValue = new OtherData();
-            keyValue.setKey(key);
-            keyValue.setValue(sf.otherDetails.get(key).toString());
-            features.addOtherData(keyValue);
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof Map<?, ?>)
+            {
+              for (Entry<String, Object> subAttribute : ((Map<String, Object>) value)
+                      .entrySet())
+              {
+                OtherData otherData = new OtherData();
+                otherData.setKey(key);
+                otherData.setKey2(subAttribute.getKey());
+                otherData.setValue(subAttribute.getValue().toString());
+                features.addOtherData(otherData);
+              }
+            }
+            else
+            {
+              OtherData otherData = new OtherData();
+              otherData.setKey(key);
+              otherData.setValue(value.toString());
+              features.addOtherData(otherData);
+            }
           }
         }
 
@@ -1070,13 +1102,13 @@ public class Jalview2XML
           {
             TreePanel tp = (TreePanel) frames[t];
 
-            if (tp.treeCanvas.av.getAlignment() == jal)
+            if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
             {
               Tree tree = new Tree();
               tree.setTitle(tp.getTitle());
               tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
               tree.setNewick(tp.getTree().print());
-              tree.setThreshold(tp.treeCanvas.threshold);
+              tree.setThreshold(tp.getTreeCanvas().getThreshold());
 
               tree.setFitToWindow(tp.fitToWindow.getState());
               tree.setFontName(tp.getTreeFont().getName());
@@ -1215,7 +1247,7 @@ public class Jalview2XML
       {
         view.setComplementId(av.getCodingComplement().getViewId());
       }
-      view.setViewName(av.viewName);
+      view.setViewName(av.getViewName());
       view.setGatheredViews(av.isGatherViewsHere());
 
       Rectangle size = ap.av.getExplodedGeometry();
@@ -1313,19 +1345,33 @@ public class Jalview2XML
       {
         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
 
-        String[] renderOrder = ap.getSeqPanel().seqCanvas
-                .getFeatureRenderer().getRenderOrder()
-                .toArray(new String[0]);
+        FeatureRenderer fr = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer();
+        String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
 
         Vector<String> settingsAdded = new Vector<>();
         if (renderOrder != null)
         {
           for (String featureType : renderOrder)
           {
-            FeatureColourI fcol = ap.getSeqPanel().seqCanvas
-                    .getFeatureRenderer().getFeatureStyle(featureType);
             Setting setting = new Setting();
             setting.setType(featureType);
+
+            /*
+             * save any filter for the feature type
+             */
+            FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+            if (filter != null)  {
+              Iterator<FeatureMatcherI> filters = filter.getMatchers().iterator();
+              FeatureMatcherI firstFilter = filters.next();
+              setting.setMatcherSet(Jalview2XML.marshalFilter(
+                      firstFilter, filters, filter.isAnded()));
+            }
+
+            /*
+             * save colour scheme for the feature type
+             */
+            FeatureColourI fcol = fr.getFeatureStyle(featureType);
             if (!fcol.isSimpleColour())
             {
               setting.setColour(fcol.getMaxColour().getRGB());
@@ -1333,8 +1379,25 @@ public class Jalview2XML
               setting.setMin(fcol.getMin());
               setting.setMax(fcol.getMax());
               setting.setColourByLabel(fcol.isColourByLabel());
+              if (fcol.isColourByAttribute())
+              {
+                setting.setAttributeName(fcol.getAttributeName());
+              }
               setting.setAutoScale(fcol.isAutoScaled());
               setting.setThreshold(fcol.getThreshold());
+              Color noColour = fcol.getNoColour();
+              if (noColour == null)
+              {
+                setting.setNoValueColour(NoValueColour.NONE);
+              }
+              else if (noColour.equals(fcol.getMaxColour()))
+              {
+                setting.setNoValueColour(NoValueColour.MAX);
+              }
+              else
+              {
+                setting.setNoValueColour(NoValueColour.MIN);
+              }
               // -1 = No threshold, 0 = Below, 1 = Above
               setting.setThreshstate(fcol.isAboveThreshold() ? 1
                       : (fcol.isBelowThreshold() ? 0 : -1));
@@ -1346,7 +1409,7 @@ public class Jalview2XML
 
             setting.setDisplay(
                     av.getFeaturesDisplayed().isVisible(featureType));
-            float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+            float rorder = fr
                     .getOrder(featureType);
             if (rorder > -1)
             {
@@ -1358,8 +1421,7 @@ public class Jalview2XML
         }
 
         // is groups actually supposed to be a map here ?
-        Iterator<String> en = ap.getSeqPanel().seqCanvas
-                .getFeatureRenderer().getFeatureGroups().iterator();
+        Iterator<String> en = fr.getFeatureGroups().iterator();
         Vector<String> groupsAdded = new Vector<>();
         while (en.hasNext())
         {
@@ -1370,8 +1432,7 @@ public class Jalview2XML
           }
           Group g = new Group();
           g.setName(grp);
-          g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
-                  .getFeatureRenderer().checkGroupVisibility(grp, false))
+          g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
                           .booleanValue());
           fs.addGroup(g);
           groupsAdded.addElement(grp);
@@ -2963,19 +3024,46 @@ public class Jalview2XML
                     features[f].getEnd(), features[f].getScore(),
                     features[f].getFeatureGroup());
             sf.setStatus(features[f].getStatus());
+
+            /*
+             * load any feature attributes - include map-valued attributes
+             */
+            Map<String, Map<String, String>> mapAttributes = new HashMap<>();
             for (int od = 0; od < features[f].getOtherDataCount(); od++)
             {
               OtherData keyValue = features[f].getOtherData(od);
-              if (keyValue.getKey().startsWith("LINK"))
+              String attributeName = keyValue.getKey();
+              String attributeValue = keyValue.getValue();
+              if (attributeName.startsWith("LINK"))
               {
-                sf.addLink(keyValue.getValue());
+                sf.addLink(attributeValue);
               }
               else
               {
-                sf.setValue(keyValue.getKey(), keyValue.getValue());
+                String subAttribute = keyValue.getKey2();
+                if (subAttribute == null)
+                {
+                  // simple string-valued attribute
+                  sf.setValue(attributeName, attributeValue);
+                }
+                else
+                {
+                  // attribute 'key' has sub-attribute 'key2'
+                  if (!mapAttributes.containsKey(attributeName))
+                  {
+                    mapAttributes.put(attributeName, new HashMap<>());
+                  }
+                  mapAttributes.get(attributeName).put(subAttribute,
+                          attributeValue);
+                }
               }
-
             }
+            for (Entry<String, Map<String, String>> mapAttribute : mapAttributes
+                    .entrySet())
+            {
+              sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
+            }
+
             // adds feature to datasequence's feature set (since Jalview 2.10)
             al.getSequenceAt(i).addSequenceFeature(sf);
           }
@@ -3675,11 +3763,11 @@ public class Jalview2XML
           tp.setTitle(tree.getTitle());
           tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(),
                   tree.getWidth(), tree.getHeight()));
-          tp.av = av; // af.viewport; // TODO: verify 'associate with all
+          tp.setViewport(av); // af.viewport; // TODO: verify 'associate with all
           // views'
           // works still
-          tp.treeCanvas.av = av; // af.viewport;
-          tp.treeCanvas.ap = ap; // af.alignPanel;
+          tp.getTreeCanvas().setViewport(av); // af.viewport;
+          tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
 
         }
         if (tp == null)
@@ -3707,7 +3795,7 @@ public class Jalview2XML
         tp.showBootstrap(tree.getShowBootstrap());
         tp.showDistances(tree.getShowDistances());
 
-        tp.treeCanvas.threshold = tree.getThreshold();
+        tp.getTreeCanvas().setThreshold(tree.getThreshold());
 
         if (tree.getCurrentTree())
         {
@@ -4440,7 +4528,7 @@ public class Jalview2XML
 
     if (view.getViewName() != null)
     {
-      af.viewport.viewName = view.getViewName();
+      af.viewport.setViewName(view.getViewName());
       af.setInitialTabVisible();
     }
     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
@@ -4551,9 +4639,11 @@ public class Jalview2XML
       af.viewport.setShowGroupConservation(false);
     }
 
-    // recover featre settings
+    // recover feature settings
     if (jms.getFeatureSettings() != null)
     {
+      FeatureRenderer fr = af.alignPanel.getSeqPanel().seqCanvas
+              .getFeatureRenderer();
       FeaturesDisplayed fdi;
       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
@@ -4565,14 +4655,51 @@ public class Jalview2XML
               .getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
+        String featureType = setting.getType();
+
+        /*
+         * restore feature filters (if any)
+         */
+        MatcherSet filters = setting.getMatcherSet();
+        if (filters != null)
+        {
+          FeatureMatcherSetI filter = Jalview2XML
+                  .unmarshalFilter(featureType, filters);
+          if (!filter.isEmpty())
+          {
+            fr.setFeatureFilter(featureType, filter);
+          }
+        }
+
+        /*
+         * restore feature colour scheme
+         */
+        Color maxColour = new Color(setting.getColour());
         if (setting.hasMincolour())
         {
-          FeatureColourI gc = setting.hasMin()
-                  ? new FeatureColour(new Color(setting.getMincolour()),
-                          new Color(setting.getColour()), setting.getMin(),
-                          setting.getMax())
-                  : new FeatureColour(new Color(setting.getMincolour()),
-                          new Color(setting.getColour()), 0, 1);
+          /*
+           * minColour is always set unless a simple colour
+           * (including for colour by label though it doesn't use it)
+           */
+          Color minColour = new Color(setting.getMincolour());
+          Color noValueColour = minColour;
+          NoValueColour noColour = setting.getNoValueColour();
+          if (noColour == NoValueColour.NONE)
+          {
+            noValueColour = null;
+          }
+          else if (noColour == NoValueColour.MAX)
+          {
+            noValueColour = maxColour;
+          }
+          float min = setting.hasMin() ? setting.getMin() : 0f;
+          float max = setting.hasMin() ? setting.getMax() : 1f;
+          FeatureColourI gc = new FeatureColour(minColour, maxColour,
+                  noValueColour, min, max);
+          if (setting.getAttributeNameCount() > 0)
+          {
+            gc.setAttributeName(setting.getAttributeName());
+          }
           if (setting.hasThreshold())
           {
             gc.setThreshold(setting.getThreshold());
@@ -4597,26 +4724,26 @@ public class Jalview2XML
             gc.setColourByLabel(setting.getColourByLabel());
           }
           // and put in the feature colour table.
-          featureColours.put(setting.getType(), gc);
+          featureColours.put(featureType, gc);
         }
         else
         {
-          featureColours.put(setting.getType(),
-                  new FeatureColour(new Color(setting.getColour())));
+          featureColours.put(featureType,
+                  new FeatureColour(maxColour));
         }
-        renderOrder[fs] = setting.getType();
+        renderOrder[fs] = featureType;
         if (setting.hasOrder())
         {
-          featureOrder.put(setting.getType(), setting.getOrder());
+          featureOrder.put(featureType, setting.getOrder());
         }
         else
         {
-          featureOrder.put(setting.getType(), new Float(
+          featureOrder.put(featureType, new Float(
                   fs / jms.getFeatureSettings().getSettingCount()));
         }
         if (setting.getDisplay())
         {
-          fdi.setVisible(setting.getType());
+          fdi.setVisible(featureType);
         }
       }
       Map<String, Boolean> fgtable = new Hashtable<>();
@@ -4630,9 +4757,7 @@ public class Jalview2XML
       // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
       FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
               fgtable, featureColours, 1.0f, featureOrder);
-      af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
-              .transferSettings(frs);
-
+      fr.transferSettings(frs);
     }
 
     if (view.getHiddenColumnsCount() > 0)
@@ -5334,7 +5459,7 @@ public class Jalview2XML
 
     if (this.frefedSequence == null)
     {
-      frefedSequence = new Vector<SeqFref>();
+      frefedSequence = new Vector<>();
     }
 
     viewportsAdded.clear();
@@ -5586,4 +5711,289 @@ public class Jalview2XML
   {
     return counter++;
   }
+
+  /**
+   * Populates an XML model of the feature colour scheme for one feature type
+   * 
+   * @param featureType
+   * @param fcol
+   * @return
+   */
+  protected static jalview.schemabinding.version2.Colour marshalColour(
+          String featureType, FeatureColourI fcol)
+  {
+    jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
+    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() ? ColourThreshTypeType.ABOVE
+              : (fcol.isBelowThreshold() ? ColourThreshTypeType.BELOW
+                      : ColourThreshTypeType.NONE));
+      if (fcol.isColourByAttribute())
+      {
+        col.setAttributeName(fcol.getAttributeName());
+      }
+      Color noColour = fcol.getNoColour();
+      if (noColour == null)
+      {
+        col.setNoValueColour(NoValueColour.NONE);
+      }
+      else if (noColour == fcol.getMaxColour())
+      {
+        col.setNoValueColour(NoValueColour.MAX);
+      }
+      else
+      {
+        col.setNoValueColour(NoValueColour.MIN);
+      }
+    }
+    col.setName(featureType);
+    return col;
+  }
+
+  /**
+   * Populates an XML model of the feature filter(s) for one feature type
+   * 
+   * @param firstMatcher
+   *          the first (or only) match condition)
+   * @param filter
+   *          remaining match conditions (if any)
+   * @param and
+   *          if true, conditions are and-ed, else or-ed
+   */
+  protected static MatcherSet marshalFilter(FeatureMatcherI firstMatcher,
+          Iterator<FeatureMatcherI> filters, boolean and)
+  {
+    MatcherSet result = new MatcherSet();
+  
+    if (filters.hasNext())
+    {
+      /*
+       * compound matcher
+       */
+      CompoundMatcher compound = new CompoundMatcher();
+      compound.setAnd(and);
+      MatcherSet matcher1 = marshalFilter(firstMatcher,
+              Collections.emptyIterator(), and);
+      compound.addMatcherSet(matcher1);
+      FeatureMatcherI nextMatcher = filters.next();
+      MatcherSet matcher2 = marshalFilter(nextMatcher, filters, and);
+      compound.addMatcherSet(matcher2);
+      result.setCompoundMatcher(compound);
+    }
+    else
+    {
+      /*
+       * single condition matcher
+       */
+      MatchCondition matcherModel = new MatchCondition();
+      matcherModel.setCondition(
+              firstMatcher.getMatcher().getCondition().getStableName());
+      matcherModel.setValue(firstMatcher.getMatcher().getPattern());
+      if (firstMatcher.isByAttribute())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYATTRIBUTE);
+        matcherModel.setAttributeName(firstMatcher.getAttribute());
+      }
+      else if (firstMatcher.isByLabel())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYLABEL);
+      }
+      else if (firstMatcher.isByScore())
+      {
+        matcherModel.setBy(FeatureMatcherByType.BYSCORE);
+      }
+      result.setMatchCondition(matcherModel);
+    }
+  
+    return result;
+  }
+
+  /**
+   * Loads one XML model of a feature filter to a Jalview object
+   * 
+   * @param featureType
+   * @param matcherSetModel
+   * @return
+   */
+  protected static FeatureMatcherSetI unmarshalFilter(
+          String featureType, MatcherSet matcherSetModel)
+  {
+    FeatureMatcherSetI result = new FeatureMatcherSet();
+    try
+    {
+      unmarshalFilterConditions(result, matcherSetModel, true);
+    } catch (IllegalStateException e)
+    {
+      // mixing AND and OR conditions perhaps
+      System.err.println(
+              String.format("Error reading filter conditions for '%s': %s",
+                      featureType, e.getMessage()));
+      // return as much as was parsed up to the error
+    }
+  
+    return result;
+  }
+
+  /**
+   * Adds feature match conditions to matcherSet as unmarshalled from XML
+   * (possibly recursively for compound conditions)
+   * 
+   * @param matcherSet
+   * @param matcherSetModel
+   * @param and
+   *          if true, multiple conditions are AND-ed, else they are OR-ed
+   * @throws IllegalStateException
+   *           if AND and OR conditions are mixed
+   */
+  protected static void unmarshalFilterConditions(
+          FeatureMatcherSetI matcherSet, MatcherSet matcherSetModel,
+          boolean and)
+  {
+    MatchCondition mc = matcherSetModel.getMatchCondition();
+    if (mc != null)
+    {
+      /*
+       * single condition
+       */
+      FeatureMatcherByType filterBy = mc.getBy();
+      Condition cond = Condition.fromString(mc.getCondition());
+      String pattern = mc.getValue();
+      FeatureMatcherI matchCondition = null;
+      if (filterBy == FeatureMatcherByType.BYLABEL)
+      {
+        matchCondition = FeatureMatcher.byLabel(cond, pattern);
+      }
+      else if (filterBy == FeatureMatcherByType.BYSCORE)
+      {
+        matchCondition = FeatureMatcher.byScore(cond, pattern);
+  
+      }
+      else if (filterBy == FeatureMatcherByType.BYATTRIBUTE)
+      {
+        String[] attNames = mc.getAttributeName();
+        matchCondition = FeatureMatcher.byAttribute(cond, pattern,
+                attNames);
+      }
+  
+      /*
+       * note this throws IllegalStateException if AND-ing to a 
+       * previously OR-ed compound condition, or vice versa
+       */
+      if (and)
+      {
+        matcherSet.and(matchCondition);
+      }
+      else
+      {
+        matcherSet.or(matchCondition);
+      }
+    }
+    else
+    {
+      /*
+       * compound condition
+       */
+      MatcherSet[] matchers = matcherSetModel.getCompoundMatcher()
+              .getMatcherSet();
+      boolean anded = matcherSetModel.getCompoundMatcher().getAnd();
+      if (matchers.length == 2)
+      {
+        unmarshalFilterConditions(matcherSet, matchers[0], anded);
+        unmarshalFilterConditions(matcherSet, matchers[1], anded);
+      }
+      else
+      {
+        System.err.println("Malformed compound filter condition");
+      }
+    }
+  }
+
+  /**
+   * Loads one XML model of a feature colour to a Jalview object
+   * 
+   * @param colourModel
+   * @return
+   */
+  protected static FeatureColourI unmarshalColour(
+          jalview.schemabinding.version2.Colour colourModel)
+  {
+    FeatureColourI colour = null;
+  
+    if (colourModel.hasMax())
+    {
+      Color mincol = null;
+      Color maxcol = null;
+      Color noValueColour = null;
+  
+      try
+      {
+        mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16));
+        maxcol = new Color(Integer.parseInt(colourModel.getRGB(), 16));
+      } catch (Exception e)
+      {
+        Cache.log.warn("Couldn't parse out graduated feature color.", e);
+      }
+  
+      NoValueColour noCol = colourModel.getNoValueColour();
+      if (noCol == NoValueColour.MIN)
+      {
+        noValueColour = mincol;
+      }
+      else if (noCol == NoValueColour.MAX)
+      {
+        noValueColour = maxcol;
+      }
+  
+      colour = new FeatureColour(mincol, maxcol, noValueColour,
+              colourModel.getMin(),
+              colourModel.getMax());
+      String[] attributes = colourModel.getAttributeName();
+      if (attributes != null && attributes.length > 0)
+      {
+        colour.setAttributeName(attributes);
+      }
+      if (colourModel.hasAutoScale())
+      {
+        colour.setAutoScaled(colourModel.getAutoScale());
+      }
+      if (colourModel.hasColourByLabel())
+      {
+        colour.setColourByLabel(colourModel.getColourByLabel());
+      }
+      if (colourModel.hasThreshold())
+      {
+        colour.setThreshold(colourModel.getThreshold());
+      }
+      ColourThreshTypeType ttyp = colourModel.getThreshType();
+      if (ttyp != null)
+      {
+        if (ttyp == ColourThreshTypeType.ABOVE)
+        {
+          colour.setAboveThreshold(true);
+        }
+        else if (ttyp == ColourThreshTypeType.BELOW)
+        {
+          colour.setBelowThreshold(true);
+        }
+      }
+    }
+    else
+    {
+      Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16));
+      colour = new FeatureColour(color);
+    }
+  
+    return colour;
+  }
 }