X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2Frest%2FRestServiceDescription.java;h=9c39b37483055404c2e664def13cfe7d211d4eb7;hb=ab43013b7e357b84b4abade0dba949668dfb2a0e;hp=fe1434c99b582a48f56638b21eaecf0b940ad7d9;hpb=603ec5f3d10d05a8668477ad627ac3010473b579;p=jalview.git diff --git a/src/jalview/ws/rest/RestServiceDescription.java b/src/jalview/ws/rest/RestServiceDescription.java index fe1434c..9c39b37 100644 --- a/src/jalview/ws/rest/RestServiceDescription.java +++ b/src/jalview/ws/rest/RestServiceDescription.java @@ -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 . + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * 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 @@ -64,12 +63,15 @@ public class RestServiceDescription { 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; @@ -229,7 +231,7 @@ public class RestServiceDescription /** * input info given as key/value pairs - mapped to post arguments */ - Map inputParams = new HashMap(); + Map inputParams = new HashMap(); /** * assigns the given inputType it to its corresponding input parameter token @@ -318,30 +320,20 @@ public class RestServiceDescription 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*/ } @@ -380,12 +372,13 @@ public class RestServiceDescription return null; java.util.ArrayList jv = new ArrayList(); int cp = 0, pos, escape; - boolean wasescaped = false; + boolean wasescaped = false, wasquoted = false; String lstitem = null; while ((pos = list.indexOf(separator, cp)) >= cp) { - escape = (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, @@ -399,11 +392,20 @@ public class RestServiceDescription } 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); @@ -455,7 +457,7 @@ public class RestServiceDescription { if (list[i] != null) { - if (i > 0) + if (v.length() > 0) { v.append(separator); } @@ -487,16 +489,18 @@ public class RestServiceDescription * 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) { @@ -520,8 +524,9 @@ public class RestServiceDescription } 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 + "'")); @@ -536,12 +541,14 @@ public class RestServiceDescription _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 ""; } @@ -559,9 +566,10 @@ public class RestServiceDescription 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 = ""; @@ -589,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 vls = new ArrayList(); + 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); @@ -624,26 +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 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; } /** @@ -741,7 +802,8 @@ public class RestServiceDescription 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) { @@ -759,19 +821,25 @@ public class RestServiceDescription return valid; } - public static boolean parseTypeString(String fullstring, String tok, String iprm, String iprmparams, - Map 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 iparams, + StringBuffer warnings) + { + boolean valid = true; + InputType jinput; + for (Class type : getInputTypes()) { try { @@ -781,20 +849,21 @@ public class RestServiceDescription ArrayList al = new ArrayList(); 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; } @@ -807,87 +876,6 @@ public class RestServiceDescription return valid; } - - public static void main(String argv[]) - { - 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() @@ -966,7 +954,7 @@ public class RestServiceDescription return jobId + urlSuffix; } - private List resultData; + private List resultData = new ArrayList(); /** * @@ -998,5 +986,41 @@ 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 parseDescriptions( + String services) throws Exception + { + String[] list = separatorListToArray(services, "|"); + List svcparsed = new ArrayList(); + 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; + } + }