JAL-3070 rough-and-ready refactor of JABA SequenceAnnotation style services - needs...
authorJim Procter <jprocter@issues.jalview.org>
Fri, 20 Sep 2019 16:01:57 +0000 (17:01 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Fri, 20 Sep 2019 16:05:43 +0000 (17:05 +0100)
25 files changed:
src/jalview/workers/AlignCalcWorker.java
src/jalview/ws/api/SequenceAnnotationServiceI.java [new file with mode: 0644]
src/jalview/ws/api/WSAnnotationCalcManagerI.java [new file with mode: 0644]
src/jalview/ws/gui/AnnotationWsJob.java [new file with mode: 0644]
src/jalview/ws/gui/MsaWSJob.java
src/jalview/ws/gui/WsJob.java
src/jalview/ws/jws2/AAConClient.java [deleted file]
src/jalview/ws/jws2/AADisorderClient.java [deleted file]
src/jalview/ws/jws2/AbstractJabaCalcWorker.java
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/Jws2ClientFactory.java [new file with mode: 0644]
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/SequenceAnnotationWSClient.java
src/jalview/ws/jws2/jabaws2/AAConClient.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/AADisorderClient.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/JabawsAnnotationInstance.java [moved from src/jalview/ws/jws2/JabawsCalcWorker.java with 51% similarity]
src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java
src/jalview/ws/jws2/jabaws2/JabawsMsaInterfaceAlignCalcWorker.java
src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/Jws2Instance.java
src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
src/jalview/ws/jws2/jabaws2/RNAalifoldClient.java [moved from src/jalview/ws/jws2/RNAalifoldClient.java with 72% similarity]
src/jalview/ws/uimodel/AlignAnalysisUIText.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/RNAStructExportImport.java

index 0ad8726..cf4805d 100644 (file)
@@ -136,4 +136,8 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
     }
   }
 
+  public AlignViewportI getAlignViewport()
+  {
+    return alignViewport;
+  }
 }
diff --git a/src/jalview/ws/api/SequenceAnnotationServiceI.java b/src/jalview/ws/api/SequenceAnnotationServiceI.java
new file mode 100644 (file)
index 0000000..d0965e5
--- /dev/null
@@ -0,0 +1,40 @@
+package jalview.ws.api;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
+import jalview.ws.gui.AnnotationWsJob;
+import jalview.ws.jws2.AbstractJabaCalcWorker;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+
+public interface SequenceAnnotationServiceI extends JalviewWebServiceI
+{
+
+
+  /**
+   * submit sequences to service with no parameters, or preset or parameter set.
+   * Nb- almost the same as the 'align' method in the Msa service :)
+   * 
+   * @param seqs
+   * @param preset
+   * @param paramset
+   * @return
+   * @throws Throwable
+   */
+  JobId submitToService(List<SequenceI> seqs, WsParamSetI preset,
+          List<ArgumentI> paramset) throws Throwable;
+
+  /**
+   * todo: move to SequenceAnnotationResult
+   * 
+   * @param running
+   * @param abstractJabaCalcWorker
+   * @return
+   */
+  List<AlignmentAnnotation> getAlignmentAnnotation(AnnotationWsJob running,
+          AbstractJabaCalcWorker abstractJabaCalcWorker) throws Throwable;
+
+
+}
diff --git a/src/jalview/ws/api/WSAnnotationCalcManagerI.java b/src/jalview/ws/api/WSAnnotationCalcManagerI.java
new file mode 100644 (file)
index 0000000..47e3195
--- /dev/null
@@ -0,0 +1,6 @@
+package jalview.ws.api;
+
+public interface WSAnnotationCalcManagerI
+{
+
+}
diff --git a/src/jalview/ws/gui/AnnotationWsJob.java b/src/jalview/ws/gui/AnnotationWsJob.java
new file mode 100644 (file)
index 0000000..3381138
--- /dev/null
@@ -0,0 +1,106 @@
+package jalview.ws.gui;
+
+import jalview.api.FeatureRenderer;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
+
+import java.util.List;
+import java.util.Map;
+
+public class AnnotationWsJob extends WsJob
+{
+  /**
+   * sequences (anonymised)
+   */
+  List<SequenceI> seqs;
+
+  /**
+   * mapping to original sequences
+   */
+  Map<String, SequenceI> seqNames;
+
+  /**
+   * first column in the segment of the alignment view that was submitted
+   */
+  int startPos;
+
+  public int getStartPos()
+  {
+    return startPos;
+  }
+
+  public void setStartPos(int startPos)
+  {
+    this.startPos = startPos;
+  }
+
+  /**
+   * outputs
+   */
+  List<AlignmentAnnotation> annotation = null;
+
+  boolean transferSequenceFeatures = false;
+
+  public boolean isTransferSequenceFeatures()
+  {
+    return transferSequenceFeatures;
+  }
+
+  public void setTransferSequenceFeatures(boolean transferSequenceFeatures)
+  {
+    this.transferSequenceFeatures = transferSequenceFeatures;
+  }
+
+  public List<AlignmentAnnotation> getAnnotation()
+  {
+    return annotation;
+  }
+
+  public void setAnnotation(List<AlignmentAnnotation> annotation)
+  {
+    this.annotation = annotation;
+  }
+
+  @Override
+  public boolean hasResults()
+  {
+    return (isSubmitted() && isFinished()
+            && (annotation != null || transferSequenceFeatures));
+  }
+
+  public List<SequenceI> getSeqs()
+  {
+    return seqs;
+  }
+
+  public void setSeqs(List<SequenceI> seqs)
+  {
+    this.seqs = seqs;
+  }
+
+  public Map<String, SequenceI> getSeqNames()
+  {
+    return seqNames;
+  }
+
+  public void setSeqNames(Map<String, SequenceI> seqNames)
+  {
+    this.seqNames = seqNames;
+  }
+
+  /**
+   * configured by the WS framework just before results are collected
+   */
+  FeatureRenderer featureRenderer;
+
+  public void setFeatureRenderer(FeatureRenderer fr)
+  {
+    this.featureRenderer = fr;
+  }
+  public FeatureRenderer getFeatureRenderer()
+  {
+    // TODO Auto-generated method stub
+    return featureRenderer;
+  }
+
+}
index b27222f..d8cae20 100644 (file)
@@ -6,7 +6,6 @@ import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
-import jalview.ws.api.JobId;
 import jalview.ws.jws2.dm.JabaWsParamSet;
 import jalview.ws.params.ArgumentI;
 
@@ -278,6 +277,7 @@ class MsaWSJob extends WsJob
   {
     // TODO: get attributes for this MsaWS instance to check if it can do two
     // sequence alignment.
+    // TODO: check type of sequences are valid for this service
     if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
     {
       return true;
@@ -367,18 +367,4 @@ class MsaWSJob extends WsJob
       jobProgress.append("\nJob Output:\n");
     }
   }
-
-  JobId jobHandle = null;
-  public void setJobHandle(JobId align)
-  {
-    jobHandle = align;
-    setJobId(jobHandle.getJobId());
-
-  }
-
-  public JobId getJobHandle()
-  {
-    return jobHandle;
-  }
-
 }
\ No newline at end of file
index 05379d7..e5af8c1 100644 (file)
@@ -4,6 +4,7 @@
 package jalview.ws.gui;
 
 import jalview.ws.AWsJob;
+import jalview.ws.api.JobId;
 
 /**
  * Bean that holds state for a job
@@ -173,4 +174,27 @@ public class WsJob extends AWsJob
 
   }
 
+  /*
+   * bean holding submission info for a next-gen ws job 
+   */
+  JobId jobHandle = null;
+
+  /**
+   * stash the handle for the job and mark it as submitted
+   * 
+   * @param align
+   */
+  public void setJobHandle(JobId align)
+  {
+    jobHandle = align;
+    setJobId(jobHandle.getJobId());
+    submitted = true;
+
+  }
+
+  public JobId getJobHandle()
+  {
+    return jobHandle;
+  }
+
 }
diff --git a/src/jalview/ws/jws2/AAConClient.java b/src/jalview/ws/jws2/AAConClient.java
deleted file mode 100644 (file)
index ddd69a7..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws.jws2;
-
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.gui.AlignFrame;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.WsParamSetI;
-import jalview.ws.uimodel.AlignAnalysisUIText;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import compbio.data.sequence.FastaSequence;
-import compbio.data.sequence.Score;
-
-public class AAConClient extends JabawsCalcWorker
-{
-
-  public AAConClient(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<ArgumentI> paramset)
-  {
-    super(service, alignFrame, preset, paramset);
-    submitGaps = true;
-    alignedSeqs = true;
-    nucleotidesAllowed = false;
-    proteinAllowed = true;
-    filterNonStandardResidues = true;
-    gapMap = new boolean[0];
-    initViewportParams();
-  }
-
-  @Override
-  public String getServiceActionText()
-  {
-    return "calculating Amino acid consensus using AACon service";
-  }
-
-  /**
-   * update the consensus annotation from the sequence profile data using
-   * current visualization settings.
-   */
-
-  @Override
-  public void updateResultAnnotation(boolean immediate)
-  {
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
-    {
-      Map<String, TreeSet<Score>> scoremap = scoremanager.asMap();
-      int alWidth = alignViewport.getAlignment().getWidth();
-      ArrayList<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-      for (String score : scoremap.keySet())
-      {
-        Set<Score> scores = scoremap.get(score);
-        for (Score scr : scores)
-        {
-          if (scr.getRanges() != null && scr.getRanges().size() > 0)
-          {
-            /**
-             * annotation in range annotation = findOrCreate(scr.getMethod(),
-             * true, null, null); Annotation[] elm = new Annotation[alWidth];
-             * Iterator<Float> vals = scr.getScores().iterator(); for (Range rng
-             * : scr.getRanges()) { float val = vals.next().floatValue(); for
-             * (int i = rng.from; i <= rng.to; i++) { elm[i] = new
-             * Annotation("", "", ' ', val); } } annotation.annotations = elm;
-             * annotation.validateRangeAndDisplay();
-             */
-          }
-          else
-          {
-            createAnnotationRowsForScores(ourAnnot, getCalcId(), alWidth,
-                    scr);
-          }
-        }
-      }
-
-      if (ourAnnot.size() > 0)
-      {
-        updateOurAnnots(ourAnnot);
-      }
-    }
-  }
-
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
-  {
-    return (seqs.size() > 1);
-  }
-
-  @Override
-  public String getCalcId()
-  {
-    return CALC_ID;
-  }
-
-  private static String CALC_ID = "jabaws2.AACon";
-
-  public static AlignAnalysisUIText getAlignAnalysisUITest()
-  {
-    return new AlignAnalysisUIText(
-            compbio.ws.client.Services.AAConWS.toString(),
-            jalview.ws.jws2.AAConClient.class, CALC_ID, false, true, true,
-            MessageManager.getString("label.aacon_calculations"),
-            MessageManager.getString("tooltip.aacon_calculations"),
-            MessageManager.getString("label.aacon_settings"),
-            MessageManager.getString("tooltip.aacon_settings"));
-  }
-}
diff --git a/src/jalview/ws/jws2/AADisorderClient.java b/src/jalview/ws/jws2/AADisorderClient.java
deleted file mode 100644 (file)
index 91db7c3..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws.jws2;
-
-import jalview.api.FeatureColourI;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.GraphLine;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.schemes.FeatureColour;
-import jalview.util.ColorUtils;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.WsParamSetI;
-
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import compbio.data.sequence.FastaSequence;
-import compbio.data.sequence.Range;
-import compbio.data.sequence.Score;
-import compbio.data.sequence.ScoreManager.ScoreHolder;
-
-public class AADisorderClient extends JabawsCalcWorker
-{
-
-  private static final String THRESHOLD = "THRESHOLD";
-
-  private static final String RANGE = "RANGE";
-
-  String typeName;
-
-  String methodName;
-
-  String groupName;
-
-  AlignFrame af;
-
-  public AADisorderClient(Jws2Instance sh, AlignFrame alignFrame,
-          WsParamSetI thePreset, List<ArgumentI> paramset)
-  {
-    super(sh, alignFrame, thePreset, paramset);
-    af = alignFrame;
-    typeName = sh.getAction();
-    methodName = sh.getName();
-
-    submitGaps = false;
-    alignedSeqs = false;
-    nucleotidesAllowed = false;
-    proteinAllowed = true;
-    bySequence = true;
-  }
-
-  @Override
-  public String getServiceActionText()
-  {
-    return "Submitting amino acid sequences for disorder prediction.";
-  }
-
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
-  {
-    return (seqs.size() > 0);
-  }
-
-  private static Map<String, Map<String, String[]>> featureMap;
-
-  private static Map<String, Map<String, Map<String, Object>>> annotMap;
-
-  private static String DONTCOMBINE = "DONTCOMBINE";
-
-  private static String INVISIBLE = "INVISIBLE";
-  static
-  {
-    // TODO: turn this into some kind of configuration file that's a bit easier
-    // to edit
-    featureMap = new HashMap<>();
-    Map<String, String[]> fmap;
-    featureMap.put(compbio.ws.client.Services.IUPredWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("Glob",
-            new String[]
-            { "Globular Domain", "Predicted globular domain" });
-    featureMap.put(compbio.ws.client.Services.JronnWS.toString(),
-            fmap = new HashMap<>());
-    featureMap.put(compbio.ws.client.Services.DisemblWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("REM465", new String[] { "REM465", "Missing density" });
-    fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" });
-    fmap.put("COILS", new String[] { "COILS", "Random coil" });
-    featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
-            fmap = new HashMap<>());
-    fmap.put("GlobDoms",
-            new String[]
-            { "Globular Domain", "Predicted globular domain" });
-    fmap.put("Disorder",
-            new String[]
-            { "Protein Disorder", "Probable unstructured peptide region" });
-    Map<String, Map<String, Object>> amap;
-    annotMap = new HashMap<>();
-    annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
-            amap = new HashMap<>());
-    amap.put("Dydx", new HashMap<String, Object>());
-    amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE);
-    amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 });
-    amap.get("Dydx").put(RANGE, new float[] { -1, +1 });
-
-    amap.put("SmoothedScore", new HashMap<String, Object>());
-    amap.get("SmoothedScore").put(INVISIBLE, INVISIBLE);
-    amap.put("RawScore", new HashMap<String, Object>());
-    amap.get("RawScore").put(INVISIBLE, INVISIBLE);
-    annotMap.put(compbio.ws.client.Services.DisemblWS.toString(),
-            amap = new HashMap<>());
-    amap.put("COILS", new HashMap<String, Object>());
-    amap.put("HOTLOOPS", new HashMap<String, Object>());
-    amap.put("REM465", new HashMap<String, Object>());
-    amap.get("COILS").put(THRESHOLD, new double[] { 1, 0.516 });
-    amap.get("COILS").put(RANGE, new float[] { 0, 1 });
-
-    amap.get("HOTLOOPS").put(THRESHOLD, new double[] { 1, 0.6 });
-    amap.get("HOTLOOPS").put(RANGE, new float[] { 0, 1 });
-    amap.get("REM465").put(THRESHOLD, new double[] { 1, 0.1204 });
-    amap.get("REM465").put(RANGE, new float[] { 0, 1 });
-
-    annotMap.put(compbio.ws.client.Services.IUPredWS.toString(),
-            amap = new HashMap<>());
-    amap.put("Long", new HashMap<String, Object>());
-    amap.put("Short", new HashMap<String, Object>());
-    amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("Long").put(RANGE, new float[] { 0, 1 });
-    amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("Short").put(RANGE, new float[] { 0, 1 });
-    annotMap.put(compbio.ws.client.Services.JronnWS.toString(),
-            amap = new HashMap<>());
-    amap.put("JRonn", new HashMap<String, Object>());
-    amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 });
-    amap.get("JRonn").put(RANGE, new float[] { 0, 1 });
-  }
-
-  @Override
-  public void updateResultAnnotation(boolean immediate)
-  {
-
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
-    {
-      Map<String, String[]> featureTypeMap = featureMap
-              .get(service.getName());
-      Map<String, Map<String, Object>> annotTypeMap = annotMap
-              .get(service.getName());
-      boolean dispFeatures = false;
-      Map<String, Object> fc = new Hashtable<>();
-      List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-      /**
-       * grouping for any annotation rows created
-       */
-      int graphGroup = 1;
-      if (alignViewport.getAlignment().getAlignmentAnnotation() != null)
-      {
-        for (AlignmentAnnotation ala : alignViewport.getAlignment()
-                .getAlignmentAnnotation())
-        {
-          if (ala.graphGroup > graphGroup)
-          {
-            graphGroup = ala.graphGroup;
-          }
-        }
-      }
-
-      for (String seqId : seqNames.keySet())
-      {
-        boolean sameGroup = false;
-        SequenceI dseq, aseq, seq = seqNames.get(seqId);
-        int base = seq.findPosition(start) - 1;
-        aseq = seq;
-        while ((dseq = seq).getDatasetSequence() != null)
-        {
-          seq = seq.getDatasetSequence();
-        }
-        ScoreHolder scores = null;
-        try
-        {
-          scores = scoremanager.getAnnotationForSequence(seqId);
-        } catch (Exception q)
-        {
-          Cache.log
-                  .info("Couldn't recover disorder prediction for sequence "
-                          + seq.getName() + "(Prediction name was " + seqId
-                          + ")"
-                          + "\nSee http://issues.jalview.org/browse/JAL-1319 for one possible reason why disorder predictions might fail.");
-        }
-        float last = Float.NaN, val = Float.NaN;
-        int lastAnnot = ourAnnot.size();
-        if (scores != null && scores.scores != null)
-        {
-          for (Score scr : scores.scores)
-          {
-
-            if (scr.getRanges() != null && scr.getRanges().size() > 0)
-            {
-              Iterator<Float> vals = scr.getScores().iterator();
-              // make features on sequence
-              for (Range rn : scr.getRanges())
-              {
-
-                SequenceFeature sf;
-                String[] type = featureTypeMap.get(scr.getMethod());
-                if (type == null)
-                {
-                  // create a default type for this feature
-                  type = new String[] {
-                      typeName + " (" + scr.getMethod() + ")",
-                      service.getActionText() };
-                }
-                if (vals.hasNext())
-                {
-                  val = vals.next().floatValue();
-                  sf = new SequenceFeature(type[0], type[1],
-                          base + rn.from, base + rn.to, val, methodName);
-                }
-                else
-                {
-                  sf = new SequenceFeature(type[0], type[1],
-                          base + rn.from, base + rn.to, methodName);
-                }
-                dseq.addSequenceFeature(sf);
-                if (last != val && !Float.isNaN(last))
-                {
-                  fc.put(sf.getType(), sf);
-                }
-                last = val;
-                dispFeatures = true;
-              }
-            }
-            else
-            {
-              if (scr.getScores().size() == 0)
-              {
-                continue;
-              }
-              String typename, calcName;
-              AlignmentAnnotation annot = createAnnotationRowsForScores(
-                      ourAnnot,
-                      typename = service.getName() + " ("
-                              + scr.getMethod() + ")",
-                      calcName = service.getNameURI() + "/"
-                              + scr.getMethod(),
-                      aseq, base + 1, scr);
-              annot.graph = AlignmentAnnotation.LINE_GRAPH;
-
-              Map<String, Object> styleMap = (annotTypeMap == null) ? null
-                      : annotTypeMap.get(scr.getMethod());
-
-              annot.visible = (styleMap == null
-                      || styleMap.get(INVISIBLE) == null);
-              double[] thrsh = (styleMap == null) ? null
-                      : (double[]) styleMap.get(THRESHOLD);
-              float[] range = (styleMap == null) ? null
-                      : (float[]) styleMap.get(RANGE);
-              if (range != null)
-              {
-                annot.graphMin = range[0];
-                annot.graphMax = range[1];
-              }
-              if (styleMap == null || styleMap.get(DONTCOMBINE) == null)
-              {
-                {
-                  if (!sameGroup)
-                  {
-                    graphGroup++;
-                    sameGroup = true;
-                  }
-
-                  annot.graphGroup = graphGroup;
-                }
-              }
-
-              annot.description = "<html>" + service.getActionText()
-                      + " - raw scores";
-              if (thrsh != null)
-              {
-                String threshNote = (thrsh[0] > 0 ? "Above " : "Below ")
-                        + thrsh[1] + " indicates disorder";
-                annot.threshold = new GraphLine((float) thrsh[1],
-                        threshNote, Color.red);
-                annot.description += "<br/>" + threshNote;
-              }
-              annot.description += "</html>";
-              Color col = ColorUtils
-                      .createColourFromName(typeName + scr.getMethod());
-              for (int p = 0, ps = annot.annotations.length; p < ps; p++)
-              {
-                if (annot.annotations[p] != null)
-                {
-                  annot.annotations[p].colour = col;
-                }
-              }
-              annot._linecolour = col;
-              // finally, update any dataset annotation
-              replaceAnnotationOnAlignmentWith(annot, typename, calcName,
-                      aseq);
-            }
-          }
-        }
-        if (lastAnnot + 1 == ourAnnot.size())
-        {
-          // remove singleton alignment annotation row
-          ourAnnot.get(lastAnnot).graphGroup = -1;
-        }
-      }
-      {
-        if (dispFeatures)
-        {
-          jalview.api.FeatureRenderer fr = ((jalview.gui.AlignmentPanel) ap)
-                  .cloneFeatureRenderer();
-          for (String ft : fc.keySet())
-          {
-            FeatureColourI gc = fr.getFeatureStyle(ft);
-            if (gc.isSimpleColour())
-            {
-              // set graduated color as fading to white for minimum, and
-              // autoscaling to values on alignment
-              FeatureColourI ggc = new FeatureColour(gc.getColour(),
-                      Color.white, gc.getColour(), Color.white,
-                      Float.MIN_VALUE, Float.MAX_VALUE);
-              ggc.setAutoScaled(true);
-              fr.setColour(ft, ggc);
-            }
-          }
-          // TODO: JAL-1150 - create sequence feature settings API for defining
-          // styles and enabling/disabling feature overlay on alignment panel
-          ((jalview.gui.AlignmentPanel) ap).updateFeatureRendererFrom(fr);
-          if (af.alignPanel == ap)
-          {
-            // only do this if the alignFrame is currently showing this view.
-            af.setShowSeqFeatures(true);
-          }
-        }
-        if (ourAnnot.size() > 0)
-        {
-          // Modify the visible annotation on the alignment viewport with the
-          // new alignment annotation rows created.
-          updateOurAnnots(ourAnnot);
-          ap.adjustAnnotationHeight();
-          ap.paintAlignment(true, true);
-        }
-      }
-    }
-  }
-
-  @Override
-  public String getCalcId()
-  {
-    // Disorder predictions are not dynamically updated so we return null
-    return null;
-  }
-
-}
index 1a361c4..422b4ef 100644 (file)
@@ -24,17 +24,24 @@ import jalview.analysis.AlignSeq;
 import jalview.analysis.SeqsetUtils;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.IProgressIndicator;
 import jalview.gui.IProgressIndicatorHandler;
+import jalview.gui.JvOptionPane;
 import jalview.schemes.ResidueProperties;
+import jalview.util.MessageManager;
 import jalview.workers.AlignCalcWorker;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.WSAnnotationCalcManagerI;
+import jalview.ws.gui.AnnotationWsJob;
 import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.dm.JabaWsParamSet;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
@@ -44,14 +51,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import compbio.data.sequence.FastaSequence;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.JobSubmissionException;
-import compbio.metadata.Option;
-import compbio.metadata.ResultNotAvailableException;
-
-public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
+public class AbstractJabaCalcWorker extends AlignCalcWorker
+        implements WSAnnotationCalcManagerI
 {
 
   protected Jws2Instance service;
@@ -88,7 +89,11 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
    * @return null or a string used to recover all annotation generated by this
    *         worker
    */
-  public abstract String getCalcId();
+  public String getCalcId()
+  {
+    return service.getAlignAnalysisUI() == null ? null
+            : service.getAlignAnalysisUI().getCalcId();
+  }
 
   public WsParamSetI getPreset()
   {
@@ -116,27 +121,6 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
     calcMan.startWorker(this);
     initViewportParams();
   }
-
-  public List<Option> getJabaArguments()
-  {
-    List<Option> newargs = new ArrayList<>();
-    if (preset != null && preset instanceof JabaWsParamSet)
-    {
-      newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-    }
-    if (arguments != null && arguments.size() > 0)
-    {
-      for (Object rg : JabaParamStore.getJabafromJwsArgs(arguments))
-      {
-        if (Option.class.isAssignableFrom(rg.getClass()))
-        {
-          newargs.add((Option) rg);
-        }
-      }
-    }
-    return newargs;
-  }
-
   protected boolean alignedSeqs = true;
 
   protected boolean nucleotidesAllowed = false;
@@ -150,6 +134,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
 
   protected Map<String, SequenceI> seqNames;
 
+  // TODO: convert to bitset
   protected boolean[] gapMap;
 
   int realw;
@@ -158,6 +143,13 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
 
   int end;
 
+  private AlignFrame alignFrame;
+
+  public boolean[] getGapMap()
+  {
+    return gapMap;
+  }
+
   public AbstractJabaCalcWorker(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
   {
@@ -168,19 +160,71 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
           WsParamSetI preset, List<ArgumentI> paramset)
   {
     this(alignFrame.getCurrentView(), alignFrame.alignPanel);
+    // TODO: both these fields needed ?
+    this.alignFrame = alignFrame;
     this.guiProgress = alignFrame;
     this.preset = preset;
     this.arguments = paramset;
     this.service = service;
+    try
+    {
+      annotService = (jalview.ws.api.SequenceAnnotationServiceI) service
+              .getEndpoint();
+    } catch (ClassCastException cce)
+    {
+      JvOptionPane.showMessageDialog(Desktop.desktop,
+              MessageManager.formatMessage(
+                      "label.service_called_is_not_an_annotation_service",
+                      new String[]
+                      { service.getName() }),
+              MessageManager.getString("label.internal_jalview_error"),
+              JvOptionPane.WARNING_MESSAGE);
+
+    }
+    // configure submission flags
+    if (service.getAlignAnalysisUI() != null)
+    {
+      // instantaneous calculation. Right now that's either AACons or RNAAliFold
+      proteinAllowed = service.getAlignAnalysisUI().isPr();
+      nucleotidesAllowed = service.getAlignAnalysisUI().isNa();
+      alignedSeqs = service.getAlignAnalysisUI().isNeedsAlignedSeqs();
+      bySequence = !service.getAlignAnalysisUI().isAA();
+      filterNonStandardResidues = service.getAlignAnalysisUI()
+              .isFilterSymbols();
+      min_valid_seqs = service.getAlignAnalysisUI().getMinimumSequences();
+      initViewportParams();
+    }
+    else
+    {
+      // assume disorder prediction : per-sequence protein only no gaps
+      // analysis.
+      // TODO - move configuration to UIInfo base class for all these flags !
+      alignedSeqs = false;
+      bySequence = true;
+      filterNonStandardResidues = true;
+      nucleotidesAllowed = false;
+      proteinAllowed = true;
+      submitGaps = false;
+      min_valid_seqs = 1;
+    }
   }
 
   /**
    * 
    * @return true if the submission thread should attempt to submit data
    */
-  abstract boolean hasService();
+  public boolean hasService()
+  {
+    return annotService != null;
+  }
+
+  protected jalview.ws.api.SequenceAnnotationServiceI annotService = null;
+
+  volatile JobId rslt = null;
 
-  volatile String rslt = "JOB NOT DEFINED";
+  AnnotationWsJob running = null;
+
+  private int min_valid_seqs;
 
   @Override
   public void run()
@@ -189,10 +233,12 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
     {
       return;
     }
+
     long progressId = -1;
 
     int serverErrorsLeft = 3;
-
+    final boolean cancellable = CancellableI.class
+            .isAssignableFrom(annotService.getClass());
     StringBuffer msg = new StringBuffer();
     try
     {
@@ -200,12 +246,14 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
       {
         return;
       }
-      List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(
+      List<SequenceI> seqs = getInputSequences(
               alignViewport.getAlignment(),
               bySequence ? alignViewport.getSelectionGroup() : null);
 
-      if (seqs == null || !checkValidInputSeqs(true, seqs))
+      if (seqs == null || !checkValidInputSeqs(seqs))
       {
+        jalview.bin.Cache.log.debug(
+                "Sequences for analysis service were null or not valid");
         calcMan.workerComplete(this);
         return;
       }
@@ -214,10 +262,28 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
               .getAlignmentAnnotation();
       if (guiProgress != null)
       {
-        guiProgress.setProgressBar("JABA " + getServiceActionText(),
+        guiProgress.setProgressBar(service.getActionText(),
                 progressId = System.currentTimeMillis());
       }
-      rslt = submitToService(seqs);
+      jalview.bin.Cache.log.debug("submitted " + seqs.size()
+              + " sequences to " + service.getActionText());
+
+      rslt = annotService.submitToService(seqs, getPreset(),
+              getArguments());
+      if (rslt == null)
+      {
+        return;
+      }
+      // TODO: handle job submission error reporting here.
+      
+      // ///
+      // otherwise, construct WsJob and any UI handlers
+      running = new AnnotationWsJob();
+      running.setJobHandle(rslt);
+      running.setSeqNames(seqNames);
+      running.setStartPos(start);
+      running.setSeqs(seqs);
+
       if (guiProgress != null)
       {
         guiProgress.registerHandler(progressId,
@@ -227,110 +293,95 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
                   @Override
                   public boolean cancelActivity(long id)
                   {
-                    cancelCurrentJob();
+                    ((CancellableI) annotService).cancel(running);
                     return true;
                   }
 
                   @Override
                   public boolean canCancel()
                   {
-                    return true;
+                    return cancellable;
                   }
                 });
       }
+      
+      // ///
+      // and poll for updates until job finishes, fails or becomes stale
+      
       boolean finished = false;
       long rpos = 0;
       do
       {
-        JobStatus status = getJobStatus(rslt);
-        if (status.equals(JobStatus.FINISHED))
+        Cache.log.debug("Updating status for annotation service.");
+        annotService.updateStatus(running);
+
+        if (running.isFinished())
         {
+          Cache.log.debug("Analysis service job reported finished.");
           finished = true;
         }
-        if (calcMan.isPending(this) && isInteractiveUpdate())
+        else
         {
-          finished = true;
-          // cancel this job and yield to the new job
-          try
-          {
-            if (cancelJob(rslt))
-            {
-              System.err.println("Cancelled AACon job: " + rslt);
-            }
-            else
-            {
-              System.err.println("FAILED TO CANCEL AACon job: " + rslt);
-            }
-
-          } catch (Exception x)
-          {
-
-          }
-          rslt = "CANCELLED JOB";
-          return;
+          Cache.log.debug("Status now " + running.getState());
         }
-        long cpos;
-        ChunkHolder stats = null;
-        do
+
+        if (calcMan.isPending(this) && isInteractiveUpdate())
         {
-          cpos = rpos;
-          boolean retry = false;
-          do
-          {
+          Cache.log.debug("Analysis service job is stale. aborting.");
+          // job has become stale.
+          if (!finished) {
+            finished = true;
+            // cancel this job and yield to the new job
             try
             {
-              stats = pullExecStatistics(rslt, rpos);
-            } catch (Exception x)
-            {
-
-              if (x.getMessage().contains(
-                      "Position in a file could not be negative!"))
+              if (cancellable
+                        && ((CancellableI) annotService).cancel(running))
               {
-                // squash index out of bounds exception- seems to happen for
-                // disorder predictors which don't (apparently) produce any
-                // progress information and JABA server throws an exception
-                // because progress length is -1.
-                stats = null;
+                System.err.println("Cancelled AACon job: " + rslt);
               }
               else
               {
-                if (--serverErrorsLeft > 0)
-                {
-                  retry = true;
-                  try
-                  {
-                    Thread.sleep(200);
-                  } catch (InterruptedException q)
-                  {
-                  }
-                  ;
-                }
-                else
-                {
-                  throw x;
-                }
+                System.err.println("FAILED TO CANCEL AACon job: " + rslt);
               }
+  
+            } catch (Exception x)
+            {
+  
             }
-          } while (retry);
-          if (stats != null)
-          {
-            System.out.print(stats.getChunk());
-            msg.append(stats);
-            rpos = stats.getNextPosition();
           }
-        } while (stats != null && rpos > cpos);
+          rslt = running.getJobHandle();
+          return;
+        }
+
+        // pull any stats - some services need to flush log output before
+        // results are available
+        Cache.log.debug("Updating progress log for annotation service.");
+
+        try
+        {
+        annotService.updateJobProgress(running);
+        } catch (Throwable thr)
+        {
+          Cache.log.debug("Ignoring exception during progress update.",
+                  thr);
+        }
+        Cache.log.trace("Result of poll: " + running.getStatus());
 
-        if (!finished && status.equals(JobStatus.FAILED))
+        if (!finished && !running.isFailed())
         {
           try
           {
+            Cache.log.debug("Analysis service job thread sleeping.");
             Thread.sleep(200);
+            Cache.log.debug("Analysis service job thread woke.");
           } catch (InterruptedException x)
           {
           }
           ;
         }
       } while (!finished);
+
+      // TODO: need to poll/retry
       if (serverErrorsLeft > 0)
       {
         try
@@ -339,42 +390,71 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
         } catch (InterruptedException x)
         {
         }
-        if (collectAnnotationResultsFor(rslt))
+      }
+      // configure job with the associated view's feature renderer, if one
+      // exists.
+      // TODO: here one would also grab the 'master feature renderer' in order
+      // to enable/disable
+      // features automatically according to user preferences
+      running.setFeatureRenderer(
+              ((jalview.gui.AlignmentPanel) ap).cloneFeatureRenderer());
+      Cache.log.debug("retrieving job results.");
+      List<AlignmentAnnotation> returnedAnnot = annotService
+              .getAlignmentAnnotation(running, this);
+      Cache.log.debug("Obtained " + (returnedAnnot == null ? "no rows"
+              : ("" + returnedAnnot.size())));
+      running.setAnnotation(returnedAnnot);
+
+      if (running.hasResults())
+      {
+        jalview.bin.Cache.log.debug("Updating result annotation from Job "
+                + rslt + " at " + service.getUri());
+        updateResultAnnotation(true);
+        if (running.isTransferSequenceFeatures())
         {
-          jalview.bin.Cache.log.debug("Updating result annotation from Job "
-                  + rslt + " at " + service.getUri());
-          updateResultAnnotation(true);
-          ap.adjustAnnotationHeight();
+          jalview.bin.Cache.log.debug(
+                  "Updating feature display settings and transferring features from Job "
+                          + rslt + " at " + service.getUri());
+          ((jalview.gui.AlignmentPanel) ap)
+                  .updateFeatureRendererFrom(running.getFeatureRenderer());
+          // TODO: JAL-1150 - create sequence feature settings API for defining
+          // styles and enabling/disabling feature overlay on alignment panel
+
+          if (alignFrame.alignPanel == ap)
+          {
+            // only do this if the alignFrame is currently showing this view.
+            Desktop.getAlignFrameFor(alignViewport)
+                    .setShowSeqFeatures(true);
+          }
         }
+        ap.adjustAnnotationHeight();
       }
+      Cache.log.debug("Annotation Service Worker thread finished.");
     }
-
-    catch (JobSubmissionException x)
-    {
-
-      System.err.println(
-              "submission error with " + getServiceActionText() + " :");
-      x.printStackTrace();
-      calcMan.disableWorker(this);
-    } catch (ResultNotAvailableException x)
-    {
-      System.err.println("collection error:\nJob ID: " + rslt);
-      x.printStackTrace();
-      calcMan.disableWorker(this);
-
-    } catch (OutOfMemoryError error)
-    {
-      calcMan.disableWorker(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      ap.raiseOOMWarning(getServiceActionText(), error);
-    } catch (Exception x)
+// TODO: use service specitic exception handlers
+//    catch (JobSubmissionException x)
+//    {
+//
+//      System.err.println(
+//              "submission error with " + getServiceActionText() + " :");
+//      x.printStackTrace();
+//      calcMan.disableWorker(this);
+//    } catch (ResultNotAvailableException x)
+//    {
+//      System.err.println("collection error:\nJob ID: " + rslt);
+//      x.printStackTrace();
+//      calcMan.disableWorker(this);
+//
+//    } catch (OutOfMemoryError error)
+//    {
+//      calcMan.disableWorker(this);
+//
+//      ap.raiseOOMWarning(getServiceActionText(), error);
+//    } 
+    catch (Throwable x)
     {
       calcMan.disableWorker(this);
 
-      // consensus = null;
-      // hconsensus = null;
       System.err
               .println("Blacklisting worker due to unexpected exception:");
       x.printStackTrace();
@@ -408,34 +488,36 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
   }
 
   /**
-   * validate input for dynamic/non-dynamic update context
-   * 
-   * @param dynamic
+   * validate input for dynamic/non-dynamic update context TODO: move to
+   * analysis interface ?
    * @param seqs
+   * 
    * @return true if input is valid
    */
-  abstract boolean checkValidInputSeqs(boolean dynamic,
-          List<FastaSequence> seqs);
-
-  abstract String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException;
-
-  abstract boolean cancelJob(String rslt) throws Exception;
-
-  abstract JobStatus getJobStatus(String rslt) throws Exception;
-
-  abstract ChunkHolder pullExecStatistics(String rslt, long rpos);
-
-  abstract boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException;
+  boolean checkValidInputSeqs(List<SequenceI> seqs)
+  {
+    int nvalid = 0;
+    for (SequenceI sq : seqs)
+    {
+      if (sq.getStart() <= sq.getEnd()
+              && (sq.isProtein() ? proteinAllowed : nucleotidesAllowed))
+      {
+        if (submitGaps
+                || sq.getLength() == (sq.getEnd() - sq.getStart() + 1))
+        {
+          nvalid++;
+        }
+      }
+    }
+    return nvalid >= min_valid_seqs;
+  }
 
   public void cancelCurrentJob()
   {
     try
     {
-      String id = rslt;
-      if (cancelJob(rslt))
+      String id = running.getJobId();
+      if (((CancellableI) annotService).cancel(running))
       {
         System.err.println("Cancelled job " + id);
       }
@@ -457,9 +539,20 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
    * @return true if a running job should be cancelled because new input data is
    *         available for analysis
    */
-  abstract boolean isInteractiveUpdate();
+  boolean isInteractiveUpdate()
+  {
+    return service.isInteractiveUpdate();
+  }
 
-  public List<FastaSequence> getInputSequences(AlignmentI alignment,
+  /**
+   * decide what sequences will be analysed TODO: refactor to generate
+   * List<SequenceI> for submission to service interface
+   * 
+   * @param alignment
+   * @param inputSeqs
+   * @return
+   */
+  public List<SequenceI> getInputSequences(AlignmentI alignment,
           AnnotatedCollectionI inputSeqs)
   {
     if (alignment == null || alignment.getWidth() <= 0
@@ -476,7 +569,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
       inputSeqs = alignment;
     }
 
-    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
+    List<SequenceI> seqs = new ArrayList<>();
 
     int minlen = 10;
     int ln = -1;
@@ -487,7 +580,10 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
     gapMap = new boolean[0];
     start = inputSeqs.getStartRes();
     end = inputSeqs.getEndRes();
-
+    // TODO: URGENT! unify with JPred / MSA code to handle hidden regions
+    // correctly
+    // TODO: push attributes into WsJob instance (so they can be safely
+    // persisted/restored
     for (SequenceI sq : (inputSeqs.getSequences()))
     {
       if (bySequence
@@ -501,12 +597,12 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
         {
           seqNames.put(newname, sq);
         }
-        FastaSequence seq;
+        SequenceI seq;
         if (submitGaps)
         {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
+          seqs.add(seq = new jalview.datamodel.Sequence(newname,
                   sq.getSequenceAsString()));
-          if (gapMap == null || gapMap.length < seq.getSequence().length())
+          if (gapMap == null || gapMap.length < seq.getLength())
           {
             boolean[] tg = gapMap;
             gapMap = new boolean[seq.getLength()];
@@ -530,13 +626,17 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
         }
         else
         {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
+          // TODO: add ability to exclude hidden regions
+          seqs.add(seq = new jalview.datamodel.Sequence(newname,
                   AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
                           sq.getSequenceAsString(start, end + 1))));
+          // for annotation need to also record map to sequence start/end
+          // position in range
+          // then transfer back to original sequence on return.
         }
-        if (seq.getSequence().length() > ln)
+        if (seq.getLength() > ln)
         {
-          ln = seq.getSequence().length();
+          ln = seq.getLength();
         }
       }
     }
@@ -555,11 +655,10 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
       // acids at each position, and AAcon doesn't gracefully degrade.
       for (int p = 0; p < seqs.size(); p++)
       {
-        FastaSequence sq = seqs.get(p);
-        int l = sq.getSequence().length();
+        SequenceI sq = seqs.get(p);
         // strip gapped columns
         char[] padded = new char[realw],
-                orig = sq.getSequence().toCharArray();
+                orig = sq.getSequence();
         for (int i = 0, pp = 0; i < realw; pp++)
         {
           if (gapMap[pp])
@@ -574,7 +673,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
             }
           }
         }
-        seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
+        seqs.set(p, new jalview.datamodel.Sequence(sq.getName(),
                 new String(padded)));
       }
     }
@@ -587,9 +686,15 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
     updateResultAnnotation(false);
   }
 
-  public abstract void updateResultAnnotation(boolean immediate);
-
-  public abstract String getServiceActionText();
+  public void updateResultAnnotation(boolean immediate)
+  {
+    if ((immediate || !calcMan.isWorking(this)) && running != null
+            && running.hasResults())
+    {
+      List<AlignmentAnnotation> ourAnnot = running.getAnnotation();
+      updateOurAnnots(ourAnnot);
+    }
+  }
 
   /**
    * notify manager that we have started, and wait for a free calculation slot
@@ -647,7 +752,13 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
         }
       }
       our.clear();
-
+      // validate rows and update Alignmment state
+      for (AlignmentAnnotation an : ourAnnots)
+      {
+        alignViewport.getAlignment().validateAnnotation(an);
+      }
+      // TODO: may need a menu refresh after this
+      // af.setMenusForViewport();
       ap.adjustAnnotationHeight();
     }
   }
index 2094202..36f927d 100644 (file)
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
-import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
 import jalview.ws.api.ServiceWithParameters;
-import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
-import jalview.ws.uimodel.AlignAnalysisUIText;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.util.List;
 
-import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
 
 /**
  * provides metadata for a jabaws2 service instance - resolves names, etc.
@@ -50,33 +37,18 @@ import javax.swing.event.MenuListener;
  */
 public abstract class Jws2Client extends jalview.ws.WSClient
 {
+  /**
+   * instantiate a new service client. preset and arguments are assumed to be
+   * valid for the service
+   * 
+   * @param _alignFrame
+   * @param preset
+   * @param arguments
+   */
   public Jws2Client(AlignFrame _alignFrame, WsParamSetI preset,
           List<ArgumentI> arguments)
   {
     super(_alignFrame, preset, arguments);
-    if (preset != null)
-    {
-//      if (!((preset instanceof JabaPreset)
-//              || preset instanceof JabaWsParamSet))
-//      {
-//        /*
-//         * { this.preset = ((JabaPreset) preset).p; } else if (preset instanceof
-//         * JabaWsParamSet) { List<Argument> newargs = new ArrayList<Argument>();
-//         * JabaWsParamSet pset = ((JabaWsParamSet) preset); for (Option opt :
-//         * pset.getjabaArguments()) { newargs.add(opt); } if (arguments != null
-//         * && arguments.size() > 0) { // merge arguments with preset's own
-//         * arguments. for (Argument opt : arguments) { newargs.add(opt); } }
-//         * paramset = newargs; } else {
-//         */
-//        throw new Error(MessageManager.getString(
-//                "error.implementation_error_can_only_instantiate_jaba_param_sets"));
-//      }
-    }
-    else
-    {
-      // TODO: verify that paramset is compatible with the service being
-      // contacted
-    }
   }
 
   public Jws2Client()
@@ -105,254 +77,4 @@ public abstract class Jws2Client extends jalview.ws.WSClient
   abstract void attachWSMenuEntry(JMenu wsmenu,
           final ServiceWithParameters service,
           final AlignFrame alignFrame);
-
-  static boolean registerAAConWSInstance(final JMenu wsmenu,
-          final ServiceWithParameters service, final AlignFrame alignFrame)
-  {
-    Jws2Instance jaba_service = (Jws2Instance) service;
-    final AlignAnalysisUIText aaui = jaba_service.getAlignAnalysisUI(); // null ; //
-                                                                   // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
-    if (aaui == null)
-    {
-      // not an instantaneous calculation GUI type service
-      return false;
-    }
-    // create the instaneous calculation GUI bits and update state if existing
-    // GUI elements already present
-
-    JCheckBoxMenuItem _aaConEnabled = null;
-    for (int i = 0; i < wsmenu.getItemCount(); i++)
-    {
-      JMenuItem item = wsmenu.getItem(i);
-      if (item instanceof JCheckBoxMenuItem
-              && item.getText().equals(aaui.getAAconToggle()))
-      {
-        _aaConEnabled = (JCheckBoxMenuItem) item;
-      }
-    }
-    // is there an aaCon worker already present - if so, set it to use the
-    // given service handle
-    {
-      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-              .getCalcManager()
-              .getRegisteredWorkersOfClass(aaui.getClient());
-      if (aaconClient != null && aaconClient.size() > 0)
-      {
-        AbstractJabaCalcWorker worker = (AbstractJabaCalcWorker) aaconClient
-                .get(0);
-        if (!worker.service.getHostURL().equals(service.getHostURL()))
-        {
-          // javax.swing.SwingUtilities.invokeLater(new Runnable()
-          {
-            // @Override
-            // public void run()
-            {
-              removeCurrentAAConWorkerFor(aaui, alignFrame);
-              buildCurrentAAConWorkerFor(aaui, alignFrame, jaba_service);
-            }
-          } // );
-        }
-      }
-    }
-
-    // is there a service already registered ? there shouldn't be if we are
-    // being called correctly
-    if (_aaConEnabled == null)
-    {
-      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
-              aaui.getAAconToggle());
-
-      aaConEnabled.setToolTipText(
-              JvSwingUtils.wrapTooltip(true, aaui.getAAconToggleTooltip()));
-      aaConEnabled.addActionListener(new ActionListener()
-      {
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            removeCurrentAAConWorkerFor(aaui, alignFrame);
-          }
-          else
-          {
-            buildCurrentAAConWorkerFor(aaui, alignFrame);
-
-          }
-        }
-
-      });
-      wsmenu.add(aaConEnabled);
-      final JMenuItem modifyParams = new JMenuItem(
-              aaui.getAAeditSettings());
-      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
-              aaui.getAAeditSettingsTooltip()));
-      modifyParams.addActionListener(new ActionListener()
-      {
-
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          showAAConAnnotationSettingsFor(aaui, alignFrame);
-        }
-      });
-      wsmenu.add(modifyParams);
-      wsmenu.addMenuListener(new MenuListener()
-      {
-
-        @Override
-        public void menuSelected(MenuEvent arg0)
-        {
-          // TODO: refactor to the implementing class.
-          if (alignFrame.getViewport().getAlignment().isNucleotide()
-                  ? aaui.isNa()
-                  : aaui.isPr())
-          {
-            aaConEnabled.setEnabled(true);
-            modifyParams.setEnabled(true);
-          }
-          else
-          {
-            aaConEnabled.setEnabled(false);
-            modifyParams.setEnabled(false);
-          }
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            aaConEnabled.setSelected(true);
-          }
-          else
-          {
-            aaConEnabled.setSelected(false);
-          }
-        }
-
-        @Override
-        public void menuDeselected(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void menuCanceled(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-      });
-
-    }
-    return true;
-  }
-
-  private static void showAAConAnnotationSettingsFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    // could actually do a class search for this too
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui);
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, true);
-
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
-          Jws2Instance service)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui, service);
-    }
-    else
-    {
-      if (service != null
-              && !fave.getService().getHostURL()
-                      .equals(service.getHostURL()))
-      {
-        Cache.log.debug("Changing AACon service to " + service.getHostURL()
-                + " from " + fave.getService().getHostURL());
-        fave.setService(service);
-      }
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, false);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui)
-  {
-    return createDefaultAAConSettings(aaui, null);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui, Jws2Instance service)
-  {
-    if (service != null)
-    {
-      if (!service.getServiceType()
-              .equals(compbio.ws.client.Services.AAConWS.toString()))
-      {
-        Cache.log.warn(
-                "Ignoring invalid preferred service for AACon calculations (service type was "
-                        + service.getServiceType() + ")");
-        service = null;
-      }
-      else
-      {
-        // check service is actually in the list of currently avaialable
-        // services
-        if (!Jws2Discoverer.getDiscoverer().getServices().contains(service))
-        {
-          // it isn't ..
-          service = null;
-        }
-      }
-    }
-    if (service == null)
-    {
-      // get the default service for AACon
-      service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null,
-              aaui.getServiceType());
-    }
-    if (service == null)
-    {
-      // TODO raise dialog box explaining error, and/or open the JABA
-      // preferences menu.
-      throw new Error(
-              MessageManager.getString("error.no_aacon_service_found"));
-    }
-    return new AAConSettings(true, service, null, null);
-  }
-
-  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
-          AlignFrame alignFrame)
-  {
-    alignFrame.getViewport().getCalcManager()
-            .removeRegisteredWorkersOfClass(aaui.getClient());
-  }
-
 }
diff --git a/src/jalview/ws/jws2/Jws2ClientFactory.java b/src/jalview/ws/jws2/Jws2ClientFactory.java
new file mode 100644 (file)
index 0000000..4af8124
--- /dev/null
@@ -0,0 +1,273 @@
+package jalview.ws.jws2;
+
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.Cache;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.ws.api.ServiceWithParameters;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.uimodel.AlignAnalysisUIText;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+
+public class Jws2ClientFactory
+{
+  static boolean registerAAConWSInstance(final JMenu wsmenu,
+          final ServiceWithParameters service, final AlignFrame alignFrame)
+  {
+    Jws2Instance jaba_service = (Jws2Instance) service;
+    final AlignAnalysisUIText aaui = jaba_service.getAlignAnalysisUI(); // null
+                                                                        // ; //
+    // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
+    if (aaui == null)
+    {
+      // not an instantaneous calculation GUI type service
+      return false;
+    }
+    // create the instaneous calculation GUI bits and update state if existing
+    // GUI elements already present
+
+    JCheckBoxMenuItem _aaConEnabled = null;
+    for (int i = 0; i < wsmenu.getItemCount(); i++)
+    {
+      JMenuItem item = wsmenu.getItem(i);
+      if (item instanceof JCheckBoxMenuItem
+              && item.getText().equals(aaui.getAAconToggle()))
+      {
+        _aaConEnabled = (JCheckBoxMenuItem) item;
+      }
+    }
+    // is there an aaCon worker already present - if so, set it to use the
+    // given service handle
+    {
+      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+              .getCalcManager()
+              .getRegisteredWorkersOfClass(aaui.getClient());
+      if (aaconClient != null && aaconClient.size() > 0)
+      {
+        AbstractJabaCalcWorker worker = (AbstractJabaCalcWorker) aaconClient
+                .get(0);
+        if (!worker.service.getHostURL().equals(service.getHostURL()))
+        {
+          // javax.swing.SwingUtilities.invokeLater(new Runnable()
+          {
+            // @Override
+            // public void run()
+            {
+              removeCurrentAAConWorkerFor(aaui, alignFrame);
+              buildCurrentAAConWorkerFor(aaui, alignFrame, jaba_service);
+            }
+          } // );
+        }
+      }
+    }
+
+    // is there a service already registered ? there shouldn't be if we are
+    // being called correctly
+    if (_aaConEnabled == null)
+    {
+      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
+              aaui.getAAconToggle());
+
+      aaConEnabled.setToolTipText(
+              JvSwingUtils.wrapTooltip(true, aaui.getAAconToggleTooltip()));
+      aaConEnabled.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getRegisteredWorkersOfClass(aaui.getClient());
+          if (aaconClient != null && aaconClient.size() > 0)
+          {
+            removeCurrentAAConWorkerFor(aaui, alignFrame);
+          }
+          else
+          {
+            buildCurrentAAConWorkerFor(aaui, alignFrame);
+
+          }
+        }
+
+      });
+      wsmenu.add(aaConEnabled);
+      final JMenuItem modifyParams = new JMenuItem(
+              aaui.getAAeditSettings());
+      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              aaui.getAAeditSettingsTooltip()));
+      modifyParams.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+          showAAConAnnotationSettingsFor(aaui, alignFrame);
+        }
+      });
+      wsmenu.add(modifyParams);
+      wsmenu.addMenuListener(new MenuListener()
+      {
+
+        @Override
+        public void menuSelected(MenuEvent arg0)
+        {
+          // TODO: refactor to the implementing class.
+          if (alignFrame.getViewport().getAlignment().isNucleotide()
+                  ? aaui.isNa()
+                  : aaui.isPr())
+          {
+            aaConEnabled.setEnabled(true);
+            modifyParams.setEnabled(true);
+          }
+          else
+          {
+            aaConEnabled.setEnabled(false);
+            modifyParams.setEnabled(false);
+          }
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getRegisteredWorkersOfClass(aaui.getClient());
+          if (aaconClient != null && aaconClient.size() > 0)
+          {
+            aaConEnabled.setSelected(true);
+          }
+          else
+          {
+            aaConEnabled.setSelected(false);
+          }
+        }
+
+        @Override
+        public void menuDeselected(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void menuCanceled(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+      });
+
+    }
+    return true;
+  }
+
+  private static void showAAConAnnotationSettingsFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    // could actually do a class search for this too
+    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui);
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, true);
+
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
+          Jws2Instance service)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui, service);
+    }
+    else
+    {
+      if (service != null && !fave.getService().getHostURL()
+              .equals(service.getHostURL()))
+      {
+        Cache.log.debug("Changing AACon service to " + service.getHostURL()
+                + " from " + fave.getService().getHostURL());
+        fave.setService(service);
+      }
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, false);
+  }
+
+  private static AAConSettings createDefaultAAConSettings(
+          AlignAnalysisUIText aaui)
+  {
+    return createDefaultAAConSettings(aaui, null);
+  }
+
+  private static AAConSettings createDefaultAAConSettings(
+          AlignAnalysisUIText aaui, Jws2Instance service)
+  {
+    if (service != null)
+    {
+      if (!service.getServiceType()
+              .equals(compbio.ws.client.Services.AAConWS.toString()))
+      {
+        Cache.log.warn(
+                "Ignoring invalid preferred service for AACon calculations (service type was "
+                        + service.getServiceType() + ")");
+        service = null;
+      }
+      else
+      {
+        // check service is actually in the list of currently avaialable
+        // services
+        if (!Jws2Discoverer.getDiscoverer().getServices().contains(service))
+        {
+          // it isn't ..
+          service = null;
+        }
+      }
+    }
+    if (service == null)
+    {
+      // get the default service for AACon
+      service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null,
+              aaui.getServiceType());
+    }
+    if (service == null)
+    {
+      // TODO raise dialog box explaining error, and/or open the JABA
+      // preferences menu.
+      throw new Error(
+              MessageManager.getString("error.no_aacon_service_found"));
+    }
+    return new AAConSettings(true, service, null, null);
+  }
+
+  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
+          AlignFrame alignFrame)
+  {
+    alignFrame.getViewport().getCalcManager()
+            .removeRegisteredWorkersOfClass(aaui.getClient());
+  }
+}
\ No newline at end of file
index 83d6d16..3c57ca8 100644 (file)
@@ -236,7 +236,7 @@ public class MsaWSClient extends Jws2Client implements WSMenuEntryProviderI
   public void attachWSMenuEntry(JMenu rmsawsmenu,
           final ServiceWithParameters service, final AlignFrame alignFrame)
   {
-    if (registerAAConWSInstance(rmsawsmenu, service,
+    if (Jws2ClientFactory.registerAAConWSInstance(rmsawsmenu, service,
             alignFrame))
     {
       // Alignment dependent analysis calculation WS gui
index fda9ab5..493a73b 100644 (file)
@@ -93,12 +93,8 @@ public class SequenceAnnotationWSClient extends Jws2Client
         }
         try
         {
-          worker = (AbstractJabaCalcWorker) (clientClass
-                  .getConstructor(new Class[]
-                  { Jws2Instance.class, AlignFrame.class, WsParamSetI.class,
-                      List.class })
-                  .newInstance(new Object[]
-                  { sh, alignFrame, this.preset, paramset }));
+          worker = new AbstractJabaCalcWorker(sh, alignFrame, this.preset,
+                  paramset);
         } catch (Exception x)
         {
           x.printStackTrace();
@@ -139,7 +135,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
       }
 
       alignFrame.getViewport().getCalcManager().startWorker(
-              new AADisorderClient(sh, alignFrame, preset, paramset));
+              new AbstractJabaCalcWorker(sh, alignFrame, preset, paramset));
     }
   }
 
@@ -162,7 +158,8 @@ public class SequenceAnnotationWSClient extends Jws2Client
           final ServiceWithParameters service,
           final AlignFrame alignFrame)
   {
-    if (registerAAConWSInstance(wsmenu, service, alignFrame))
+    if (Jws2ClientFactory.registerAAConWSInstance(wsmenu, service,
+            alignFrame))
     {
       // Alignment dependent analysis calculation WS gui
       return;
diff --git a/src/jalview/ws/jws2/jabaws2/AAConClient.java b/src/jalview/ws/jws2/jabaws2/AAConClient.java
new file mode 100644 (file)
index 0000000..f0a4b67
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.jws2.jabaws2;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.util.MessageManager;
+import jalview.ws.gui.AnnotationWsJob;
+import jalview.ws.uimodel.AlignAnalysisUIText;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import compbio.data.sequence.Score;
+
+public class AAConClient extends JabawsAnnotationInstance
+{
+  // configuration for factory
+  public static String getServiceActionText()
+  {
+    return "calculating Amino acid consensus using AACon service";
+  }
+
+
+
+  private static String CALC_ID = "jabaws2.AACon";
+
+  public static AlignAnalysisUIText getAlignAnalysisUIText()
+  {
+    return new AlignAnalysisUIText(
+            compbio.ws.client.Services.AAConWS.toString(),
+            AAConClient.class, CALC_ID, false, true, true, true,
+            true, 2, MessageManager.getString("label.aacon_calculations"),
+            MessageManager.getString("tooltip.aacon_calculations"),
+            MessageManager.getString("label.aacon_settings"),
+            MessageManager.getString("tooltip.aacon_settings"));
+  }
+
+  // instance
+  public AAConClient(Jws2Instance handle)
+  {
+    super(handle);
+  }
+
+  @Override
+  List<AlignmentAnnotation> annotationFromScoreManager(
+          AnnotationWsJob running, AlignViewportI alignViewport,
+          boolean[] gapMap)
+  {
+
+    Map<String, TreeSet<Score>> scoremap = scoremanager.asMap();
+    int alWidth = alignViewport.getAlignment().getWidth();
+    ArrayList<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+    for (String score : scoremap.keySet())
+    {
+      Set<Score> scores = scoremap.get(score);
+      for (Score scr : scores)
+      {
+        if (scr.getRanges() != null && scr.getRanges().size() > 0)
+        {
+          /**
+           * annotation in range annotation = findOrCreate(scr.getMethod(),
+           * true, null, null); Annotation[] elm = new Annotation[alWidth];
+           * Iterator<Float> vals = scr.getScores().iterator(); for (Range rng :
+           * scr.getRanges()) { float val = vals.next().floatValue(); for (int i
+           * = rng.from; i <= rng.to; i++) { elm[i] = new Annotation("", "", '
+           * ', val); } } annotation.annotations = elm;
+           * annotation.validateRangeAndDisplay();
+           */
+        }
+        else
+        {
+          createAnnotationRowsForScores(alignViewport, gapMap, ourAnnot,
+                  getCalcId(), alWidth, scr);
+        }
+      }
+    }
+    return ourAnnot;
+  }
+
+}
diff --git a/src/jalview/ws/jws2/jabaws2/AADisorderClient.java b/src/jalview/ws/jws2/jabaws2/AADisorderClient.java
new file mode 100644 (file)
index 0000000..6e65cce
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.jws2.jabaws2;
+
+import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+import jalview.ws.gui.AnnotationWsJob;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.sequence.Range;
+import compbio.data.sequence.Score;
+import compbio.data.sequence.ScoreManager.ScoreHolder;
+
+public class AADisorderClient extends JabawsAnnotationInstance
+{
+  // static configuration
+  public static String getServiceActionText()
+  {
+    return "Submitting amino acid sequences for disorder prediction.";
+  }
+
+  // minSeq = 1; protein only, no gaps
+
+  // instance
+  public AADisorderClient(Jws2Instance handle)
+  {
+    super(handle);
+
+  }
+
+  @Override
+  List<AlignmentAnnotation> annotationFromScoreManager(
+          AnnotationWsJob running, AlignViewportI alignViewport,
+          boolean[] gapMap)
+  {
+
+    Map<String, String[]> featureTypeMap = featureMap.get(our.getName());
+    Map<String, Map<String, Object>> annotTypeMap = annotMap
+            .get(our.getName());
+    boolean dispFeatures = false;
+    Map<String, Object> fc = new Hashtable<>();
+    List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+    /**
+     * grouping for any annotation rows created
+     */
+    int graphGroup = 1;
+    if (alignViewport.getAlignment().getAlignmentAnnotation() != null)
+    {
+      for (AlignmentAnnotation ala : alignViewport.getAlignment()
+              .getAlignmentAnnotation())
+      {
+        if (ala.graphGroup > graphGroup)
+        {
+          graphGroup = ala.graphGroup;
+        }
+      }
+    }
+
+    // TODO: downgrade code below so we don't annotate sequences directly
+    Map<String, SequenceI> seqNames = running.getSeqNames();
+    int start = running.getStartPos();
+    for (String seqId : seqNames.keySet())
+    {
+      boolean sameGroup = false;
+      SequenceI dseq, aseq, seq = seqNames.get(seqId);
+      int base = seq.findPosition(start) - 1;
+      aseq = seq;
+      while ((dseq = seq).getDatasetSequence() != null)
+      {
+        seq = seq.getDatasetSequence();
+      }
+      ScoreHolder scores = null;
+      try
+      {
+        scores = scoremanager.getAnnotationForSequence(seqId);
+      } catch (Exception q)
+      {
+        Cache.log.info("Couldn't recover disorder prediction for sequence "
+                + seq.getName() + "(Prediction name was " + seqId + ")"
+                + "\nSee http://issues.jalview.org/browse/JAL-1319 for one possible reason why disorder predictions might fail.");
+      }
+      float last = Float.NaN, val = Float.NaN;
+      int lastAnnot = ourAnnot.size();
+      if (scores != null && scores.scores != null)
+      {
+        for (Score scr : scores.scores)
+        {
+
+          if (scr.getRanges() != null && scr.getRanges().size() > 0)
+          {
+            Iterator<Float> vals = scr.getScores().iterator();
+            // make features on sequence
+            for (Range rn : scr.getRanges())
+            {
+
+              SequenceFeature sf;
+              String[] type = featureTypeMap.get(scr.getMethod());
+              if (type == null)
+              {
+                // create a default type for this feature
+                type = new String[] {
+                    typeName + " (" + scr.getMethod() + ")",
+                    our.getActionText() };
+              }
+              if (vals.hasNext())
+              {
+                val = vals.next().floatValue();
+                sf = new SequenceFeature(type[0], type[1], base + rn.from,
+                        base + rn.to, val, methodName);
+              }
+              else
+              {
+                sf = new SequenceFeature(type[0], type[1], base + rn.from,
+                        base + rn.to, methodName);
+              }
+              dseq.addSequenceFeature(sf);
+              if (last != val && !Float.isNaN(last))
+              {
+                fc.put(sf.getType(), sf);
+              }
+              last = val;
+              running.setTransferSequenceFeatures(true);
+              dispFeatures = true;
+            }
+          }
+          else
+          {
+            if (scr.getScores().size() == 0)
+            {
+              continue;
+            }
+            String typename, calcName;
+            AlignmentAnnotation annot = createAnnotationRowsForScores(
+                    alignViewport, gapMap, ourAnnot,
+                    typename = our.getName() + " (" + scr.getMethod() + ")",
+                    calcName = our.getNameURI() + "/" + scr.getMethod(),
+                    aseq, base + 1, scr);
+            annot.graph = AlignmentAnnotation.LINE_GRAPH;
+
+            Map<String, Object> styleMap = (annotTypeMap == null) ? null
+                    : annotTypeMap.get(scr.getMethod());
+
+            annot.visible = (styleMap == null
+                    || styleMap.get(INVISIBLE) == null);
+            double[] thrsh = (styleMap == null) ? null
+                    : (double[]) styleMap.get(THRESHOLD);
+            float[] range = (styleMap == null) ? null
+                    : (float[]) styleMap.get(RANGE);
+            if (range != null)
+            {
+              annot.graphMin = range[0];
+              annot.graphMax = range[1];
+            }
+            if (styleMap == null || styleMap.get(DONTCOMBINE) == null)
+            {
+              {
+                if (!sameGroup)
+                {
+                  graphGroup++;
+                  sameGroup = true;
+                }
+
+                annot.graphGroup = graphGroup;
+              }
+            }
+
+            annot.description = "<html>" + our.getActionText()
+                    + " - raw scores";
+            if (thrsh != null)
+            {
+              String threshNote = (thrsh[0] > 0 ? "Above " : "Below ")
+                      + thrsh[1] + " indicates disorder";
+              annot.threshold = new GraphLine((float) thrsh[1], threshNote,
+                      Color.red);
+              annot.description += "<br/>" + threshNote;
+            }
+            annot.description += "</html>";
+            Color col = ColorUtils
+                    .createColourFromName(typeName + scr.getMethod());
+            for (int p = 0, ps = annot.annotations.length; p < ps; p++)
+            {
+              if (annot.annotations[p] != null)
+              {
+                annot.annotations[p].colour = col;
+              }
+            }
+            annot._linecolour = col;
+            // finally, update any dataset annotation
+            replaceAnnotationOnAlignmentWith(annot, typename, calcName,
+                    aseq);
+          }
+        }
+      }
+      if (lastAnnot + 1 == ourAnnot.size())
+      {
+        // remove singleton alignment annotation row
+        ourAnnot.get(lastAnnot).graphGroup = -1;
+      }
+    }
+    {
+      if (dispFeatures)
+      {
+        jalview.api.FeatureRenderer fr = running.getFeatureRenderer();
+        for (String ft : fc.keySet())
+        {
+          FeatureColourI gc = fr.getFeatureStyle(ft);
+          if (gc.isSimpleColour())
+          {
+            // set graduated color as fading to white for minimum, and
+            // autoscaling to values on alignment
+            FeatureColourI ggc = new FeatureColour(gc.getColour(),
+                    Color.white, gc.getColour(), Color.white,
+                    Float.MIN_VALUE, Float.MAX_VALUE);
+            ggc.setAutoScaled(true);
+            fr.setColour(ft, ggc);
+          }
+        }
+
+      }
+      return ourAnnot;
+    }
+  }
+
+  private static final String THRESHOLD = "THRESHOLD";
+
+  private static final String RANGE = "RANGE";
+
+  String typeName;
+
+  String methodName;
+
+  String groupName;
+
+  private static Map<String, Map<String, String[]>> featureMap;
+
+  private static Map<String, Map<String, Map<String, Object>>> annotMap;
+
+  private static String DONTCOMBINE = "DONTCOMBINE";
+
+  private static String INVISIBLE = "INVISIBLE";
+  static
+  {
+    // TODO: turn this into some kind of configuration file that's a bit easier
+    // to edit
+    featureMap = new HashMap<>();
+    Map<String, String[]> fmap;
+    featureMap.put(compbio.ws.client.Services.IUPredWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("Glob",
+            new String[]
+            { "Globular Domain", "Predicted globular domain" });
+    featureMap.put(compbio.ws.client.Services.JronnWS.toString(),
+            fmap = new HashMap<>());
+    featureMap.put(compbio.ws.client.Services.DisemblWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("REM465", new String[] { "REM465", "Missing density" });
+    fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" });
+    fmap.put("COILS", new String[] { "COILS", "Random coil" });
+    featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
+            fmap = new HashMap<>());
+    fmap.put("GlobDoms",
+            new String[]
+            { "Globular Domain", "Predicted globular domain" });
+    fmap.put("Disorder",
+            new String[]
+            { "Protein Disorder", "Probable unstructured peptide region" });
+    Map<String, Map<String, Object>> amap;
+    annotMap = new HashMap<>();
+    annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(),
+            amap = new HashMap<>());
+    amap.put("Dydx", new HashMap<String, Object>());
+    amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE);
+    amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 });
+    amap.get("Dydx").put(RANGE, new float[] { -1, +1 });
+
+    amap.put("SmoothedScore", new HashMap<String, Object>());
+    amap.get("SmoothedScore").put(INVISIBLE, INVISIBLE);
+    amap.put("RawScore", new HashMap<String, Object>());
+    amap.get("RawScore").put(INVISIBLE, INVISIBLE);
+    annotMap.put(compbio.ws.client.Services.DisemblWS.toString(),
+            amap = new HashMap<>());
+    amap.put("COILS", new HashMap<String, Object>());
+    amap.put("HOTLOOPS", new HashMap<String, Object>());
+    amap.put("REM465", new HashMap<String, Object>());
+    amap.get("COILS").put(THRESHOLD, new double[] { 1, 0.516 });
+    amap.get("COILS").put(RANGE, new float[] { 0, 1 });
+
+    amap.get("HOTLOOPS").put(THRESHOLD, new double[] { 1, 0.6 });
+    amap.get("HOTLOOPS").put(RANGE, new float[] { 0, 1 });
+    amap.get("REM465").put(THRESHOLD, new double[] { 1, 0.1204 });
+    amap.get("REM465").put(RANGE, new float[] { 0, 1 });
+
+    annotMap.put(compbio.ws.client.Services.IUPredWS.toString(),
+            amap = new HashMap<>());
+    amap.put("Long", new HashMap<String, Object>());
+    amap.put("Short", new HashMap<String, Object>());
+    amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("Long").put(RANGE, new float[] { 0, 1 });
+    amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("Short").put(RANGE, new float[] { 0, 1 });
+    annotMap.put(compbio.ws.client.Services.JronnWS.toString(),
+            amap = new HashMap<>());
+    amap.put("JRonn", new HashMap<String, Object>());
+    amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 });
+    amap.get("JRonn").put(RANGE, new float[] { 0, 1 });
+  }
+
+}
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws.jws2;
+package jalview.ws.jws2.jabaws2;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
 import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.api.JobId;
+import jalview.ws.api.SequenceAnnotationServiceI;
+import jalview.ws.gui.AnnotationWsJob;
+import jalview.ws.jws2.AbstractJabaCalcWorker;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
 import compbio.data.msa.SequenceAnnotation;
+import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.Score;
 import compbio.data.sequence.ScoreManager;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
 import compbio.metadata.JobSubmissionException;
-import compbio.metadata.ResultNotAvailableException;
 import compbio.metadata.WrongParameterException;
 
-public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
+public abstract class JabawsAnnotationInstance
+        extends JabawsServiceInstance<SequenceAnnotation>
+        implements SequenceAnnotationServiceI
 {
 
-  @SuppressWarnings("unchecked")
-  protected SequenceAnnotation aaservice;
+  /**
+   * holds last results obtained when non-null. TODO: remove this as a field ?
+   */
+  protected ScoreManager scoremanager = null;
 
-  protected ScoreManager scoremanager;
-
-  public JabawsCalcWorker(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<ArgumentI> paramset)
+  public JabawsAnnotationInstance(Jws2Instance handle)
   {
-    super(service, alignFrame, preset, paramset);
-    aaservice = (SequenceAnnotation) service.service;
+    super(handle);
   }
 
-  @Override
-  ChunkHolder pullExecStatistics(String rslt, long rpos)
+
+  /**
+   * 
+   * @return the calcId for this Jabaws Service (convenience method).
+   * 
+   *         TODO: decide if this is really convenient since manager and
+   *         instance have same method !
+   */
+  public String getCalcId()
   {
-    return aaservice.pullExecStatistics(rslt, rpos);
+    return our.aaui == null ? null : our.aaui.getCalcId();
   }
 
   @Override
-  boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException
+  public JobId submitToService(List<SequenceI> seqs, WsParamSetI preset,
+          List<ArgumentI> arguments) throws Throwable
   {
-    scoremanager = aaservice.getAnnotation(rslt);
-    if (scoremanager != null)
+    String rslt = null;
+    scoremanager = null;
+    List<FastaSequence> jabaseqs = new ArrayList(seqs.size());
+    for (SequenceI seq : seqs)
     {
-      return true;
+      jabaseqs.add(
+              new FastaSequence(seq.getName(), seq.getSequenceAsString()));
     }
-    return false;
-  }
-
-  @Override
-  boolean cancelJob(String rslt) throws Exception
-  {
-    return aaservice.cancelJob(rslt);
-  }
-
-  @Override
-  protected JobStatus getJobStatus(String rslt) throws Exception
-  {
-    return aaservice.getJobStatus(rslt);
-  }
-
-  @Override
-  boolean hasService()
-  {
-    return aaservice != null;
-  }
-
-  @Override
-  protected boolean isInteractiveUpdate()
-  {
-    return this instanceof AAConClient;
-  }
-
-  @Override
-  protected String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException
-  {
-    String rslt;
     if (preset == null && arguments == null)
     {
-      rslt = aaservice.analize(seqs);
+      rslt = service.analize(jabaseqs);
     }
-    else
+    if (preset != null)
+    {
+      if (preset instanceof JabaPreset)
+      {
+        // TODO: verify behaviour is really the same, since preset analyze was
+        // never called in Jalview 2.11.x
+        rslt = service.presetAnalize(jabaseqs,
+                ((JabaPreset) preset).getJabaPreset());
+      }
+      else
+      {
+        rslt = service.customAnalize(jabaseqs,
+                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
+      }
+    }
+    else if (arguments != null && arguments.size() > 0)
     {
       try
       {
-        rslt = aaservice.customAnalize(seqs, getJabaArguments());
+        rslt = service.customAnalize(jabaseqs,
+                JabaParamStore.getJabafromJwsArgs(arguments));
       } catch (WrongParameterException x)
       {
         throw new JobSubmissionException(MessageManager.getString(
@@ -120,12 +97,44 @@ public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
 
       }
     }
-    return rslt;
+
+    if (rslt == null)
+    {
+      return null;
+    }
+    return new JobId(our.getServiceType(), our.getName(), rslt);
   }
 
-  protected void createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
-          Score scr)
+  @Override
+  public List<AlignmentAnnotation> getAlignmentAnnotation(
+          AnnotationWsJob running,
+          AbstractJabaCalcWorker abstractJabaCalcWorker) throws Throwable
+  {
+    if (scoremanager == null)
+    {
+      // TODO: raise annotation unavailable exception ?
+      scoremanager = service.getAnnotation(running.getJobId());
+    }
+    if (scoremanager == null)
+    {
+      return List.of();
+    }
+
+    return annotationFromScoreManager(running,
+            abstractJabaCalcWorker.getAlignViewport(),
+            abstractJabaCalcWorker.getGapMap());
+  }
+
+  abstract List<AlignmentAnnotation> annotationFromScoreManager(
+          AnnotationWsJob running,
+          AlignViewportI alignViewport, boolean[] gapMap);
+
+  // From JabawsCalcWorker
+
+  protected void createAnnotationRowsForScores(AlignViewportI alignViewport,
+          boolean[] gapMap, List<AlignmentAnnotation> ourAnnot,
+          String calcId,
+          int alWidth, Score scr)
   {
     // simple annotation row
     AlignmentAnnotation annotation = alignViewport.getAlignment()
@@ -133,12 +142,14 @@ public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
                     null);
     if (alWidth == gapMap.length) // scr.getScores().size())
     {
-      constructAnnotationFromScore(annotation, 0, alWidth, scr);
+      constructAnnotationFromScore(gapMap, annotation, 0,
+              alWidth, scr);
       ourAnnot.add(annotation);
     }
   }
 
   protected AlignmentAnnotation createAnnotationRowsForScores(
+          AlignViewportI alignViewport, boolean[] gapMap,
           List<AlignmentAnnotation> ourAnnot, String typeName,
           String calcId, SequenceI dseq, int base, Score scr)
   {
@@ -151,7 +162,8 @@ public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
     // annotation.setCalcId(calcId);
     AlignmentAnnotation annotation = alignViewport.getAlignment()
             .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
-    constructAnnotationFromScore(annotation, 0, dseq.getLength(), scr);
+    constructAnnotationFromScore(gapMap, annotation, 0, dseq.getLength(),
+            scr);
     annotation.createSequenceMapping(dseq, base, false);
     annotation.adjustForAlignment();
     dseq.addAlignmentAnnotation(annotation);
@@ -183,7 +195,8 @@ public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
     dssan.adjustForAlignment();
   }
 
-  private void constructAnnotationFromScore(AlignmentAnnotation annotation,
+  protected void constructAnnotationFromScore(boolean[] gapMap,
+          AlignmentAnnotation annotation,
           int base, int alWidth, Score scr)
   {
     Annotation[] elm = new Annotation[alWidth];
@@ -212,7 +225,9 @@ public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
       // if we're at a gapped column then skip to next ungapped position
       if (gapMap != null && gapMap.length > 0)
       {
-        while (!gapMap[i])
+        // if gapMap is a different length to the result then it may be out of
+        // sync with the job.
+        while (i < gapMap.length && !gapMap[i])
         {
           elm[i++] = new Annotation("", "", ' ', Float.NaN);
         }
index 80de5b2..cbb4b05 100644 (file)
@@ -1,19 +1,13 @@
 package jalview.ws.jws2.jabaws2;
 
-import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
-import jalview.gui.WebserviceInfo;
-import jalview.util.MessageManager;
 import jalview.ws.api.CancellableI;
 import jalview.ws.api.JobId;
 import jalview.ws.api.MultipleSequenceAlignmentI;
-import jalview.ws.gui.WsJob;
-import jalview.ws.gui.WsJob.JobState;
 import jalview.ws.jws2.JabaParamStore;
 import jalview.ws.jws2.JabaPreset;
-import jalview.ws.jws2.dm.JabaWsParamSet;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.InvalidArgumentException;
 import jalview.ws.params.WsParamSetI;
@@ -21,29 +15,16 @@ import jalview.ws.params.WsParamSetI;
 import java.io.IOError;
 import java.rmi.ServerError;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
-import compbio.data.msa.MsaWS;
 import compbio.data.sequence.Alignment;
 import compbio.data.sequence.FastaSequence;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.Preset;
 import compbio.metadata.ResultNotAvailableException;
 
 public class JabawsMsaInstance
+        extends JabawsServiceInstance<compbio.data.msa.MsaWS>
         implements MultipleSequenceAlignmentI, CancellableI
 {
-  /**
-   * our service instance handler generated by the discoverer
-   */
-  Jws2Instance our;
-
-  MsaWS<?> service;
-
   @Override
   public JobId align(List<SequenceI> toalign, WsParamSetI parameters,
           List<ArgumentI> arguments) throws Throwable
@@ -118,168 +99,7 @@ public class JabawsMsaInstance
 
   public JabawsMsaInstance(Jws2Instance handle)
   {
-    our = handle;
-    service = (MsaWS<?>) our.service;
-  }
-
-  @Override
-  public boolean cancel(WsJob job)
-  {
-    service.cancelJob(job.getJobId());
-    // if the Jaba server indicates the job can't be cancelled, its
-    // because its running on the server's local execution engine
-    // so we just close the window anyway.
-
-    return true;
-  }
-
-  Map<JobStatus, JobState> jwsState = new HashMap<>();
-  {
-    jwsState.put(JobStatus.CANCELLED, JobState.CANCELLED);
-    jwsState.put(JobStatus.COLLECTED, JobState.FINISHED);
-    jwsState.put(JobStatus.FAILED, JobState.FAILED);
-    jwsState.put(JobStatus.FINISHED, JobState.FINISHED);
-    jwsState.put(JobStatus.PENDING, JobState.QUEUED);
-    jwsState.put(JobStatus.RUNNING, JobState.RUNNING);
-    jwsState.put(JobStatus.STARTED, JobState.RUNNING);
-    jwsState.put(JobStatus.SUBMITTED, JobState.SUBMITTED);
-    jwsState.put(JobStatus.UNDEFINED, JobState.UNKNOWN);
-  }
-  @Override
-  public void updateStatus(WsJob job)
-  {
-    JobStatus jwsstatus = service.getJobStatus(job.getJobId());
-    job.setState(jwsState.get(jwsstatus));
-  }
-
-  @Override
-  public boolean updateJobProgress(WsJob job) throws Exception
-  {
-    StringBuilder response = new StringBuilder(job.getStatus());
-    long lastchunk = job.getNextChunk();
-    boolean changed = false;
-    do
-    {
-      ChunkHolder chunk = service.pullExecStatistics(job.getJobId(),
-              lastchunk);
-      if (chunk != null)
-      {
-        changed |= chunk.getChunk().length() > 0;
-        response.append(chunk.getChunk());
-        lastchunk = chunk.getNextPosition();
-        try
-        {
-          Thread.sleep(50);
-        } catch (InterruptedException x)
-        {
-        }
-        ;
-      }
-      ;
-      job.setnextChunk(lastchunk);
-    } while (lastchunk >= 0 && job.getNextChunk() != lastchunk);
-    if (job instanceof WsJob)
-    {
-      // TODO decide if WsJob will be the bean for all ng-webservices
-      job.setStatus(response.toString());
-    }
-    return changed;
-  }
-
-  public boolean isPresetJob(WsJob job)
-  {
-    return job.getPreset() != null && job.getPreset() instanceof JabaPreset;
-  }
-
-  public Preset getServerPreset(WsJob job)
-  {
-    return (isPresetJob(job))
-            ? ((JabaPreset) job.getPreset()).getJabaPreset()
-            : null;
-  }
-
-  public List<Argument> getJabaArguments(WsParamSetI preset)
-  {
-    List<Argument> newargs = new ArrayList<>();
-    if (preset != null)
-    {
-      if (preset instanceof JabaWsParamSet)
-      {
-        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-      }
-      else
-      {
-        newargs.addAll(
-                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
-      }
-    }
-    return newargs;
+    super(handle);
   }
 
-  @Override
-  public boolean handleSubmitError(Throwable _lex, WsJob j,
-          WebserviceInfo wsInfo) throws Exception, Error
-  {
-    if (_lex instanceof compbio.metadata.UnsupportedRuntimeException)
-    {
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_server_doesnt_support_program",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.service_not_supported"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      wsInfo.setStatus(j.getJobnum(),
-              WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      return true;
-    }
-    if (_lex instanceof compbio.metadata.LimitExceededException)
-    {
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.input_is_too_big"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-      return true;
-    }
-    if (_lex instanceof compbio.metadata.WrongParameterException)
-    {
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.invalid_job_param_set"));
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_incorrect_param_setting",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-      return true;
-    }
-    // pass on to generic error handler
-    return false;
-  }
-
-  @Override
-  public boolean handleCollectionException(Exception ex, WsJob msjob,
-          WebserviceInfo wsInfo)
-  {
-    if (ex instanceof compbio.metadata.ResultNotAvailableException)
-    {
-      // job has failed for some reason - probably due to invalid
-      // parameters
-      Cache.log.debug(
-              "Results not available for finished job - marking as broken job.",
-              ex);
-      String status = msjob.getStatus();
-
-      msjob.setStatus(status
-              +
-              "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
-                      + ex.getLocalizedMessage());
-      msjob.setState(WsJob.JobState.BROKEN);
-      return true;
-    }
-    return false;
-  }
 }
index 5ccef8a..aff7ad6 100644 (file)
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.jws2;
+package jalview.ws.jws2.jabaws2;
 
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.jws2.AbstractJabaCalcWorker;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
 
-import java.util.Iterator;
 import java.util.List;
 
-import compbio.data.msa.MsaWS;
-import compbio.data.sequence.Alignment;
-import compbio.data.sequence.Score;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.JobSubmissionException;
-import compbio.metadata.ResultNotAvailableException;
-import compbio.metadata.WrongParameterException;
-
 public abstract class JabawsMsaInterfaceAlignCalcWorker
         extends AbstractJabaCalcWorker
 {
 
-  @SuppressWarnings("unchecked")
-  protected MsaWS msaservice;
-
-  protected Alignment msascoreset;
-
-  public JabawsMsaInterfaceAlignCalcWorker(AlignViewportI alignViewport,
-          AlignmentViewPanel alignPanel)
-  {
-    super(alignViewport, alignPanel);
-  }
-
   public JabawsMsaInterfaceAlignCalcWorker(Jws2Instance service,
           AlignFrame alignFrame, WsParamSetI preset,
           List<ArgumentI> paramset)
   {
-    this(alignFrame.getCurrentView(), alignFrame.alignPanel);
-    this.guiProgress = alignFrame;
-    this.preset = preset;
-    this.arguments = paramset;
-    this.service = service;
-    msaservice = (MsaWS) service.service;
-
-  }
-
-  @Override
-  ChunkHolder pullExecStatistics(String rslt, long rpos)
-  {
-    return msaservice.pullExecStatistics(rslt, rpos);
-  }
-
-  @Override
-  boolean collectAnnotationResultsFor(String rslt)
-          throws ResultNotAvailableException
-  {
-    msascoreset = msaservice.getResult(rslt);
-    if (msascoreset != null)
-    {
-      return true;
-    }
-    return false;
-  }
-
-  @Override
-  boolean cancelJob(String rslt) throws Exception
-  {
-    return msaservice.cancelJob(rslt);
-  }
-
-  @Override
-  protected JobStatus getJobStatus(String rslt) throws Exception
-  {
-    return msaservice.getJobStatus(rslt);
-  }
-
-  @Override
-  boolean hasService()
-  {
-    return msaservice != null;
-  }
-
-  @Override
-  protected boolean isInteractiveUpdate()
-  {
-    return false; // this instanceof AAConClient;
-  }
-
-  @Override
-  protected String submitToService(
-          List<compbio.data.sequence.FastaSequence> seqs)
-          throws JobSubmissionException
-  {
-    String rslt;
-    if (preset == null && arguments == null)
-    {
-      rslt = msaservice.align(seqs);
-    }
-    else
-    {
-      try
-      {
-        rslt = msaservice.customAlign(seqs, getJabaArguments());
-      } catch (WrongParameterException x)
-      {
-        throw new JobSubmissionException(MessageManager.getString(
-                "exception.jobsubmission_invalid_params_set"), x);
-      }
-    }
-    return rslt;
-  }
-
-  protected void createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
-          Score scr)
-  {
-    // simple annotation row
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
-                    null);
-    if (alWidth == gapMap.length) // scr.getScores().size())
-    {
-      constructAnnotationFromScore(annotation, 0, alWidth, scr);
-      ourAnnot.add(annotation);
-    }
-  }
-
-  protected AlignmentAnnotation createAnnotationRowsForScores(
-          List<AlignmentAnnotation> ourAnnot, String typeName,
-          String calcId, SequenceI dseq, int base, Score scr)
-  {
-    System.out.println("Creating annotation on dseq:" + dseq.getStart()
-            + " base is " + base + " and length=" + dseq.getLength()
-            + " == " + scr.getScores().size());
-    // AlignmentAnnotation annotation = new AlignmentAnnotation(
-    // scr.getMethod(), typeName, new Annotation[]
-    // {}, 0, -1, AlignmentAnnotation.LINE_GRAPH);
-    // annotation.setCalcId(calcId);
-    AlignmentAnnotation annotation = alignViewport.getAlignment()
-            .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
-    constructAnnotationFromScore(annotation, 0, dseq.getLength(), scr);
-    annotation.createSequenceMapping(dseq, base, false);
-    annotation.adjustForAlignment();
-    dseq.addAlignmentAnnotation(annotation);
-    ourAnnot.add(annotation);
-    return annotation;
-  }
-
-  private void constructAnnotationFromScore(AlignmentAnnotation annotation,
-          int base, int alWidth, Score scr)
-  {
-    Annotation[] elm = new Annotation[alWidth];
-    Iterator<Float> vals = scr.getScores().iterator();
-    float m = 0f, x = 0f;
-    for (int i = 0; vals.hasNext(); i++)
-    {
-      float val = vals.next().floatValue();
-      if (i == 0)
-      {
-        m = val;
-        x = val;
-      }
-      else
-      {
-        if (m > val)
-        {
-          m = val;
-        }
-        ;
-        if (x < val)
-        {
-          x = val;
-        }
-      }
-      // if we're at a gapped column then skip to next ungapped position
-      if (gapMap != null && gapMap.length > 0)
-      {
-        while (!gapMap[i])
-        {
-          elm[i++] = new Annotation("", "", ' ', Float.NaN);
-        }
-      }
-      elm[i] = new Annotation("", "" + val, ' ', val);
-    }
-
-    annotation.annotations = elm;
-    annotation.belowAlignment = true;
-    if (x < 0)
-    {
-      x = 0;
-    }
-    x += (x - m) * 0.1;
-    annotation.graphMax = x;
-    annotation.graphMin = m;
-    annotation.validateRangeAndDisplay();
+    super(service, alignFrame, preset, paramset);
+    // TODO Auto-generated constructor stub
   }
 
+  // TODO: REFACTOR if needed !
+  // may be able to get away with overriding run() only, but maybe not.
+  /***
+   * @SuppressWarnings("unchecked") protected MsaWS msaservice;
+   * 
+   * protected Alignment msascoreset;
+   * 
+   * public JabawsMsaInterfaceAlignCalcWorker(AlignViewportI alignViewport,
+   * AlignmentViewPanel alignPanel) { super(alignViewport, alignPanel); }
+   * 
+   * public JabawsMsaInterfaceAlignCalcWorker(Jws2Instance service, AlignFrame
+   * alignFrame, WsParamSetI preset, List<ArgumentI> paramset) {
+   * this(alignFrame.getCurrentView(), alignFrame.alignPanel); this.guiProgress
+   * = alignFrame; this.preset = preset; this.arguments = paramset; this.service
+   * = service; msaservice = (MsaWS) service.service;
+   * 
+   * }
+   * 
+   * @Override ChunkHolder pullExecStatistics(String rslt, long rpos) { return
+   *           msaservice.pullExecStatistics(rslt, rpos); }
+   * 
+   * @Override boolean collectAnnotationResultsFor(String rslt) throws
+   *           ResultNotAvailableException { msascoreset =
+   *           msaservice.getResult(rslt); if (msascoreset != null) { return
+   *           true; } return false; }
+   * 
+   * @Override boolean cancelJob(String rslt) throws Exception { return
+   *           msaservice.cancelJob(rslt); }
+   * 
+   * @Override protected JobStatus getJobStatus(String rslt) throws Exception {
+   *           return msaservice.getJobStatus(rslt); }
+   * 
+   * @Override boolean hasService() { return msaservice != null; }
+   * 
+   * @Override protected boolean isInteractiveUpdate() { return false; // this
+   *           instanceof AAConClient; }
+   * 
+   * @Override protected String submitToService(
+   *           List<compbio.data.sequence.FastaSequence> seqs) throws
+   *           JobSubmissionException { String rslt; if (preset == null &&
+   *           arguments == null) { rslt = msaservice.align(seqs); } else { try
+   *           { rslt = msaservice.customAlign(seqs, getJabaArguments()); }
+   *           catch (WrongParameterException x) { throw new
+   *           JobSubmissionException(MessageManager.getString(
+   *           "exception.jobsubmission_invalid_params_set"), x); } } return
+   *           rslt; }
+   * 
+   *           protected void createAnnotationRowsForScores(
+   *           List<AlignmentAnnotation> ourAnnot, String calcId, int alWidth,
+   *           Score scr) { // simple annotation row AlignmentAnnotation
+   *           annotation = alignViewport.getAlignment()
+   *           .findOrCreateAnnotation(scr.getMethod(), calcId, true, null,
+   *           null); if (alWidth == gapMap.length) // scr.getScores().size()) {
+   *           constructAnnotationFromScore(annotation, 0, alWidth, scr);
+   *           ourAnnot.add(annotation); } }
+   * 
+   *           protected AlignmentAnnotation createAnnotationRowsForScores(
+   *           List<AlignmentAnnotation> ourAnnot, String typeName, String
+   *           calcId, SequenceI dseq, int base, Score scr) {
+   *           System.out.println("Creating annotation on dseq:" +
+   *           dseq.getStart() + " base is " + base + " and length=" +
+   *           dseq.getLength() + " == " + scr.getScores().size()); //
+   *           AlignmentAnnotation annotation = new AlignmentAnnotation( //
+   *           scr.getMethod(), typeName, new Annotation[] // {}, 0, -1,
+   *           AlignmentAnnotation.LINE_GRAPH); // annotation.setCalcId(calcId);
+   *           AlignmentAnnotation annotation = alignViewport.getAlignment()
+   *           .findOrCreateAnnotation(typeName, calcId, false, dseq, null);
+   *           constructAnnotationFromScore(annotation, 0, dseq.getLength(),
+   *           scr); annotation.createSequenceMapping(dseq, base, false);
+   *           annotation.adjustForAlignment();
+   *           dseq.addAlignmentAnnotation(annotation);
+   *           ourAnnot.add(annotation); return annotation; }
+   * 
+   *           private void constructAnnotationFromScore(AlignmentAnnotation
+   *           annotation, int base, int alWidth, Score scr) { Annotation[] elm
+   *           = new Annotation[alWidth]; Iterator<Float> vals =
+   *           scr.getScores().iterator(); float m = 0f, x = 0f; for (int i = 0;
+   *           vals.hasNext(); i++) { float val = vals.next().floatValue(); if
+   *           (i == 0) { m = val; x = val; } else { if (m > val) { m = val; } ;
+   *           if (x < val) { x = val; } } // if we're at a gapped column then
+   *           skip to next ungapped position if (gapMap != null &&
+   *           gapMap.length > 0) { while (!gapMap[i]) { elm[i++] = new
+   *           Annotation("", "", ' ', Float.NaN); } } elm[i] = new
+   *           Annotation("", "" + val, ' ', val); }
+   * 
+   *           annotation.annotations = elm; annotation.belowAlignment = true;
+   *           if (x < 0) { x = 0; } x += (x - m) * 0.1; annotation.graphMax =
+   *           x; annotation.graphMin = m; annotation.validateRangeAndDisplay();
+   *           }
+   ***/
 }
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java b/src/jalview/ws/jws2/jabaws2/JabawsServiceInstance.java
new file mode 100644 (file)
index 0000000..9e8cefa
--- /dev/null
@@ -0,0 +1,212 @@
+package jalview.ws.jws2.jabaws2;
+
+import jalview.bin.Cache;
+import jalview.gui.WebserviceInfo;
+import jalview.util.MessageManager;
+import jalview.ws.gui.WsJob;
+import jalview.ws.gui.WsJob.JobState;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import compbio.metadata.Argument;
+import compbio.metadata.ChunkHolder;
+import compbio.metadata.JobStatus;
+import compbio.metadata.Preset;
+
+/**
+ * Base class for JABAWS service instances. Provides helper methods for
+ * interfacing with Jalview.
+ * 
+ * @author jprocter
+ *
+ */
+public class JabawsServiceInstance<T extends compbio.data.msa.JManagement>
+        implements
+        jalview.ws.api.JalviewWebServiceI, jalview.ws.api.CancellableI
+{
+  /**
+   * our service instance handler generated by the discoverer
+   */
+  Jws2Instance our;
+  protected T service;
+  protected Map<JobStatus, JobState> jwsState = new HashMap<>();
+
+  @Override
+  public boolean cancel(WsJob job)
+  {
+    service.cancelJob(job.getJobId());
+    // if the Jaba server indicates the job can't be cancelled, its
+    // because its running on the server's local execution engine
+    // so we just close the window anyway.
+  
+    return true;
+  }
+
+
+  public JabawsServiceInstance(Jws2Instance handle)
+  {
+    our = handle;
+    service = (T) handle.service;
+  }
+
+  @Override
+  public void updateStatus(WsJob job)
+  {
+    JobStatus jwsstatus = service.getJobStatus(job.getJobId());
+    job.setState(jwsState.get(jwsstatus));
+  }
+
+  @Override
+  public boolean updateJobProgress(WsJob job) throws Exception
+  {
+    StringBuilder response = new StringBuilder(job.getStatus());
+    long lastchunk = job.getNextChunk();
+    if (lastchunk == -1)
+    {
+      Cache.log.debug("No more status messages for job " + job.getJobId());
+      return false;
+    }
+    boolean changed = false;
+    do
+    {
+      ChunkHolder chunk = service.pullExecStatistics(job.getJobId(),
+              lastchunk);
+      if (chunk != null)
+      {
+        changed |= chunk.getChunk().length() > 0;
+        response.append(chunk.getChunk());
+        lastchunk = chunk.getNextPosition();
+        try
+        {
+          Thread.sleep(50);
+        } catch (InterruptedException x)
+        {
+        }
+        ;
+      }
+      ;
+      job.setnextChunk(lastchunk);
+    } while (lastchunk >= 0 && job.getNextChunk() != lastchunk);
+    if (job instanceof WsJob)
+    {
+      // TODO decide if WsJob will be the bean for all ng-webservices
+      job.setStatus(response.toString());
+    }
+    return changed;
+  }
+
+  {
+    jwsState.put(JobStatus.CANCELLED, JobState.CANCELLED);
+    jwsState.put(JobStatus.COLLECTED, JobState.FINISHED);
+    jwsState.put(JobStatus.FAILED, JobState.FAILED);
+    jwsState.put(JobStatus.FINISHED, JobState.FINISHED);
+    jwsState.put(JobStatus.PENDING, JobState.QUEUED);
+    jwsState.put(JobStatus.RUNNING, JobState.RUNNING);
+    jwsState.put(JobStatus.STARTED, JobState.RUNNING);
+    jwsState.put(JobStatus.SUBMITTED, JobState.SUBMITTED);
+    jwsState.put(JobStatus.UNDEFINED, JobState.UNKNOWN);
+  }
+
+  public boolean isPresetJob(WsJob job)
+  {
+    return job.getPreset() != null && job.getPreset() instanceof JabaPreset;
+  }
+
+  public Preset getServerPreset(WsJob job)
+  {
+    return (isPresetJob(job))
+            ? ((JabaPreset) job.getPreset()).getJabaPreset()
+            : null;
+  }
+
+  public List<Argument> getJabaArguments(WsParamSetI preset)
+  {
+    List<Argument> newargs = new ArrayList<>();
+    if (preset != null)
+    {
+      if (preset instanceof JabaWsParamSet)
+      {
+        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
+      }
+      else
+      {
+        newargs.addAll(
+                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
+      }
+    }
+    return newargs;
+  }
+
+  @Override
+  public boolean handleSubmitError(Throwable _lex, WsJob j,
+          WebserviceInfo wsInfo) throws Exception, Error
+  {
+    if (_lex instanceof compbio.metadata.UnsupportedRuntimeException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_server_doesnt_support_program",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.service_not_supported"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      wsInfo.setStatus(j.getJobnum(),
+              WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.LimitExceededException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.input_is_too_big"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.WrongParameterException)
+    {
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.invalid_job_param_set"));
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_incorrect_param_setting",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    // pass on to generic error handler
+    return false;
+  }
+
+  @Override
+  public boolean handleCollectionException(Exception ex, WsJob msjob,
+          WebserviceInfo wsInfo)
+  {
+    if (ex instanceof compbio.metadata.ResultNotAvailableException)
+    {
+      // job has failed for some reason - probably due to invalid
+      // parameters
+      Cache.log.debug(
+              "Results not available for finished job - marking as broken job.",
+              ex);
+      String status = msjob.getStatus();
+
+      msjob.setStatus(status
+              + "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
+              + ex.getLocalizedMessage());
+      msjob.setState(WsJob.JobState.BROKEN);
+      return true;
+    }
+    return false;
+  }
+}
index 55799f0..29f1ed1 100644 (file)
@@ -265,18 +265,44 @@ public class Jws2Instance extends ServiceWithParameters
   @Override
   public Object getEndpoint()
   {
-    if (aaui!=null) {
-      // TODO complete
-      return null;
-    } else {
-      if (service instanceof MsaWS<?>)
+    if (service instanceof MsaWS<?>)
+    {
+      if (aaui != null)
+      {
+        throw new Error(
+                "JABAWS MsaWS based instant calculation not implemented.");
+
+      }
+      else
       {
-      return new JabawsMsaInstance(this);
-    } else {
-        // TODO complete
-        // service is for sequence analysis
-        return null;
+        return new JabawsMsaInstance(this);
+      }
+    }
+    else
+    {
+      if (service instanceof compbio.data.msa.SequenceAnnotation)
+      {
+        if (aaui != null)
+        {
+          try
+          {
+            // probably a factory would be nicer but..
+            return aaui.getClient().getConstructor(getClass())
+                    .newInstance(this);
+          } catch (Throwable t)
+          {
+            throw new Error("Implementation Error in web service framework",
+                    t);
+          }
+        }
+        return new AADisorderClient(this);
+      }
+      return null;
     }
   }
-}
+
+  public boolean isInteractiveUpdate()
+  {
+    return aaui != null && aaui.isAA();
+  }
 }
index 623b8de..00127b1 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.ws.jws2.jabaws2;
 
-import jalview.ws.jws2.AAConClient;
-import jalview.ws.jws2.RNAalifoldClient;
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
 import java.util.HashMap;
@@ -46,13 +44,13 @@ public class Jws2InstanceFactory
   {
     if (aaConGUI == null)
     {
-      aaConGUI = new HashMap<String, AlignAnalysisUIText>();
+      aaConGUI = new HashMap<>();
       aaConGUI.put(compbio.ws.client.Services.AAConWS.toString(),
-              AAConClient.getAlignAnalysisUITest());
+              AAConClient.getAlignAnalysisUIText());
       aaConGUI.put(compbio.ws.client.Services.RNAalifoldWS.toString(),
-              RNAalifoldClient.getAlignAnalysisUITest());
+              RNAalifoldClient.getAlignAnalysisUIText());
       // ignore list for JABAWS services not supported in jalview ...
-      ignoreGUI = new HashSet<String>();
+      ignoreGUI = new HashSet<>();
     }
   }
 
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.jws2;
+package jalview.ws.jws2.jabaws2;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
-import jalview.gui.AlignFrame;
 import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.WsParamSetI;
+import jalview.ws.gui.AnnotationWsJob;
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
 import java.text.MessageFormat;
@@ -36,7 +34,6 @@ import java.util.List;
 import java.util.TreeSet;
 import java.util.regex.Pattern;
 
-import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.RNAStructReader.AlifoldResult;
 import compbio.data.sequence.RNAStructScoreManager;
 import compbio.data.sequence.Range;
@@ -49,29 +46,10 @@ import compbio.data.sequence.Score;
  * 
  */
 
-public class RNAalifoldClient extends JabawsCalcWorker
+public class RNAalifoldClient extends JabawsAnnotationInstance
 {
 
-  String methodName;
-
-  AlignFrame af;
-
-  // keeps track of whether the RNAalifold result includes base contact
-  // probabilities
-  boolean bpScores;
-
-  public RNAalifoldClient(Jws2Instance sh, AlignFrame alignFrame,
-          WsParamSetI preset, List<ArgumentI> paramset)
-  {
-    super(sh, alignFrame, preset, paramset);
-    af = alignFrame;
-    methodName = sh.getName();
-    alignedSeqs = true;
-    submitGaps = true;
-    nucleotidesAllowed = true;
-    proteinAllowed = false;
-    initViewportParams();
-  }
+  // configuration
 
   @Override
   public String getCalcId()
@@ -81,93 +59,89 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
   private static String CALC_ID = "jalview.ws.jws2.RNAalifoldClient";
 
-  public static AlignAnalysisUIText getAlignAnalysisUITest()
+
+  public static AlignAnalysisUIText getAlignAnalysisUIText()
   {
     return new AlignAnalysisUIText(
             compbio.ws.client.Services.RNAalifoldWS.toString(),
-            jalview.ws.jws2.RNAalifoldClient.class, CALC_ID, true, false,
-            true, MessageManager.getString("label.rnalifold_calculations"),
+            jalview.ws.jws2.jabaws2.RNAalifoldClient.class, CALC_ID, true,
+            false,
+            true, true, false, 2,
+            MessageManager.getString("label.rnalifold_calculations"),
             MessageManager.getString("tooltip.rnalifold_calculations"),
             MessageManager.getString("label.rnalifold_settings"),
             MessageManager.getString("tooltip.rnalifold_settings"));
   }
 
-  @Override
-  public String getServiceActionText()
+  public static String getServiceActionText()
   {
     return "Submitting RNA alignment for Secondary Structure prediction using "
             + "RNAalifold Service";
   }
 
-  @Override
-  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
+
+  // instance
+
+  public RNAalifoldClient(Jws2Instance handle)
   {
-    return (seqs.size() > 1);
+    super(handle);
   }
 
   @Override
-  public void updateResultAnnotation(boolean immediate)
+  List<AlignmentAnnotation> annotationFromScoreManager(
+          AnnotationWsJob running, AlignViewportI alignViewport,
+          boolean[] gapMap)
   {
-
-    if (immediate || !calcMan.isWorking(this) && scoremanager != null)
+    List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
+
+    // Unpack the ScoreManager
+    List<String> structs = ((RNAStructScoreManager) scoremanager)
+            .getStructs();
+    List<TreeSet<Score>> data = ((RNAStructScoreManager) scoremanager)
+            .getData();
+
+    // test to see if this data object contains base pair contacts
+    Score fscore = data.get(0).first();
+    boolean bpScores = (fscore.getMethod()
+            .equals(AlifoldResult.contactProbabilities.toString()));
+
+    // add annotation for the consensus sequence alignment
+    createAnnotationRowforScoreHolder(alignViewport, gapMap, ourAnnot,
+            getCalcId(), structs.get(0), null, null);
+
+    // Add annotations for the mfe Structure
+    createAnnotationRowforScoreHolder(alignViewport, gapMap, ourAnnot,
+            getCalcId(), structs.get(1), data.get(1), null);
+
+    // decide whether to add base pair contact probability histogram
+    int count = 2;
+    if (bpScores)
     {
+      createAnnotationRowforScoreHolder(alignViewport, gapMap, ourAnnot,
+              getCalcId(), structs.get(2), data.get(0), data.get(2));
+      count++;
+    }
 
-      List<AlignmentAnnotation> ourAnnot = new ArrayList<>();
-
-      // Unpack the ScoreManager
-      List<String> structs = ((RNAStructScoreManager) scoremanager)
-              .getStructs();
-      List<TreeSet<Score>> data = ((RNAStructScoreManager) scoremanager)
-              .getData();
-
-      // test to see if this data object contains base pair contacts
-      Score fscore = data.get(0).first();
-      this.bpScores = (fscore.getMethod()
-              .equals(AlifoldResult.contactProbabilities.toString()));
-
-      // add annotation for the consensus sequence alignment
-      createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-              structs.get(0), null, null);
-
-      // Add annotations for the mfe Structure
-      createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-              structs.get(1), data.get(1), null);
-
-      // decide whether to add base pair contact probability histogram
-      int count = 2;
-      if (bpScores)
-      {
-        createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-                structs.get(2), data.get(0), data.get(2));
-        count++;
-      }
-
-      // Now loop for the rest of the Annotations (if there it isn't stochastic
-      // output
-      // only the centroid and MEA structures remain anyway)
-      for (int i = count; i < structs.size(); i++)
-      {
-        // The ensemble values should be displayed in the description of the
-        // first (or all?) Stochastic Backtrack Structures.
-        if (!data.get(i).first().getMethod()
-                .equals(AlifoldResult.ensembleValues.toString()))
-        {
-
-          createAnnotationRowforScoreHolder(ourAnnot, getCalcId(),
-                  structs.get(i), data.get(i), null);
-        }
-      }
-
-      if (ourAnnot.size() > 0)
+    // Now loop for the rest of the Annotations (if there it isn't stochastic
+    // output
+    // only the centroid and MEA structures remain anyway)
+    for (int i = count; i < structs.size(); i++)
+    {
+      // The ensemble values should be displayed in the description of the
+      // first (or all?) Stochastic Backtrack Structures.
+      if (!data.get(i).first().getMethod()
+              .equals(AlifoldResult.ensembleValues.toString()))
       {
 
-        updateOurAnnots(ourAnnot);
-        ap.adjustAnnotationHeight();
+        createAnnotationRowforScoreHolder(alignViewport, gapMap, ourAnnot,
+                getCalcId(), structs.get(i), data.get(i), null);
       }
     }
+    return ourAnnot;
   }
 
-  protected void createAnnotationRowforScoreHolder(
+  private static void createAnnotationRowforScoreHolder(
+          AlignViewportI alignViewport, boolean[] gapMap,
           List<AlignmentAnnotation> ourAnnot, String calcId, String struct,
           TreeSet<Score> data, TreeSet<Score> descriptionData)
   {
@@ -197,7 +171,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
     AlignmentAnnotation annotation = alignViewport.getAlignment()
             .findOrCreateAnnotation(typename, calcId, false, null, null);
 
-    constructAnnotationFromScoreHolder(annotation, struct, data);
+    constructAnnotationFromScoreHolder(gapMap, annotation, struct, data);
 
     /*
      * update annotation description with the free Energy, frequency in ensemble
@@ -212,14 +186,11 @@ public class RNAalifoldClient extends JabawsCalcWorker
     annotation.belowAlignment = false;
     // annotation.showAllColLabels = true;
 
-    alignViewport.getAlignment().validateAnnotation(annotation);
-    af.setMenusForViewport();
-
     ourAnnot.add(annotation);
   }
 
-  private AlignmentAnnotation constructAnnotationFromScoreHolder(
-          AlignmentAnnotation annotation, String struct,
+  private static AlignmentAnnotation constructAnnotationFromScoreHolder(
+          boolean[] gapMap, AlignmentAnnotation annotation, String struct,
           TreeSet<Score> data)
   {
     Annotation[] anns = new Annotation[gapMap != null ? gapMap.length + 1
@@ -307,7 +278,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
     return annotation;
   }
 
-  private String[] constructTypenameAndDescription(Score score)
+  private static String[] constructTypenameAndDescription(Score score)
   {
     String description = "";
     String typename = "";
@@ -374,7 +345,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
 
   // Check whether, at position i there is a base contact and return all the
   // contacts at this position. Should be in order of descending probability.
-  private LinkedHashMap<Range, Float> isContact(
+  private static LinkedHashMap<Range, Float> isContact(
           LinkedHashMap<Range, Float> basePairs, int i)
   {
     LinkedHashMap<Range, Float> contacts = new LinkedHashMap<>();
@@ -393,10 +364,11 @@ public class RNAalifoldClient extends JabawsCalcWorker
     return contacts;
   }
 
-  private char isSS(char chr)
+  private static char isSS(char chr)
   {
     String regex = "\\(|\\)|\\{|\\}|\\[|\\]";
     char ss = (Pattern.matches(regex, Character.toString(chr))) ? 'S' : ' ';
     return ss;
   }
-}
+
+  }
index 9518eaa..c0bef07 100644 (file)
  */
 package jalview.ws.uimodel;
 
+/**
+ * configures annotation worker style web service clients
+ * 
+ * @author jprocter
+ *
+ */
 public class AlignAnalysisUIText
 {
 
@@ -54,6 +60,9 @@ public class AlignAnalysisUIText
     return isPr;
   }
 
+  /**
+   * @return true if service can accept sequences with gaps
+   */
   public boolean isAA()
   {
     return isAA;
@@ -63,9 +72,19 @@ public class AlignAnalysisUIText
 
   private boolean isAA;
 
+  private boolean filterSymbols;
+
+
+  private boolean needsAlignedSeqs;
+
+  private int min_valid_seqs;
+
+
   public AlignAnalysisUIText(String serviceType, Class<?> client,
           String calcId, boolean acceptNucl, boolean acceptProt,
-          boolean acceptGaps, String toggle, String toggleTooltip,
+          boolean acceptGaps, boolean alignedSeq,
+          boolean filterNonStandardSymbols, int minSeq, String toggle,
+          String toggleTooltip,
           String settings, String settingsTooltip)
   {
     this.serviceType = serviceType;
@@ -73,11 +92,35 @@ public class AlignAnalysisUIText
     isNa = acceptNucl;
     isPr = acceptProt;
     isAA = acceptGaps;
+    this.needsAlignedSeqs = alignedSeq;
+    this.filterSymbols = filterNonStandardSymbols;
     this.client = client;
     this.AAconToggle = toggle;
     this.AAconToggleTooltip = toggleTooltip;
     this.AAeditSettings = settings;
     this.AAeditSettingsTooltip = settingsTooltip;
+    this.min_valid_seqs = minSeq;
+  }
+
+  /**
+   * 
+   * @return true if non-standard nucleotides and amino acids should be replaced
+   *         or omitted
+   * 
+   */
+  public boolean isFilterSymbols()
+  {
+    return filterSymbols;
+  }
+
+  /**
+   * 
+   * @return true if service needs sequences all the same length (ie padded with
+   *         gaps if necessary)
+   */
+  public boolean isNeedsAlignedSeqs()
+  {
+    return needsAlignedSeqs;
   }
 
   public Class getClient()
@@ -130,4 +173,9 @@ public class AlignAnalysisUIText
     AAeditSettingsTooltip = aAeditSettingsTooltip;
   }
 
+  public int getMinimumSequences()
+  {
+    return min_valid_seqs;
+  }
+
 }
index 6698ed1..3e3a06d 100644 (file)
@@ -32,7 +32,7 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 import jalview.io.StockholmFileTest;
-import jalview.ws.jws2.AADisorderClient;
+import jalview.ws.jws2.AbstractJabaCalcWorker;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 
@@ -65,7 +65,7 @@ public class DisorderAnnotExportImport
 
   public static List<Jws2Instance> iupreds;
 
-  jalview.ws.jws2.AADisorderClient disorderClient;
+  jalview.ws.jws2.AbstractJabaCalcWorker disorderClient;
 
   public static jalview.gui.AlignFrame af = null;
 
@@ -114,7 +114,8 @@ public class DisorderAnnotExportImport
   @Test(groups = { "External", "Network" })
   public void testDisorderAnnotExport()
   {
-    disorderClient = new AADisorderClient(iupreds.get(0), af, null, null);
+    disorderClient = new AbstractJabaCalcWorker(iupreds.get(0), af, null,
+            null);
     af.getViewport().getCalcManager().startWorker(disorderClient);
     do
     {
index 3c83b71..4af8086 100644 (file)
@@ -33,9 +33,9 @@ import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
 import jalview.io.StockholmFileTest;
 import jalview.project.Jalview2XML;
+import jalview.ws.jws2.AbstractJabaCalcWorker;
 import jalview.ws.jws2.JabaParamStore;
 import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.jws2.RNAalifoldClient;
 import jalview.ws.jws2.SequenceAnnotationWSClient;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.AutoCalcSetting;
@@ -79,7 +79,7 @@ public class RNAStructExportImport
 
   public static Jws2Instance rnaalifoldws;
 
-  jalview.ws.jws2.RNAalifoldClient alifoldClient;
+  AbstractJabaCalcWorker alifoldClient;
 
   public static jalview.gui.AlignFrame af = null;
 
@@ -155,7 +155,8 @@ public class RNAStructExportImport
   public void testRNAAliFoldValidStructure()
   {
 
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null, null);
+    alifoldClient = new AbstractJabaCalcWorker(rnaalifoldws, af, null,
+            null);
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);
 
@@ -188,7 +189,8 @@ public class RNAStructExportImport
   public void testRNAStructExport()
   {
 
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null, null);
+    alifoldClient = new AbstractJabaCalcWorker(rnaalifoldws, af, null,
+            null);
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);
 
@@ -205,7 +207,7 @@ public class RNAStructExportImport
     AlignmentI orig_alig = af.getViewport().getAlignment();
     // JBPNote: this assert fails (2.10.2) because the 'Reference Positions'
     // annotation is mistakenly recognised as an RNA annotation row when read in
-    // as an annotation file.
+    // as an annotation file. bug is JAL-3122
     verifyAnnotationFileIO("Testing RNAalifold Annotation IO", orig_alig);
 
   }
@@ -281,7 +283,7 @@ public class RNAStructExportImport
         opts.add(rg);
       }
     }
-    alifoldClient = new RNAalifoldClient(rnaalifoldws, af, null,
+    alifoldClient = new AbstractJabaCalcWorker(rnaalifoldws, af, null,
             JabaParamStore.getJwsArgsfromJaba(opts));
 
     af.getViewport().getCalcManager().startWorker(alifoldClient);