JAL-2546 JAL-2490 efficient lookup of features, correct handling of
[jalview.git] / src / jalview / io / JSONFile.java
index 8725556..14574d0 100644 (file)
@@ -26,12 +26,13 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.ComplexAlignFile;
 import jalview.api.FeatureRenderer;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.bin.BuildDetails;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
-import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
@@ -39,12 +40,18 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.json.binding.biojson.v1.AlignmentAnnotationPojo;
 import jalview.json.binding.biojson.v1.AlignmentPojo;
+import jalview.json.binding.biojson.v1.AnnotationDisplaySettingPojo;
 import jalview.json.binding.biojson.v1.AnnotationPojo;
 import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.json.binding.biojson.v1.SequenceFeaturesPojo;
 import jalview.json.binding.biojson.v1.SequenceGrpPojo;
 import jalview.json.binding.biojson.v1.SequencePojo;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.JalviewColourScheme;
+import jalview.schemes.ResidueColourScheme;
+import jalview.util.ColorUtils;
+import jalview.util.Format;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 
 import java.awt.Color;
@@ -68,10 +75,6 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
   private String application = "Jalview";
 
-  public static final String FILE_EXT = "json";
-
-  public static final String FILE_DESC = "JSON";
-
   private String globalColourScheme;
 
   private boolean showSeqFeatures;
@@ -82,14 +85,14 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
   private FeatureRenderer fr;
 
-  private List<int[]> hiddenColumns;
-
-  private ColumnSelection columnSelection;
+  private HiddenColumns hiddenColumns;
 
   private List<String> hiddenSeqRefs;
 
   private ArrayList<SequenceI> hiddenSequences;
 
+  private final static String TCOFFEE_SCORE = "TCoffeeScore";
+
   public JSONFile()
   {
     super();
@@ -100,9 +103,10 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     super(source);
   }
 
-  public JSONFile(String inFile, String type) throws IOException
+  public JSONFile(String inFile, DataSourceType sourceType)
+          throws IOException
   {
-    super(inFile, type);
+    super(inFile, sourceType);
   }
 
   @Override
@@ -113,7 +117,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
   }
 
   @Override
-  public String print()
+  public String print(SequenceI[] sqs, boolean jvsuffix)
   {
     String jsonOutput = null;
     try
@@ -166,7 +170,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       }
 
       int count = 0;
-      for (SequenceI seq : seqs)
+      for (SequenceI seq : sqs)
       {
         StringBuilder name = new StringBuilder();
         name.append(seq.getName()).append("/").append(seq.getStart())
@@ -209,11 +213,23 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         jsonAlignmentPojo
                 .setAlignAnnotation(annotationToJsonPojo(annotations));
       }
+      else
+      {
+        // These color schemes require annotation, disable them if annotations
+        // are not exported
+        if (globalColourScheme
+                .equalsIgnoreCase(JalviewColourScheme.RNAHelices.toString())
+                || globalColourScheme
+                        .equalsIgnoreCase(JalviewColourScheme.TCoffee
+                                .toString()))
+        {
+          jsonAlignmentPojo.setGlobalColorScheme(ResidueColourScheme.NONE);
+        }
+      }
 
       if (exportSettings.isExportFeatures())
       {
-        jsonAlignmentPojo
-                .setSeqFeatures(sequenceFeatureToJsonPojo(seqs, fr));
+        jsonAlignmentPojo.setSeqFeatures(sequenceFeatureToJsonPojo(sqs));
       }
 
       if (exportSettings.isExportGroups() && seqGroups != null
@@ -224,7 +240,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
           SequenceGrpPojo seqGrpPojo = new SequenceGrpPojo();
           seqGrpPojo.setGroupName(seqGrp.getName());
           seqGrpPojo.setColourScheme(ColourSchemeProperty
-                  .getColourName(seqGrp.cs));
+                  .getColourName(seqGrp.getColourScheme()));
           seqGrpPojo.setColourText(seqGrp.getColourText());
           seqGrpPojo.setDescription(seqGrp.getDescription());
           seqGrpPojo.setDisplayBoxes(seqGrp.getDisplayBoxes());
@@ -263,8 +279,9 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     // hidden column business
     if (getViewport().hasHiddenColumns())
     {
-      List<int[]> hiddenCols = getViewport().getColumnSelection()
-              .getHiddenColumns();
+      List<int[]> hiddenCols = getViewport().getAlignment()
+              .getHiddenColumns()
+              .getHiddenRegions();
       StringBuilder hiddenColsBuilder = new StringBuilder();
       for (int[] range : hiddenCols)
       {
@@ -302,46 +319,50 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     return hiddenSections;
   }
 
-  public List<SequenceFeaturesPojo> sequenceFeatureToJsonPojo(
-          List<SequenceI> seqs, FeatureRenderer fr)
+  protected List<SequenceFeaturesPojo> sequenceFeatureToJsonPojo(
+          SequenceI[] sqs)
   {
     displayedFeatures = (fr == null) ? null : fr.getFeaturesDisplayed();
     List<SequenceFeaturesPojo> sequenceFeaturesPojo = new ArrayList<SequenceFeaturesPojo>();
-    for (SequenceI seq : seqs)
+    if (sqs == null)
     {
-      SequenceI dataSetSequence = seq.getDatasetSequence();
-      SequenceFeature[] seqFeatures = (dataSetSequence == null) ? null
-              : seq.getDatasetSequence().getSequenceFeatures();
+      return sequenceFeaturesPojo;
+    }
 
-      seqFeatures = (seqFeatures == null) ? seq.getSequenceFeatures()
-              : seqFeatures;
-      if (seqFeatures == null)
-      {
-        continue;
-      }
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
 
+    String[] visibleFeatureTypes = displayedFeatures == null ? null
+            : displayedFeatures.getVisibleFeatures().toArray(
+                    new String[displayedFeatures.getVisibleFeatureCount()]);
+
+    for (SequenceI seq : sqs)
+    {
+      /*
+       * get all features currently visible (and any non-positional features)
+       */
+      List<SequenceFeature> seqFeatures = seq.getFeatures().getAllFeatures(
+              visibleFeatureTypes);
       for (SequenceFeature sf : seqFeatures)
       {
-        if (displayedFeatures != null
-                && displayedFeatures.isVisible(sf.getType()))
-        {
-          SequenceFeaturesPojo jsonFeature = new SequenceFeaturesPojo(
-                  String.valueOf(seq.hashCode()));
-
-          String featureColour = (fr == null) ? null : jalview.util.Format
-                  .getHexString(fr.findFeatureColour(Color.white, seq,
-                          seq.findIndex(sf.getBegin())));
-          jsonFeature.setXstart(seq.findIndex(sf.getBegin()) - 1);
-          jsonFeature.setXend(seq.findIndex(sf.getEnd()));
-          jsonFeature.setType(sf.getType());
-          jsonFeature.setDescription(sf.getDescription());
-          jsonFeature.setLinks(sf.links);
-          jsonFeature.setOtherDetails(sf.otherDetails);
-          jsonFeature.setScore(sf.getScore());
-          jsonFeature.setFillColor(featureColour);
-          jsonFeature.setFeatureGroup(sf.getFeatureGroup());
-          sequenceFeaturesPojo.add(jsonFeature);
-        }
+        SequenceFeaturesPojo jsonFeature = new SequenceFeaturesPojo(
+                String.valueOf(seq.hashCode()));
+
+        String featureColour = (fr == null) ? null : Format
+                .getHexString(finder.findFeatureColour(Color.white, seq,
+                        seq.findIndex(sf.getBegin())));
+        int xStart = sf.getBegin() == 0 ? 0
+                : seq.findIndex(sf.getBegin()) - 1;
+        int xEnd = sf.getEnd() == 0 ? 0 : seq.findIndex(sf.getEnd());
+        jsonFeature.setXstart(xStart);
+        jsonFeature.setXend(xEnd);
+        jsonFeature.setType(sf.getType());
+        jsonFeature.setDescription(sf.getDescription());
+        jsonFeature.setLinks(sf.links);
+        jsonFeature.setOtherDetails(sf.otherDetails);
+        jsonFeature.setScore(sf.getScore());
+        jsonFeature.setFillColor(featureColour);
+        jsonFeature.setFeatureGroup(sf.getFeatureGroup());
+        sequenceFeaturesPojo.add(jsonFeature);
       }
     }
     return sequenceFeaturesPojo;
@@ -360,6 +381,26 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       AlignmentAnnotationPojo alignAnnotPojo = new AlignmentAnnotationPojo();
       alignAnnotPojo.setDescription(annot.description);
       alignAnnotPojo.setLabel(annot.label);
+      if (!Double.isNaN(annot.score))
+      {
+        alignAnnotPojo.setScore(annot.score);
+      }
+      alignAnnotPojo.setCalcId(annot.getCalcId());
+      alignAnnotPojo.setGraphType(annot.graph);
+
+      AnnotationDisplaySettingPojo annotSetting = new AnnotationDisplaySettingPojo();
+      annotSetting.setBelowAlignment(annot.belowAlignment);
+      annotSetting.setCentreColLabels(annot.centreColLabels);
+      annotSetting.setScaleColLabel(annot.scaleColLabel);
+      annotSetting.setShowAllColLabels(annot.showAllColLabels);
+      annotSetting.setVisible(annot.visible);
+      annotSetting.setHasIcon(annot.hasIcons);
+      alignAnnotPojo.setAnnotationSettings(annotSetting);
+      SequenceI refSeq = annot.sequenceRef;
+      if (refSeq != null)
+      {
+        alignAnnotPojo.setSequenceRef(String.valueOf(refSeq.hashCode()));
+      }
       for (Annotation annotation : annot.annotations)
       {
         AnnotationPojo annotationPojo = new AnnotationPojo();
@@ -369,12 +410,28 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
           annotationPojo.setValue(annotation.value);
           annotationPojo
                   .setSecondaryStructure(annotation.secondaryStructure);
-          annotationPojo.setDisplayCharacter(annotation.displayCharacter);
+          String displayChar = annotation.displayCharacter == null ? null
+                  : annotation.displayCharacter;
+          // System.out.println("--------------------->[" + displayChar + "]");
+          annotationPojo.setDisplayCharacter(displayChar);
+          if (annotation.colour != null)
+          {
+            annotationPojo.setColour(jalview.util.Format
+                    .getHexString(annotation.colour));
+          }
           alignAnnotPojo.getAnnotations().add(annotationPojo);
         }
         else
         {
-          alignAnnotPojo.getAnnotations().add(annotationPojo);
+          if (annot.getCalcId() != null
+                  && annot.getCalcId().equalsIgnoreCase(TCOFFEE_SCORE))
+          {
+            // do nothing
+          }
+          else
+          {
+            alignAnnotPojo.getAnnotations().add(annotationPojo);
+          }
         }
       }
       jsonAnnotations.add(alignAnnotPojo);
@@ -432,7 +489,6 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         seqMap.put(seqUniqueId, seq);
       }
 
-
       parseFeatures(jsonSeqArray);
 
       for (Iterator<JSONObject> seqGrpIter = seqGrpJsonArray.iterator(); seqGrpIter
@@ -471,10 +527,9 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
           }
         }
         SequenceGroup seqGrp = new SequenceGroup(grpSeqs, grpName, null,
-                displayBoxes, displayText, colourText,
-                startRes, endRes);
-        seqGrp.cs = ColourSchemeMapper.getJalviewColourScheme(colourScheme,
-                seqGrp);
+                displayBoxes, displayText, colourText, startRes, endRes);
+        seqGrp.setColourScheme(ColourSchemeMapper.getJalviewColourScheme(
+                colourScheme, seqGrp));
         seqGrp.setShowNonconserved(showNonconserved);
         seqGrp.setDescription(description);
         this.seqGroups.add(seqGrp);
@@ -510,6 +565,12 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
                     : annot.get("displayCharacter").toString();
 
             annotations[count] = new Annotation(displayChar, desc, ss, val);
+            if (annot.get("colour") != null)
+            {
+              Color color = ColorUtils.parseColourString(annot.get(
+                      "colour").toString());
+              annotations[count].colour = color;
+            }
           }
           ++count;
         }
@@ -517,7 +578,64 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         AlignmentAnnotation alignAnnot = new AlignmentAnnotation(alAnnot
                 .get("label").toString(), alAnnot.get("description")
                 .toString(), annotations);
+        alignAnnot.graph = (alAnnot.get("graphType") == null) ? 0 : Integer
+                .valueOf(alAnnot.get("graphType").toString());
+
+        JSONObject diplaySettings = (JSONObject) alAnnot
+                .get("annotationSettings");
+        if (diplaySettings != null)
+        {
+
+          alignAnnot.scaleColLabel = (diplaySettings.get("scaleColLabel") == null) ? false
+                  : Boolean.valueOf(diplaySettings.get("scaleColLabel")
+                          .toString());
+          alignAnnot.showAllColLabels = (diplaySettings
+                  .get("showAllColLabels") == null) ? true : Boolean
+                  .valueOf(diplaySettings.get("showAllColLabels")
+                          .toString());
+          alignAnnot.centreColLabels = (diplaySettings
+                  .get("centreColLabels") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("centreColLabels")
+                          .toString());
+          alignAnnot.belowAlignment = (diplaySettings.get("belowAlignment") == null) ? false
+                  : Boolean.valueOf(diplaySettings.get("belowAlignment")
+                          .toString());
+          alignAnnot.visible = (diplaySettings.get("visible") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("visible")
+                          .toString());
+          alignAnnot.hasIcons = (diplaySettings.get("hasIcon") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("hasIcon")
+                          .toString());
+
+        }
+        if (alAnnot.get("score") != null)
+        {
+          alignAnnot.score = Double
+                  .valueOf(alAnnot.get("score").toString());
+        }
+
+        String calcId = (alAnnot.get("calcId") == null) ? "" : alAnnot.get(
+                "calcId").toString();
+        alignAnnot.setCalcId(calcId);
+        String seqHash = (alAnnot.get("sequenceRef") != null) ? alAnnot
+                .get("sequenceRef").toString() : null;
+
+        Sequence sequence = (seqHash != null) ? seqMap.get(seqHash) : null;
+        if (sequence != null)
+        {
+          alignAnnot.sequenceRef = sequence;
+          sequence.addAlignmentAnnotation(alignAnnot);
+          if (alignAnnot.label.equalsIgnoreCase("T-COFFEE"))
+          {
+            alignAnnot.createSequenceMapping(sequence, sequence.getStart(),
+                    false);
+            sequence.addAlignmentAnnotation(alignAnnot);
+            alignAnnot.adjustForAlignment();
+          }
+        }
+        alignAnnot.validateRangeAndDisplay();
         this.annotations.add(alignAnnot);
+
       }
     } catch (Exception e)
     {
@@ -545,12 +663,12 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     String hiddenCols = (String) jvSettingsJson.get("hiddenCols");
     if (hiddenCols != null && !hiddenCols.isEmpty())
     {
-      columnSelection = new ColumnSelection();
+      hiddenColumns = new HiddenColumns();
       String[] rangeStrings = hiddenCols.split(";");
       for (String rangeString : rangeStrings)
       {
         String[] range = rangeString.split("-");
-        columnSelection.hideColumns(Integer.valueOf(range[0]),
+        hiddenColumns.hideColumns(Integer.valueOf(range[0]),
                 Integer.valueOf(range[1]));
       }
     }
@@ -570,12 +688,23 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         Long end = (Long) jsonFeature.get("xEnd");
         String type = (String) jsonFeature.get("type");
         String featureGrp = (String) jsonFeature.get("featureGroup");
-        String descripiton = (String) jsonFeature.get("description");
+        String description = (String) jsonFeature.get("description");
         String seqRef = (String) jsonFeature.get("sequenceRef");
         Float score = Float.valueOf(jsonFeature.get("score").toString());
 
         Sequence seq = seqMap.get(seqRef);
-        SequenceFeature sequenceFeature = new SequenceFeature();
+
+        /*
+         * begin/end of 0 is for a non-positional feature
+         */
+        int featureBegin = begin.intValue() == 0 ? 0 : seq
+                .findPosition(begin.intValue());
+        int featureEnd = end.intValue() == 0 ? 0 : seq.findPosition(end
+                .intValue()) - 1;
+
+        SequenceFeature sequenceFeature = new SequenceFeature(type,
+                description, featureBegin, featureEnd, score, featureGrp);
+
         JSONArray linksJsonArray = (JSONArray) jsonFeature.get("links");
         if (linksJsonArray != null && linksJsonArray.size() > 0)
         {
@@ -586,18 +715,14 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
             sequenceFeature.addLink(link);
           }
         }
-        sequenceFeature.setFeatureGroup(featureGrp);
-        sequenceFeature.setScore(score);
-        sequenceFeature.setDescription(descripiton);
-        sequenceFeature.setType(type);
-        sequenceFeature.setBegin(seq.findPosition(begin.intValue()));
-        sequenceFeature.setEnd(seq.findPosition(end.intValue()) - 1);
+
         seq.addSequenceFeature(sequenceFeature);
         displayedFeatures.setVisible(type);
       }
     }
   }
 
+  @Override
   public String getGlobalColourScheme()
   {
     return globalColourScheme;
@@ -619,8 +744,13 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     this.displayedFeatures = displayedFeatures;
   }
 
+  @Override
   public void configureForView(AlignmentViewPanel avpanel)
   {
+    if (avpanel == null)
+    {
+      return;
+    }
     super.configureForView(avpanel);
     AlignViewportI viewport = avpanel.getAlignViewport();
     AlignmentI alignment = viewport.getAlignment();
@@ -630,15 +760,14 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     fr = avpanel.cloneFeatureRenderer();
 
     // Add non auto calculated annotation to AlignFile
-    for (AlignmentAnnotation annot : annots)
+    if (annots != null)
     {
-      if (annot != null && !annot.autoCalculated)
+      for (AlignmentAnnotation annot : annots)
       {
-        if (!annot.visible)
+        if (annot != null && !annot.autoCalculated)
         {
-          continue;
+          annotations.add(annot);
         }
-        annotations.add(annot);
       }
     }
     globalColourScheme = ColourSchemeProperty.getColourName(viewport
@@ -648,6 +777,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
   }
 
+  @Override
   public boolean isShowSeqFeatures()
   {
     return showSeqFeatures;
@@ -663,21 +793,18 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     return annotations;
   }
 
-  public List<int[]> getHiddenColumns()
+  @Override
+  public HiddenColumns getHiddenColumns()
   {
     return hiddenColumns;
   }
 
-  public ColumnSelection getColumnSelection()
+  public void setHiddenColumns(HiddenColumns hidden)
   {
-    return columnSelection;
-  }
-
-  public void setColumnSelection(ColumnSelection columnSelection)
-  {
-    this.columnSelection = columnSelection;
+    this.hiddenColumns = hidden;
   }
 
+  @Override
   public SequenceI[] getHiddenSequences()
   {
     if (hiddenSequences == null || hiddenSequences.isEmpty())
@@ -757,4 +884,19 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       this.exportJalviewSettings = exportJalviewSettings;
     }
   }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
 }