Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
RestServiceDescription | 41 | 252 | 113 | ||
RestServiceDescription.UIinfo | 111 | 6 | 6 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.ws.rest; | |
22 | ||
23 | import jalview.datamodel.SequenceI; | |
24 | import jalview.io.packed.DataProvider.JvDataType; | |
25 | import jalview.util.StringUtils; | |
26 | import jalview.ws.rest.params.Alignment; | |
27 | import jalview.ws.rest.params.AnnotationFile; | |
28 | import jalview.ws.rest.params.SeqGroupIndexVector; | |
29 | ||
30 | import java.net.URL; | |
31 | import java.util.ArrayList; | |
32 | import java.util.HashMap; | |
33 | import java.util.Hashtable; | |
34 | import java.util.List; | |
35 | import java.util.Map; | |
36 | import java.util.NoSuchElementException; | |
37 | import java.util.StringTokenizer; | |
38 | import java.util.regex.Matcher; | |
39 | import java.util.regex.Pattern; | |
40 | ||
41 | public class RestServiceDescription | |
42 | { | |
43 | private static final Pattern PARAM_ENCODED_URL_PATTERN = Pattern | |
44 | .compile("([?&])([A-Za-z0-9_]+)=\\$([^$]+)\\$"); | |
45 | ||
46 | /** | |
47 | * create a new rest service description ready to be configured | |
48 | */ | |
49 | 6 | public RestServiceDescription() |
50 | { | |
51 | ||
52 | } | |
53 | ||
54 | /** | |
55 | * @param details | |
56 | * @param postUrl | |
57 | * @param urlSuffix | |
58 | * @param inputParams | |
59 | * @param hseparable | |
60 | * @param vseparable | |
61 | * @param gapCharacter | |
62 | */ | |
63 | 8 | public RestServiceDescription(String action, String description, |
64 | String name, String postUrl, String urlSuffix, | |
65 | Map<String, InputType> inputParams, boolean hseparable, | |
66 | boolean vseparable, char gapCharacter) | |
67 | { | |
68 | 8 | super(); |
69 | 8 | this.details = new UIinfo(); |
70 | 8 | details.Action = action == null ? "" : action; |
71 | 8 | details.description = description == null ? "" : description; |
72 | 8 | details.Name = name == null ? "" : name; |
73 | 8 | this.postUrl = postUrl == null ? "" : postUrl; |
74 | 8 | this.urlSuffix = urlSuffix == null ? "" : urlSuffix; |
75 | 8 | if (inputParams != null) |
76 | { | |
77 | 8 | this.inputParams = inputParams; |
78 | } | |
79 | 8 | this.hseparable = hseparable; |
80 | 8 | this.vseparable = vseparable; |
81 | 8 | this.gapCharacter = gapCharacter; |
82 | } | |
83 | ||
84 | 2 | @Override |
85 | public boolean equals(Object o) | |
86 | { | |
87 | 2 | if (o == null || !(o instanceof RestServiceDescription)) |
88 | { | |
89 | 0 | return false; |
90 | } | |
91 | 2 | RestServiceDescription other = (RestServiceDescription) o; |
92 | 2 | boolean diff = (gapCharacter != other.gapCharacter); |
93 | 2 | diff |= vseparable != other.vseparable; |
94 | 2 | diff |= hseparable != other.hseparable; |
95 | 2 | diff |= !(urlSuffix == null && other.urlSuffix == null |
96 | || (urlSuffix != null && other.urlSuffix != null | |
97 | && urlSuffix.equals(other.urlSuffix))); | |
98 | // TODO - robust diff that includes constants and reordering of URL | |
99 | // diff |= !(postUrl.equals(other.postUrl)); | |
100 | // diff |= !inputParams.equals(other.inputParams); | |
101 | 2 | diff |= !details.Name.equals(other.details.Name); |
102 | 2 | diff |= !details.Action.equals(other.details.Action); |
103 | 2 | diff |= !details.description.equals(other.details.description); |
104 | 2 | return !diff; |
105 | } | |
106 | ||
107 | /** | |
108 | * Service UI Info { Action, Specific Name of Service, Brief Description } | |
109 | */ | |
110 | ||
111 | public class UIinfo | |
112 | { | |
113 | 0 | public String getAction() |
114 | { | |
115 | 0 | return Action; |
116 | } | |
117 | ||
118 | 0 | public void setAction(String action) |
119 | { | |
120 | 0 | Action = action; |
121 | } | |
122 | ||
123 | 0 | public String getName() |
124 | { | |
125 | 0 | return Name; |
126 | } | |
127 | ||
128 | 0 | public void setName(String name) |
129 | { | |
130 | 0 | Name = name; |
131 | } | |
132 | ||
133 | 0 | public String getDescription() |
134 | { | |
135 | 0 | return description; |
136 | } | |
137 | ||
138 | 0 | public void setDescription(String description) |
139 | { | |
140 | 0 | this.description = description; |
141 | } | |
142 | ||
143 | String Action; | |
144 | ||
145 | String Name; | |
146 | ||
147 | String description; | |
148 | } | |
149 | ||
150 | public UIinfo details = new UIinfo(); | |
151 | ||
152 | 0 | public String getAction() |
153 | { | |
154 | 0 | return details.getAction(); |
155 | } | |
156 | ||
157 | 0 | public void setAction(String action) |
158 | { | |
159 | 0 | details.setAction(action); |
160 | } | |
161 | ||
162 | 0 | public String getName() |
163 | { | |
164 | 0 | return details.getName(); |
165 | } | |
166 | ||
167 | 0 | public void setName(String name) |
168 | { | |
169 | 0 | details.setName(name); |
170 | } | |
171 | ||
172 | 0 | public String getDescription() |
173 | { | |
174 | 0 | return details.getDescription(); |
175 | } | |
176 | ||
177 | 0 | public void setDescription(String description) |
178 | { | |
179 | 0 | details.setDescription(description); |
180 | } | |
181 | ||
182 | /** | |
183 | * Service base URL | |
184 | */ | |
185 | String postUrl; | |
186 | ||
187 | 0 | public String getPostUrl() |
188 | { | |
189 | 0 | return postUrl; |
190 | } | |
191 | ||
192 | 0 | public void setPostUrl(String postUrl) |
193 | { | |
194 | 0 | this.postUrl = postUrl; |
195 | } | |
196 | ||
197 | 0 | public String getUrlSuffix() |
198 | { | |
199 | 0 | return urlSuffix; |
200 | } | |
201 | ||
202 | 0 | public void setUrlSuffix(String urlSuffix) |
203 | { | |
204 | 0 | this.urlSuffix = urlSuffix; |
205 | } | |
206 | ||
207 | 2 | public Map<String, InputType> getInputParams() |
208 | { | |
209 | 2 | return inputParams; |
210 | } | |
211 | ||
212 | 0 | public void setInputParams(Map<String, InputType> inputParams) |
213 | { | |
214 | 0 | this.inputParams = inputParams; |
215 | } | |
216 | ||
217 | 0 | public void setHseparable(boolean hseparable) |
218 | { | |
219 | 0 | this.hseparable = hseparable; |
220 | } | |
221 | ||
222 | 0 | public void setVseparable(boolean vseparable) |
223 | { | |
224 | 0 | this.vseparable = vseparable; |
225 | } | |
226 | ||
227 | 0 | public void setGapCharacter(char gapCharacter) |
228 | { | |
229 | 0 | this.gapCharacter = gapCharacter; |
230 | } | |
231 | ||
232 | /** | |
233 | * suffix that should be added to any url used if it does not already end in | |
234 | * the suffix. | |
235 | */ | |
236 | String urlSuffix; | |
237 | ||
238 | /** | |
239 | * input info given as key/value pairs - mapped to post arguments | |
240 | */ | |
241 | Map<String, InputType> inputParams = new HashMap<String, InputType>(); | |
242 | ||
243 | /** | |
244 | * assigns the given inputType it to its corresponding input parameter token | |
245 | * it.token | |
246 | * | |
247 | * @param it | |
248 | */ | |
249 | 0 | public void setInputParam(InputType it) |
250 | { | |
251 | 0 | inputParams.put(it.token, it); |
252 | } | |
253 | ||
254 | /** | |
255 | * remove the given input type it from the set of service input parameters. | |
256 | * | |
257 | * @param it | |
258 | */ | |
259 | 0 | public void removeInputParam(InputType it) |
260 | { | |
261 | 0 | inputParams.remove(it.token); |
262 | } | |
263 | ||
264 | /** | |
265 | * service requests alignment data | |
266 | */ | |
267 | boolean aligndata; | |
268 | ||
269 | /** | |
270 | * service requests alignment and/or seuqence annotationo data | |
271 | */ | |
272 | boolean annotdata; | |
273 | ||
274 | /** | |
275 | * service requests partitions defined over input (alignment) data | |
276 | */ | |
277 | boolean partitiondata; | |
278 | ||
279 | /** | |
280 | * process ths input data and set the appropriate shorthand flags describing | |
281 | * the input the service wants | |
282 | */ | |
283 | 1 | public void setInvolvesFlags() |
284 | { | |
285 | 1 | aligndata = inputInvolves(Alignment.class); |
286 | 1 | annotdata = inputInvolves(AnnotationFile.class); |
287 | 1 | partitiondata = inputInvolves(SeqGroupIndexVector.class); |
288 | } | |
289 | ||
290 | /** | |
291 | * Service return info { alignment, annotation file (loaded back on to | |
292 | * alignment), tree (loaded back on to alignment), sequence annotation - | |
293 | * loaded back on to alignment), text report, pdb structures with sequence | |
294 | * mapping ) | |
295 | * | |
296 | */ | |
297 | ||
298 | /** | |
299 | * Start with bare minimum: input is alignment + groups on alignment | |
300 | * | |
301 | * @author JimP | |
302 | * | |
303 | */ | |
304 | ||
305 | private String invalidMessage = null; | |
306 | ||
307 | /** | |
308 | * parse the given linkString of the form '<label>|<url>|separator | |
309 | * char[|optional sequence separator char]' into parts. url may contain a | |
310 | * string $SEQUENCEIDS<=optional regex=>$ where <=optional regex=> must be of | |
311 | * the form =/<perl style regex>/=$ or $SEQUENCES<=optional regex=>$ or | |
312 | * $SEQUENCES<=optional regex=>$. | |
313 | * | |
314 | * @param link | |
315 | */ | |
316 | 2697 | public RestServiceDescription(String link) |
317 | { | |
318 | 2697 | StringBuffer warnings = new StringBuffer(); |
319 | 2697 | if (!configureFromEncodedString(link, warnings)) |
320 | { | |
321 | 0 | if (warnings.length() > 0) |
322 | { | |
323 | 0 | invalidMessage = warnings.toString(); |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | 0 | public RestServiceDescription(RestServiceDescription toedit) |
329 | { | |
330 | // Rather then do the above, we cheat and use our human readable | |
331 | // serialization code to clone everything | |
332 | 0 | this(toedit.toString()); |
333 | /** | |
334 | * if (toedit == null) { return; } /** urlSuffix = toedit.urlSuffix; postUrl | |
335 | * = toedit.postUrl; hseparable = toedit.hseparable; vseparable = | |
336 | * toedit.vseparable; gapCharacter = toedit.gapCharacter; details = new | |
337 | * RestServiceDescription.UIinfo(); details.Action = toedit.details.Action; | |
338 | * details.description = toedit.details.description; details.Name = | |
339 | * toedit.details.Name; for (InputType itype: toedit.inputParams.values()) { | |
340 | * inputParams.put(itype.token, itype.clone()); | |
341 | * | |
342 | * } | |
343 | */ | |
344 | // TODO Implement copy constructor NOW*/ | |
345 | } | |
346 | ||
347 | /** | |
348 | * @return the invalidMessage | |
349 | */ | |
350 | 0 | public String getInvalidMessage() |
351 | { | |
352 | 0 | return invalidMessage; |
353 | } | |
354 | ||
355 | /** | |
356 | * Check if URL string was parsed properly. | |
357 | * | |
358 | * @return boolean - if false then <code>getInvalidMessage</code> returns an | |
359 | * error message | |
360 | */ | |
361 | 7 | public boolean isValid() |
362 | { | |
363 | 7 | return invalidMessage == null; |
364 | } | |
365 | ||
366 | /** | |
367 | * parse a string containing a list of service properties and configure the | |
368 | * service description | |
369 | * | |
370 | * @param propList | |
371 | * param warnings a StringBuffer that any warnings about invalid | |
372 | * content will be appended to. | |
373 | */ | |
374 | 2703 | private boolean configureFromServiceInputProperties(String propList, |
375 | StringBuffer warnings) | |
376 | { | |
377 | 2703 | String[] props = StringUtils.separatorListToArray(propList, ","); |
378 | 2703 | if (props == null) |
379 | { | |
380 | 0 | return true; |
381 | } | |
382 | 2703 | ; |
383 | 2703 | boolean valid = true; |
384 | 2703 | String val = null; |
385 | 2703 | int l = warnings.length(); |
386 | 2703 | int i; |
387 | 2703 | for (String prop : props) |
388 | { | |
389 | ? | if ((i = prop.indexOf("=")) > -1) |
390 | { | |
391 | 5406 | val = prop.substring(i + 1); |
392 | 5406 | if (val.startsWith("\'") && val.endsWith("\'")) |
393 | { | |
394 | 5406 | val = val.substring(1, val.length() - 1); |
395 | } | |
396 | 5406 | prop = prop.substring(0, i); |
397 | } | |
398 | ||
399 | 8109 | if (prop.equals("hseparable")) |
400 | { | |
401 | 2703 | hseparable = true; |
402 | } | |
403 | 8109 | if (prop.equals("vseparable")) |
404 | { | |
405 | 0 | vseparable = true; |
406 | } | |
407 | 8109 | if (prop.equals("gapCharacter")) |
408 | { | |
409 | 2703 | if (val == null || val.length() == 0 || val.length() > 1) |
410 | { | |
411 | 0 | valid = false; |
412 | 0 | warnings.append((warnings.length() > 0 ? "\n" : "") |
413 | + ("Invalid service property: gapCharacter=' ' (single character) - was given '" | |
414 | + val + "'")); | |
415 | } | |
416 | else | |
417 | { | |
418 | 2703 | gapCharacter = val.charAt(0); |
419 | } | |
420 | } | |
421 | 8109 | if (prop.equals("returns")) |
422 | { | |
423 | 2703 | _configureOutputFormatFrom(val, warnings); |
424 | } | |
425 | } | |
426 | // return true if valid is true and warning buffer was not appended to. | |
427 | 2703 | return valid && (l == warnings.length()); |
428 | } | |
429 | ||
430 | 15 | private String _genOutputFormatString() |
431 | { | |
432 | 15 | String buff = ""; |
433 | 15 | if (resultData == null) |
434 | { | |
435 | 0 | return ""; |
436 | } | |
437 | 15 | for (JvDataType type : resultData) |
438 | { | |
439 | 15 | if (buff.length() > 0) |
440 | { | |
441 | 0 | buff += ";"; |
442 | } | |
443 | 15 | buff += type.toString(); |
444 | } | |
445 | 15 | return buff; |
446 | } | |
447 | ||
448 | 2703 | private void _configureOutputFormatFrom(String outstring, |
449 | StringBuffer warnings) | |
450 | { | |
451 | 2703 | if (outstring.indexOf(";") == -1) |
452 | { | |
453 | // we add a token, for simplicity | |
454 | 2703 | outstring = outstring + ";"; |
455 | } | |
456 | 2703 | StringTokenizer st = new StringTokenizer(outstring, ";"); |
457 | 2703 | String tok = ""; |
458 | 2703 | resultData = new ArrayList<JvDataType>(); |
459 | 5406 | while (st.hasMoreTokens()) |
460 | { | |
461 | 2703 | try |
462 | { | |
463 | 2703 | resultData.add(JvDataType.valueOf(tok = st.nextToken())); |
464 | } catch (NoSuchElementException x) | |
465 | { | |
466 | 0 | warnings.append( |
467 | "Invalid result type: '" + tok + "' (must be one of: "); | |
468 | 0 | String sep = ""; |
469 | 0 | for (JvDataType vl : JvDataType.values()) |
470 | { | |
471 | 0 | warnings.append(sep); |
472 | 0 | warnings.append(vl.toString()); |
473 | 0 | sep = " ,"; |
474 | } | |
475 | 0 | warnings.append(" separated by semi-colons)\n"); |
476 | } | |
477 | } | |
478 | } | |
479 | ||
480 | 15 | private String getServiceIOProperties() |
481 | { | |
482 | 15 | ArrayList<String> vls = new ArrayList<String>(); |
483 | 15 | if (isHseparable()) |
484 | { | |
485 | 15 | vls.add("hseparable"); |
486 | } | |
487 | 15 | ; |
488 | 15 | if (isVseparable()) |
489 | { | |
490 | 0 | vls.add("vseparable"); |
491 | } | |
492 | 15 | ; |
493 | 15 | vls.add(new String("gapCharacter='" + gapCharacter + "'")); |
494 | 15 | vls.add(new String("returns='" + _genOutputFormatString() + "'")); |
495 | 15 | return StringUtils.arrayToSeparatorList(vls.toArray(new String[0]), |
496 | ","); | |
497 | } | |
498 | ||
499 | 15 | public String toString() |
500 | { | |
501 | 15 | StringBuffer result = new StringBuffer(); |
502 | 15 | result.append("|"); |
503 | 15 | result.append(details.Name); |
504 | 15 | result.append('|'); |
505 | 15 | result.append(details.Action); |
506 | 15 | result.append('|'); |
507 | 15 | if (details.description != null) |
508 | { | |
509 | 15 | result.append(details.description); |
510 | } | |
511 | 15 | ; |
512 | // list job input flags | |
513 | 15 | result.append('|'); |
514 | 15 | result.append(getServiceIOProperties()); |
515 | // list any additional cgi parameters needed for result retrieval | |
516 | 15 | if (urlSuffix != null && urlSuffix.length() > 0) |
517 | { | |
518 | 15 | result.append('|'); |
519 | 15 | result.append(urlSuffix); |
520 | } | |
521 | 15 | result.append('|'); |
522 | 15 | result.append(getInputParamEncodedUrl()); |
523 | 15 | return result.toString(); |
524 | } | |
525 | ||
526 | /** | |
527 | * processes a service encoded as a string (as generated by | |
528 | * RestServiceDescription.toString()) Note - this will only use the first | |
529 | * service definition encountered in the string to configure the service. | |
530 | * | |
531 | * @param encoding | |
532 | * @param warnings | |
533 | * - where warning messages are reported. | |
534 | * @return true if configuration was parsed successfully. | |
535 | */ | |
536 | 2697 | public boolean configureFromEncodedString(String encoding, |
537 | StringBuffer warnings) | |
538 | { | |
539 | 2697 | String[] list = StringUtils.separatorListToArray(encoding, "|"); |
540 | ||
541 | 2697 | int nextpos = parseServiceList(list, warnings, 0); |
542 | 2697 | if (nextpos > 0) |
543 | { | |
544 | 2697 | return true; |
545 | } | |
546 | 0 | return false; |
547 | } | |
548 | ||
549 | /** | |
550 | * processes the given list from position p, attempting to configure the | |
551 | * service from it. Service lists are formed by concatenating individual | |
552 | * stringified services. The first character of a stringified service is '|', | |
553 | * enabling this, and the parser will ignore empty fields in a '|' separated | |
554 | * list when they fall outside a service definition. | |
555 | * | |
556 | * @param list | |
557 | * @param warnings | |
558 | * @param p | |
559 | * @return | |
560 | */ | |
561 | 2703 | protected int parseServiceList(String[] list, StringBuffer warnings, |
562 | int p) | |
563 | { | |
564 | 2703 | boolean invalid = false; |
565 | // look for the first non-empty position - expect it to be service name | |
566 | 5406 | while (list[p] != null && list[p].trim().length() == 0) |
567 | { | |
568 | 2703 | p++; |
569 | } | |
570 | 2703 | details.Name = list[p]; |
571 | 2703 | details.Action = list[p + 1]; |
572 | 2703 | details.description = list[p + 2]; |
573 | 2703 | invalid |= !configureFromServiceInputProperties(list[p + 3], warnings); |
574 | 2703 | if (list.length - p > 5 && list[p + 5] != null |
575 | && list[p + 5].trim().length() > 5) | |
576 | { | |
577 | 2703 | urlSuffix = list[p + 4]; |
578 | 2703 | invalid |= !configureFromInputParamEncodedUrl(list[p + 5], warnings); |
579 | 2703 | p += 6; |
580 | } | |
581 | else | |
582 | { | |
583 | 0 | if (list.length - p > 4 && list[p + 4] != null |
584 | && list[p + 4].trim().length() > 5) | |
585 | { | |
586 | 0 | urlSuffix = null; |
587 | 0 | invalid |= !configureFromInputParamEncodedUrl(list[p + 4], |
588 | warnings); | |
589 | 0 | p += 5; |
590 | } | |
591 | } | |
592 | 2703 | return invalid ? -1 : p; |
593 | } | |
594 | ||
595 | /** | |
596 | * @return string representation of the input parameters, their type and | |
597 | * constraints, appended to the service's base submission URL | |
598 | */ | |
599 | 15 | private String getInputParamEncodedUrl() |
600 | { | |
601 | 15 | StringBuffer url = new StringBuffer(); |
602 | 15 | if (postUrl == null || postUrl.length() < 5) |
603 | { | |
604 | 0 | return ""; |
605 | } | |
606 | ||
607 | 15 | url.append(postUrl); |
608 | 15 | char appendChar = (postUrl.indexOf("?") > -1) ? '&' : '?'; |
609 | 15 | boolean consts = true; |
610 | 15 | do |
611 | { | |
612 | 30 | for (Map.Entry<String, InputType> param : inputParams.entrySet()) |
613 | { | |
614 | 60 | List<String> vals = param.getValue().getURLEncodedParameter(); |
615 | 60 | if (param.getValue().isConstant()) |
616 | { | |
617 | 0 | if (consts) |
618 | { | |
619 | 0 | url.append(appendChar); |
620 | 0 | appendChar = '&'; |
621 | 0 | url.append(param.getValue().token); |
622 | 0 | if (vals.size() == 1) |
623 | { | |
624 | 0 | url.append("="); |
625 | 0 | url.append(vals.get(0)); |
626 | } | |
627 | } | |
628 | } | |
629 | else | |
630 | { | |
631 | 60 | if (!consts) |
632 | { | |
633 | 30 | url.append(appendChar); |
634 | 30 | appendChar = '&'; |
635 | 30 | url.append(param.getValue().token); |
636 | 30 | url.append("="); |
637 | // write parameter set as $TOKENPREFIX:csv list of params$ for this | |
638 | // input param | |
639 | 30 | url.append("$"); |
640 | 30 | url.append(param.getValue().getURLtokenPrefix()); |
641 | 30 | url.append(":"); |
642 | 30 | url.append(StringUtils.arrayToSeparatorList( |
643 | vals.toArray(new String[0]), ",")); | |
644 | 30 | url.append("$"); |
645 | } | |
646 | } | |
647 | ||
648 | } | |
649 | // toggle consts and repeat until !consts is false: | |
650 | ? | } while (!(consts = !consts)); |
651 | 15 | return url.toString(); |
652 | } | |
653 | ||
654 | /** | |
655 | * parse the service URL and input parameters from the given encoded URL | |
656 | * string and configure the RestServiceDescription from it. | |
657 | * | |
658 | * @param ipurl | |
659 | * @param warnings | |
660 | * where any warnings | |
661 | * @return true if URL parsed correctly. false means the configuration failed. | |
662 | */ | |
663 | 2703 | private boolean configureFromInputParamEncodedUrl(String ipurl, |
664 | StringBuffer warnings) | |
665 | { | |
666 | 2703 | boolean valid = true; |
667 | 2703 | int lastp = 0; |
668 | 2703 | String url = new String(); |
669 | 2703 | Matcher prms = PARAM_ENCODED_URL_PATTERN.matcher(ipurl); |
670 | 2703 | Map<String, InputType> iparams = new Hashtable<String, InputType>(); |
671 | 2703 | InputType jinput; |
672 | 8109 | while (prms.find()) |
673 | { | |
674 | 5406 | if (lastp < prms.start(0)) |
675 | { | |
676 | 2703 | url += ipurl.substring(lastp, prms.start(0)); |
677 | 2703 | lastp = prms.end(0) + 1; |
678 | } | |
679 | 5406 | String sep = prms.group(1); |
680 | 5406 | String tok = prms.group(2); |
681 | 5406 | String iprm = prms.group(3); |
682 | 5406 | int colon = iprm.indexOf(":"); |
683 | 5406 | String iprmparams = ""; |
684 | 5406 | if (colon > -1) |
685 | { | |
686 | 5406 | iprmparams = iprm.substring(colon + 1); |
687 | 5406 | iprm = iprm.substring(0, colon); |
688 | } | |
689 | 5406 | valid = parseTypeString(prms.group(0), tok, iprm, iprmparams, iparams, |
690 | warnings); | |
691 | } | |
692 | 2703 | if (valid) |
693 | { | |
694 | 2703 | try |
695 | { | |
696 | 2703 | URL u = new URL(url); |
697 | 2703 | postUrl = url; |
698 | 2703 | inputParams = iparams; |
699 | } catch (Exception e) | |
700 | { | |
701 | 0 | warnings.append("Failed to parse '" + url + "' as a URL.\n"); |
702 | 0 | valid = false; |
703 | } | |
704 | } | |
705 | 2703 | return valid; |
706 | } | |
707 | ||
708 | 5406 | public static Class[] getInputTypes() |
709 | { | |
710 | // TODO - find a better way of maintaining this classlist | |
711 | 5406 | return new Class[] { jalview.ws.rest.params.Alignment.class, |
712 | jalview.ws.rest.params.AnnotationFile.class, | |
713 | SeqGroupIndexVector.class, jalview.ws.rest.params.SeqIdVector.class, | |
714 | jalview.ws.rest.params.SeqVector.class, | |
715 | jalview.ws.rest.params.Tree.class }; | |
716 | } | |
717 | ||
718 | 5406 | public static boolean parseTypeString(String fullstring, String tok, |
719 | String iprm, String iprmparams, Map<String, InputType> iparams, | |
720 | StringBuffer warnings) | |
721 | { | |
722 | 5406 | boolean valid = true; |
723 | 5406 | InputType jinput; |
724 | 5406 | for (Class type : getInputTypes()) |
725 | { | |
726 | 10812 | try |
727 | { | |
728 | 10812 | jinput = (InputType) (type.getConstructor().newInstance()); |
729 | 10812 | if (iprm.equalsIgnoreCase(jinput.getURLtokenPrefix())) |
730 | { | |
731 | 5406 | ArrayList<String> al = new ArrayList<String>(); |
732 | 5406 | for (String prprm : StringUtils.separatorListToArray(iprmparams, |
733 | ",")) | |
734 | { | |
735 | // hack to ensure that strings like "sep=','" containing unescaped | |
736 | // commas as values are concatenated | |
737 | 13515 | al.add(prprm.trim()); |
738 | } | |
739 | 5406 | if (!jinput.configureFromURLtokenString(al, warnings)) |
740 | { | |
741 | 0 | valid = false; |
742 | 0 | warnings.append("Failed to parse '" + fullstring + "' as a " |
743 | + jinput.getURLtokenPrefix() + " input tag.\n"); | |
744 | } | |
745 | else | |
746 | { | |
747 | 5406 | jinput.token = tok; |
748 | 5406 | iparams.put(tok, jinput); |
749 | 5406 | valid = true; |
750 | } | |
751 | 5406 | break; |
752 | } | |
753 | ||
754 | } catch (Throwable thr) | |
755 | { | |
756 | } | |
757 | 5406 | ; |
758 | } | |
759 | 5406 | return valid; |
760 | } | |
761 | ||
762 | /** | |
763 | * covenience method to generate the id and sequence string vector from a set | |
764 | * of seuqences using each sequence's getName() and getSequenceAsString() | |
765 | * method | |
766 | * | |
767 | * @param seqs | |
768 | * @return String[][] {{sequence ids},{sequence strings}} | |
769 | */ | |
770 | 0 | public static String[][] formStrings(SequenceI[] seqs) |
771 | { | |
772 | 0 | String[][] idset = new String[2][seqs.length]; |
773 | 0 | for (int i = 0; i < seqs.length; i++) |
774 | { | |
775 | 0 | idset[0][i] = seqs[i].getName(); |
776 | 0 | idset[1][i] = seqs[i].getSequenceAsString(); |
777 | } | |
778 | 0 | return idset; |
779 | } | |
780 | ||
781 | /** | |
782 | * can this service be run on the visible portion of an alignment regardless | |
783 | * of hidden boundaries ? | |
784 | */ | |
785 | boolean hseparable = false; | |
786 | ||
787 | boolean vseparable = false; | |
788 | ||
789 | 19 | public boolean isHseparable() |
790 | { | |
791 | 19 | return hseparable; |
792 | } | |
793 | ||
794 | /** | |
795 | * | |
796 | * @return | |
797 | */ | |
798 | 15 | public boolean isVseparable() |
799 | { | |
800 | 15 | return vseparable; |
801 | } | |
802 | ||
803 | /** | |
804 | * search the input types for an instance of the given class | |
805 | * | |
806 | * @param <validInput.inputType> | |
807 | * class1 | |
808 | * @return | |
809 | */ | |
810 | 3 | public boolean inputInvolves(Class<?> class1) |
811 | { | |
812 | 3 | assert (InputType.class.isAssignableFrom(class1)); |
813 | 3 | for (InputType val : inputParams.values()) |
814 | { | |
815 | 5 | if (class1.isAssignableFrom(val.getClass())) |
816 | { | |
817 | 2 | return true; |
818 | } | |
819 | } | |
820 | 1 | return false; |
821 | } | |
822 | ||
823 | char gapCharacter = '-'; | |
824 | ||
825 | /** | |
826 | * | |
827 | * @return the preferred gap character for alignments input/output by this | |
828 | * service | |
829 | */ | |
830 | 2 | public char getGapCharacter() |
831 | { | |
832 | 2 | return gapCharacter; |
833 | } | |
834 | ||
835 | 0 | public String getDecoratedResultUrl(String jobId) |
836 | { | |
837 | // TODO: correctly write ?/& appropriate to result URL format. | |
838 | 0 | return jobId + urlSuffix; |
839 | } | |
840 | ||
841 | private List<JvDataType> resultData = new ArrayList<JvDataType>(); | |
842 | ||
843 | /** | |
844 | * | |
845 | * | |
846 | * TODO: Extend to optionally specify relative/absolute url where data of this | |
847 | * type can be retrieved from | |
848 | * | |
849 | * @param dt | |
850 | */ | |
851 | 8 | public void addResultDatatype(JvDataType dt) |
852 | { | |
853 | 8 | if (resultData == null) |
854 | { | |
855 | 0 | resultData = new ArrayList<JvDataType>(); |
856 | } | |
857 | 8 | resultData.add(dt); |
858 | } | |
859 | ||
860 | 0 | public boolean removeRsultDatatype(JvDataType dt) |
861 | { | |
862 | 0 | if (resultData != null) |
863 | { | |
864 | 0 | return resultData.remove(dt); |
865 | } | |
866 | 0 | return false; |
867 | } | |
868 | ||
869 | 0 | public List<JvDataType> getResultDataTypes() |
870 | { | |
871 | 0 | return resultData; |
872 | } | |
873 | ||
874 | /** | |
875 | * parse a concatenated list of rest service descriptions into an array | |
876 | * | |
877 | * @param services | |
878 | * @return zero or more services. | |
879 | * @throws exceptions | |
880 | * if the services are improperly encoded. | |
881 | */ | |
882 | 6 | public static List<RestServiceDescription> parseDescriptions( |
883 | String services) throws Exception | |
884 | { | |
885 | 6 | String[] list = StringUtils.separatorListToArray(services, "|"); |
886 | 6 | List<RestServiceDescription> svcparsed = new ArrayList<RestServiceDescription>(); |
887 | 6 | int p = 0, lastp = 0; |
888 | 6 | StringBuffer warnings = new StringBuffer(); |
889 | 6 | do |
890 | { | |
891 | 6 | RestServiceDescription rsd = new RestServiceDescription(); |
892 | 6 | p = rsd.parseServiceList(list, warnings, lastp = p); |
893 | 6 | if (p > lastp && rsd.isValid()) |
894 | { | |
895 | 6 | svcparsed.add(rsd); |
896 | } | |
897 | else | |
898 | { | |
899 | 0 | throw new Exception( |
900 | "Failed to parse user defined RSBS services from :" | |
901 | + services | |
902 | + "\nFirst error was encountered at token " + lastp | |
903 | + " starting " + list[lastp] + ":\n" | |
904 | + rsd.getInvalidMessage()); | |
905 | } | |
906 | 6 | } while (p < lastp && p < list.length - 1); |
907 | 6 | return svcparsed; |
908 | } | |
909 | ||
910 | } |