JAL-1620 version bump and release notes
[jalview.git] / src / jalview / ws / rest / RestServiceDescription.java
index dec4d7c..9c39b37 100644 (file)
@@ -1,31 +1,29 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
- * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
+ * Copyright (C) 2014 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.
- * 
+ * 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/>.
+ * 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.rest;
 
 import jalview.datamodel.SequenceI;
-import jalview.io.packed.DataProvider;
-import jalview.io.packed.SimpleDataProvider;
 import jalview.io.packed.DataProvider.JvDataType;
-import jalview.util.GroupUrlLink.UrlStringTooLongException;
-import jalview.util.Platform;
 import jalview.ws.rest.params.Alignment;
 import jalview.ws.rest.params.AnnotationFile;
-import jalview.ws.rest.params.JobConstant;
 import jalview.ws.rest.params.SeqGroupIndexVector;
 
 import java.net.URL;
@@ -36,19 +34,20 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.StringTokenizer;
-import java.util.Vector;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.swing.JViewport;
-
-import com.stevesoft.pat.Regex;
-import com.sun.org.apache.xml.internal.serialize.OutputFormat.DTD;
-import com.sun.tools.doclets.internal.toolkit.util.DocFinder.Output;
-
 public class RestServiceDescription
 {
   /**
+   * create a new rest service description ready to be configured
+   */
+  public RestServiceDescription()
+  {
+
+  }
+
+  /**
    * @param details
    * @param postUrl
    * @param urlSuffix
@@ -373,11 +372,11 @@ public class RestServiceDescription
       return null;
     java.util.ArrayList<String> jv = new ArrayList<String>();
     int cp = 0, pos, escape;
-    boolean wasescaped = false,wasquoted=false;
+    boolean wasescaped = false, wasquoted = false;
     String lstitem = null;
     while ((pos = list.indexOf(separator, cp)) >= cp)
     {
-      
+
       escape = (pos > 0 && list.charAt(pos - 1) == '\\') ? -1 : 0;
       if (wasescaped || wasquoted)
       {
@@ -396,12 +395,12 @@ public class RestServiceDescription
       if (!wasescaped)
       {
         // last separator may be in an unmatched quote
-        if (java.util.regex.Pattern.matches("('[^']*')*[^']*'",lstitem))
+        if (java.util.regex.Pattern.matches("('[^']*')*[^']*'", lstitem))
         {
-          wasquoted=true;
+          wasquoted = true;
         }
       }
-      
+
     }
     if (cp < list.length())
     {
@@ -501,6 +500,7 @@ public class RestServiceDescription
     ;
     boolean valid = true;
     String val = null;
+    int l = warnings.length();
     int i;
     for (String prop : props)
     {
@@ -538,12 +538,11 @@ public class RestServiceDescription
       }
       if (prop.equals("returns"))
       {
-        int l = warnings.length();
         _configureOutputFormatFrom(val, warnings);
-        valid = (l != warnings.length());
       }
     }
-    return valid;
+    // return true if valid is true and warning buffer was not appended to.
+    return valid && (l == warnings.length());
   }
 
   private String _genOutputFormatString()
@@ -598,18 +597,26 @@ public class RestServiceDescription
 
   private String getServiceIOProperties()
   {
-    String[] vls = new String[]
-    { isHseparable() ? "hseparable" : "",
-        isVseparable() ? "vseparable" : "",
-        (new String("gapCharacter='" + gapCharacter + "'")),
-        (new String("returns='" + _genOutputFormatString() + "'")) };
-
-    return arrayToSeparatorList(vls, ",");
+    ArrayList<String> vls = new ArrayList<String>();
+    if (isHseparable())
+    {
+      vls.add("hseparable");
+    }
+    ;
+    if (isVseparable())
+    {
+      vls.add("vseparable");
+    }
+    ;
+    vls.add(new String("gapCharacter='" + gapCharacter + "'"));
+    vls.add(new String("returns='" + _genOutputFormatString() + "'"));
+    return arrayToSeparatorList(vls.toArray(new String[0]), ",");
   }
 
   public String toString()
   {
     StringBuffer result = new StringBuffer();
+    result.append("|");
     result.append(details.Name);
     result.append('|');
     result.append(details.Action);
@@ -633,29 +640,71 @@ public class RestServiceDescription
     return result.toString();
   }
 
+  /**
+   * processes a service encoded as a string (as generated by
+   * RestServiceDescription.toString()) Note - this will only use the first
+   * service definition encountered in the string to configure the service.
+   * 
+   * @param encoding
+   * @param warnings
+   *          - where warning messages are reported.
+   * @return true if configuration was parsed successfully.
+   */
   public boolean configureFromEncodedString(String encoding,
           StringBuffer warnings)
   {
-    boolean invalid = false;
     String[] list = separatorListToArray(encoding, "|");
-    details.Name = list[0];
-    details.Action = list[1];
-    details.description = list[2];
-    invalid |= !configureFromServiceInputProperties(list[3], warnings);
-    if (list.length > 5)
+
+    int nextpos = parseServiceList(list, warnings, 0);
+    if (nextpos > 0)
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * processes the given list from position p, attempting to configure the
+   * service from it. Service lists are formed by concatenating individual
+   * stringified services. The first character of a stringified service is '|',
+   * enabling this, and the parser will ignore empty fields in a '|' separated
+   * list when they fall outside a service definition.
+   * 
+   * @param list
+   * @param warnings
+   * @param p
+   * @return
+   */
+  protected int parseServiceList(String[] list, StringBuffer warnings, int p)
+  {
+    boolean invalid = false;
+    // look for the first non-empty position - expect it to be service name
+    while (list[p] != null && list[p].trim().length() == 0)
     {
-      urlSuffix = list[4];
-      invalid |= !configureFromInputParamEncodedUrl(list[5], warnings);
+      p++;
+    }
+    details.Name = list[p];
+    details.Action = list[p + 1];
+    details.description = list[p + 2];
+    invalid |= !configureFromServiceInputProperties(list[p + 3], warnings);
+    if (list.length - p > 5 && list[p + 5] != null
+            && list[p + 5].trim().length() > 5)
+    {
+      urlSuffix = list[p + 4];
+      invalid |= !configureFromInputParamEncodedUrl(list[p + 5], warnings);
+      p += 6;
     }
     else
     {
-      if (list.length > 4)
+      if (list.length - p > 4 && list[p + 4] != null
+              && list[p + 4].trim().length() > 5)
       {
         urlSuffix = null;
-        invalid |= !configureFromInputParamEncodedUrl(list[4], warnings);
+        invalid |= !configureFromInputParamEncodedUrl(list[p + 4], warnings);
+        p += 5;
       }
     }
-    return !invalid;
+    return invalid ? -1 : p;
   }
 
   /**
@@ -800,7 +849,8 @@ public class RestServiceDescription
           ArrayList<String> al = new ArrayList<String>();
           for (String prprm : separatorListToArray(iprmparams, ","))
           {
-            // hack to ensure that strings like "sep=','" containing unescaped commas as values are concatenated
+            // hack to ensure that strings like "sep=','" containing unescaped
+            // commas as values are concatenated
             al.add(prprm.trim());
           }
           if (!jinput.configureFromURLtokenString(al, warnings))
@@ -826,98 +876,6 @@ public class RestServiceDescription
     return valid;
   }
 
-  public static void main(String argv[])
-  {
-    // test separator list
-    try {
-      assert(separatorListToArray("foo=',',min='foo',max='1,2,3',fa=','", ",").length==4);
-      if (separatorListToArray("minsize='2', sep=','", ",").length==2)
-      {
-        assert(false);
-      }
-      
-    } catch (AssertionError x)
-    {
-      System.err.println("separatorListToArray is faulty.");
-    }
-    if (argv.length == 0)
-    {
-      if (!testRsdExchange("Test using default Shmmr service",
-              RestClient.makeShmmrRestClient().service))
-      {
-        System.err.println("default test failed.");
-      }
-      else
-      {
-        System.err.println("default test passed.");
-      }
-    }
-    else
-    {
-      int i = 0, p = 0;
-      for (String svc : argv)
-      {
-        p += testRsdExchange("Test " + (++i), svc) ? 1 : 0;
-      }
-      System.err.println("" + p + " out of " + i + " tests passed.");
-
-    }
-  }
-
-  private static boolean testRsdExchange(String desc, String servicestring)
-  {
-    try
-    {
-      RestServiceDescription newService = new RestServiceDescription(
-              servicestring);
-      if (!newService.isValid())
-      {
-        throw new Error("Failed to create service from '" + servicestring
-                + "'.\n" + newService.getInvalidMessage());
-      }
-      return testRsdExchange(desc, newService);
-    } catch (Throwable x)
-    {
-      System.err.println("Failed for service (" + desc + "): "
-              + servicestring);
-      x.printStackTrace();
-      return false;
-    }
-  }
-
-  private static boolean testRsdExchange(String desc,
-          RestServiceDescription service)
-  {
-    try
-    {
-      String fromservicetostring = service.toString();
-      RestServiceDescription newService = new RestServiceDescription(
-              fromservicetostring);
-      if (!newService.isValid())
-      {
-        throw new Error("Failed to create service from '"
-                + fromservicetostring + "'.\n"
-                + newService.getInvalidMessage());
-      }
-
-      if (!service.equals(newService))
-      {
-        System.err.println("Failed for service (" + desc + ").");
-        System.err.println("Original service and parsed service differ.");
-        System.err.println("Original: " + fromservicetostring);
-        System.err.println("Parsed  : " + newService.toString());
-        return false;
-      }
-    } catch (Throwable x)
-    {
-      System.err.println("Failed for service (" + desc + "): "
-              + service.toString());
-      x.printStackTrace();
-      return false;
-    }
-    return true;
-  }
-
   /**
    * covenience method to generate the id and sequence string vector from a set
    * of seuqences using each sequence's getName() and getSequenceAsString()
@@ -1029,4 +987,40 @@ public class RestServiceDescription
     return resultData;
   }
 
+  /**
+   * parse a concatenated list of rest service descriptions into an array
+   * 
+   * @param services
+   * @return zero or more services.
+   * @throws exceptions
+   *           if the services are improperly encoded.
+   */
+  public static List<RestServiceDescription> parseDescriptions(
+          String services) throws Exception
+  {
+    String[] list = separatorListToArray(services, "|");
+    List<RestServiceDescription> svcparsed = new ArrayList<RestServiceDescription>();
+    int p = 0, lastp = 0;
+    StringBuffer warnings = new StringBuffer();
+    do
+    {
+      RestServiceDescription rsd = new RestServiceDescription();
+      p = rsd.parseServiceList(list, warnings, lastp = p);
+      if (p > lastp && rsd.isValid())
+      {
+        svcparsed.add(rsd);
+      }
+      else
+      {
+        throw new Exception(
+                "Failed to parse user defined RSBS services from :"
+                        + services
+                        + "\nFirst error was encountered at token " + lastp
+                        + " starting " + list[lastp] + ":\n"
+                        + rsd.getInvalidMessage());
+      }
+    } while (p < lastp && p < list.length - 1);
+    return svcparsed;
+  }
+
 }