/* * 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 . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.ws.rest; import java.util.Locale; import jalview.ws.params.ArgumentI; import jalview.ws.params.InvalidArgumentException; import jalview.ws.params.OptionI; import jalview.ws.params.ParameterI; import jalview.ws.params.simple.IntegerParameter; import jalview.ws.params.simple.Option; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.entity.mime.content.StringBody; /*** * InputType is the abstract model of each input parameter that a rest service * might take. It enables the engine to validate input by providing { formatter * for type, parser for type } * */ public abstract class InputType { private static final Pattern URL_PATTERN = Pattern .compile("^([^=]+)=?'?([^']*)?'?"); /** * not used yet */ boolean replaceids; public enum molType { NUC, PROT, MIX; public static Collection toStringValues() { Collection c = new ArrayList(); for (molType type : values()) { c.add(type.toString()); } return c; } } public String token; public int min = 1; public int max = 0; // unbounded protected ArrayList inputData = new ArrayList(); /** * initialise the InputType with a list of jalview data classes that the * RestJob needs to be able to provide to it. * * @param types */ protected InputType(Class[] types) { if (types != null) { for (Class t : types) { inputData.add(t); } } } /** * do basic tests to ensure the job's service takes this parameter, and the * job's input data can be used to generate the input data * * @param restJob * @return */ public boolean validFor(RestJob restJob) { if (!validFor(restJob.rsd)) return false; for (Class cl : inputData) { if (!restJob.hasDataOfType(cl)) { return false; } } return true; } public boolean validFor(RestServiceDescription restServiceDescription) { if (!restServiceDescription.inputParams.values().contains(this)) return false; return true; } protected ContentBody utf8StringBody(String content, String type) { Charset utf8 = Charset.forName("UTF-8"); try { if (type == null) { return new StringBody( utf8.encode(content).asCharBuffer().toString()); } else { return new StringBody( utf8.encode(content).asCharBuffer().toString(), type, utf8); } } catch (Exception ex) { jalview.bin.Console.errPrintln("Couldn't transform string\n" + content + "\nException was :"); ex.printStackTrace(System.err); } return null; } /** * * @param rj * data from which input is to be extracted and formatted * @return StringBody or FileBody ready for posting */ abstract public ContentBody formatForInput(RestJob rj) throws UnsupportedEncodingException, NoValidInputDataException; /** * * @return true if no input data needs to be provided for this parameter */ public boolean isConstant() { return (inputData == null || inputData.size() == 0); } /** * return a url encoded version of this parameter's value, or an empty string * if the parameter has no ='value' content. * * @return */ public abstract List getURLEncodedParameter(); /** * set the property known as tok, possibly by assigning it with a given val * * @param tok * @param val * (may be empty or null) * @param warnings * place where parse warnings are reported * @return true if property was set */ public abstract boolean configureProperty(String tok, String val, StringBuffer warnings); /** * Get unique key for this type of parameter in a URL encoding. * * @return the string that prefixes an input parameter of InputType type in * the string returned from getURLEncodedParameter */ public abstract String getURLtokenPrefix(); /** * parse the given token String and set InputParameter properties * appropriately * * @param tokenstring * - urlencoded parameter string as returned from * getURLEncodedParameter * @param warnings * - place where any warning messages about bad property values are * written * @return true if configuration succeeded, false otherwise. */ public boolean configureFromURLtokenString(List tokenstring, StringBuffer warnings) { boolean valid = true; for (String tok : tokenstring) { Matcher mtch = URL_PATTERN.matcher(tok); if (mtch.find()) { try { if (mtch.group(1).equals("min")) { min = Integer.parseInt(mtch.group(2)); continue; } else if (mtch.group(1).equals("max")) { max = Integer.parseInt(mtch.group(2)); continue; } } catch (NumberFormatException x) { valid = false; warnings.append("Invalid value for parameter " + mtch.group(1).toLowerCase(Locale.ROOT) + " '" + mtch.group(2) + "' (expected an integer)\n"); } if (!configureProperty(mtch.group(1), mtch.group(2), warnings)) { if (warnings.length() == 0) { warnings.append("Failed to configure InputType :" + getURLtokenPrefix() + " with property string: '" + mtch.group(0) + "'\n (token is '" + mtch.group(1) + "' and value is '" + mtch.group(2) + "')\n"); } valid = false; } } } return valid; } public void addBaseParams(ArrayList prms) { // todo : check if replaceids should be a global for the service, rather // than for a specific parameter. if (min != 1) { prms.add("min='" + min + "'"); } if (max != 0) { prms.add("max='" + max + "'"); } } public abstract List getOptions(); public List getBaseOptions() { ArrayList opts = new ArrayList(); opts.add(new IntegerParameter("min", "Minimum number of data of this type", true, 1, min, 0, -1)); opts.add(new IntegerParameter("max", "Maximum number of data of this type", false, 0, max, 0, -1)); return opts; } /** * make a copy of this InputType * * @return may not be needed public abstract InputType copy(); */ /** * parse a set of configuration options * * @param currentSettings * - modified settings originally from getOptions * @throws InvalidArgumentException * thrown if currentSettings contains invalid options for this type. */ public void configureFromArgumentI(List currentSettings) throws InvalidArgumentException { ArrayList urltoks = new ArrayList(); String rg; for (ArgumentI arg : currentSettings) { if (arg instanceof ParameterI) { rg = arg.getName() + "='" + arg.getValue() + "'"; } else { // TODO: revise architecture - this is counter intuitive - options with // different values to their names are actually parameters rg = (arg.getValue().length() > 0) ? (arg.getValue().equals(arg.getName()) ? arg.getName() : arg.getName() + "='" + arg.getValue() + "'") : arg.getName(); } if (rg.length() > 0) { urltoks.add(rg); } } StringBuffer warnings; if (!configureFromURLtokenString(urltoks, warnings = new StringBuffer())) { throw new InvalidArgumentException(warnings.toString()); } } protected OptionI createMolTypeOption(String name, String descr, boolean req, molType curType, molType defType) { return new Option(name, descr, req, defType == null ? "" : defType.toString(), curType == null ? "" : curType.toString(), molType.toStringValues(), null); } }