/*
- * 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.7)
+ * Copyright (C) 2011 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
*
* This file is part of Jalview.
*
public class RestServiceDescription
{
/**
+ * create a new rest service description ready to be configured
+ */
+ public RestServiceDescription()
+ {
+
+ }
+ /**
* @param details
* @param postUrl
* @param urlSuffix
{
super();
this.details = new UIinfo();
- details.Action = action;
- details.description = description;
- details.Name = name;
- this.postUrl = postUrl;
- this.urlSuffix = urlSuffix;
- this.inputParams = inputParams;
+ details.Action = action == null ? "" : action;
+ details.description = description == null ? "" : description;
+ details.Name = name == null ? "" : name;
+ this.postUrl = postUrl == null ? "" : postUrl;
+ this.urlSuffix = urlSuffix == null ? "" : urlSuffix;
+ if (inputParams != null)
+ {
+ this.inputParams = inputParams;
+ }
this.hseparable = hseparable;
this.vseparable = vseparable;
this.gapCharacter = gapCharacter;
/**
* input info given as key/value pairs - mapped to post arguments
*/
- Map<String, InputType> inputParams = new HashMap<String,InputType>();
+ Map<String, InputType> inputParams = new HashMap<String, InputType>();
/**
* assigns the given inputType it to its corresponding input parameter token
public RestServiceDescription(RestServiceDescription toedit)
{
- // Rather then do the above, we cheat and use our human readable serialization code to clone everything
+ // Rather then do the above, we cheat and use our human readable
+ // serialization code to clone everything
this(toedit.toString());
/**
- if (toedit == null)
- {
- return;
- }
- /**
- urlSuffix = toedit.urlSuffix;
- postUrl = toedit.postUrl;
- hseparable = toedit.hseparable;
- vseparable = toedit.vseparable;
- gapCharacter = toedit.gapCharacter;
- details = new RestServiceDescription.UIinfo();
- details.Action = toedit.details.Action;
- details.description = toedit.details.description;
- details.Name = toedit.details.Name;
- for (InputType itype: toedit.inputParams.values())
- {
- inputParams.put(itype.token, itype.clone());
-
- }
-
- */
+ * if (toedit == null) { return; } /** urlSuffix = toedit.urlSuffix; postUrl
+ * = toedit.postUrl; hseparable = toedit.hseparable; vseparable =
+ * toedit.vseparable; gapCharacter = toedit.gapCharacter; details = new
+ * RestServiceDescription.UIinfo(); details.Action = toedit.details.Action;
+ * details.description = toedit.details.description; details.Name =
+ * toedit.details.Name; for (InputType itype: toedit.inputParams.values()) {
+ * inputParams.put(itype.token, itype.clone());
+ *
+ * }
+ */
// TODO Implement copy constructor NOW*/
}
return null;
java.util.ArrayList<String> jv = new ArrayList<String>();
int cp = 0, pos, escape;
- boolean wasescaped = 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)
+
+ escape = (pos > 0 && list.charAt(pos - 1) == '\\') ? -1 : 0;
+ if (wasescaped || wasquoted)
{
// append to previous pos
jv.set(jv.size() - 1,
}
cp = pos + seplen;
wasescaped = escape == -1;
+ if (!wasescaped)
+ {
+ // last separator may be in an unmatched quote
+ if (java.util.regex.Pattern.matches("('[^']*')*[^']*'",lstitem))
+ {
+ wasquoted=true;
+ }
+ }
+
}
if (cp < list.length())
{
String c = list.substring(cp);
- if (wasescaped)
+ if (wasescaped || wasquoted)
{
// append final separator
jv.set(jv.size() - 1, lstitem + separator + c);
{
if (list[i] != null)
{
- if (v.length()>0)
+ if (v.length() > 0)
{
v.append(separator);
}
* param warnings a StringBuffer that any warnings about invalid
* content will be appended to.
*/
- private void configureFromServiceInputProperties(String propList,
+ private boolean configureFromServiceInputProperties(String propList,
StringBuffer warnings)
{
String[] props = separatorListToArray(propList, ",");
if (props == null)
{
- return;
+ return true;
}
;
+ boolean valid = true;
String val = null;
+ int l = warnings.length();
int i;
for (String prop : props)
{
}
if (prop.equals("gapCharacter"))
{
- if (val == null || val.length() > 1)
+ if (val == null || val.length() == 0 || val.length() > 1)
{
+ valid = false;
warnings.append((warnings.length() > 0 ? "\n" : "")
+ ("Invalid service property: gapCharacter=' ' (single character) - was given '"
+ val + "'"));
_configureOutputFormatFrom(val, warnings);
}
}
+ // return true if valid is true and warning buffer was not appended to.
+ return valid && (l == warnings.length());
}
private String _genOutputFormatString()
{
String buff = "";
- if (resultData==null)
+ if (resultData == null)
{
return "";
}
private void _configureOutputFormatFrom(String outstring,
StringBuffer warnings)
{
- if (outstring.indexOf(";")==-1) {
+ if (outstring.indexOf(";") == -1)
+ {
// we add a token, for simplicity
- outstring = outstring+";";
+ outstring = outstring + ";";
}
StringTokenizer st = new StringTokenizer(outstring, ";");
String tok = "";
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);
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 valid = false;
String[] list = separatorListToArray(encoding, "|");
- details.Name = list[0];
- details.Action = list[1];
- details.description = list[2];
- configureFromServiceInputProperties(list[3], warnings);
- if (list.length > 5)
+
+ int nextpos=parseServiceList(list,warnings, 0);
+ if (nextpos>0)
{
- urlSuffix = list[4];
- valid |= configureFromInputParamEncodedUrl(list[5], warnings);
+ 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)
+ {
+ 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
{
- urlSuffix = null;
- valid |= configureFromInputParamEncodedUrl(list[4], warnings);
+ if (list.length-p > 4 && list[p+4]!=null && list[p+4].trim().length()>5)
+ {
+ urlSuffix = null;
+ invalid |= !configureFromInputParamEncodedUrl(list[p+4], warnings);
+ p+=5;
+ }
}
- return valid;
+ return invalid ? -1 : p;
}
-
+
/**
* @return string representation of the input parameters, their type and
* constraints, appended to the service's base submission URL
iprmparams = iprm.substring(colon + 1);
iprm = iprm.substring(0, colon);
}
- valid = parseTypeString(prms.group(0), tok, iprm, iprmparams, iparams, warnings);
+ valid = parseTypeString(prms.group(0), tok, iprm, iprmparams,
+ iparams, warnings);
}
if (valid)
{
return valid;
}
- public static boolean parseTypeString(String fullstring, String tok, String iprm, String iprmparams,
- Map<String, InputType> iparams, StringBuffer warnings)
+ public static Class[] getInputTypes()
{
- boolean valid=true;
- InputType jinput;
// TODO - find a better way of maintaining this classlist
- for (Class type : new Class[]
+ return new Class[]
{ jalview.ws.rest.params.Alignment.class,
jalview.ws.rest.params.AnnotationFile.class,
SeqGroupIndexVector.class,
jalview.ws.rest.params.SeqIdVector.class,
jalview.ws.rest.params.SeqVector.class,
- jalview.ws.rest.params.Tree.class })
+ jalview.ws.rest.params.Tree.class };
+ }
+
+ public static boolean parseTypeString(String fullstring, String tok,
+ String iprm, String iprmparams, Map<String, InputType> iparams,
+ StringBuffer warnings)
+ {
+ boolean valid = true;
+ InputType jinput;
+ for (Class type : getInputTypes())
{
try
{
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
al.add(prprm.trim());
}
if (!jinput.configureFromURLtokenString(al, warnings))
{
valid = false;
- warnings.append("Failed to parse '" + fullstring
- + "' as a " + jinput.getURLtokenPrefix()
- + " input tag.\n");
+ warnings.append("Failed to parse '" + fullstring + "' as a "
+ + jinput.getURLtokenPrefix() + " input tag.\n");
}
else
{
jinput.token = tok;
iparams.put(tok, jinput);
- valid=true;
+ valid = true;
}
break;
}
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",
return jobId + urlSuffix;
}
- private List<JvDataType> resultData;
+ private List<JvDataType> resultData = new ArrayList<JvDataType>();
/**
*
{
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;
+ }
+
}