change ICBN to ICNAFP #2864
[cdmlib.git] / cdmlib-ext / src / main / java / eu / etaxonomy / cdm / ext / ipni / IpniService.java
1 // $Id$
2 /**
3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.cdm.ext.ipni;
11
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.InputStreamReader;
16 import java.net.HttpURLConnection;
17 import java.net.MalformedURLException;
18 import java.net.URI;
19 import java.net.URISyntaxException;
20 import java.net.URL;
21 import java.text.ParseException;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.http.HttpResponse;
29 import org.apache.log4j.Logger;
30 import org.springframework.stereotype.Component;
31
32 import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
33 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
34 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
35 import eu.etaxonomy.cdm.common.CdmUtils;
36 import eu.etaxonomy.cdm.common.UriUtils;
37 import eu.etaxonomy.cdm.model.agent.Person;
38 import eu.etaxonomy.cdm.model.agent.Team;
39 import eu.etaxonomy.cdm.model.common.Annotation;
40 import eu.etaxonomy.cdm.model.common.AnnotationType;
41 import eu.etaxonomy.cdm.model.common.Extension;
42 import eu.etaxonomy.cdm.model.common.ExtensionType;
43 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
44 import eu.etaxonomy.cdm.model.common.Language;
45 import eu.etaxonomy.cdm.model.common.TimePeriod;
46 import eu.etaxonomy.cdm.model.name.BotanicalName;
47 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
48 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
49 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
50 import eu.etaxonomy.cdm.model.name.Rank;
51 import eu.etaxonomy.cdm.model.reference.Reference;
52 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
53 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
54
55
56 /**
57 * @author a.mueller
58 * @created Aug 16, 2010
59 * @version 1.0
60 *
61 * TODO the whole ipni service should be refactored to use UriUtils. I did this for the queryService method only because we ran into timeout
62 * problems in tests. UriUtils handles these problems already.
63 *
64 */
65 @Component
66 public class IpniService implements IIpniService{
67 private static final String EAST_OR_WEST = "East or west";
68
69 private static final String NORTH_OR_SOUTH = "North or south";
70
71 private static final String LATITUDE_SECONDS = "Latitude seconds";
72
73 private static final String LATITUDE_MINUTES = "Latitude minutes";
74
75 private static final String LATITUDE_DEGREES = "Latitude degrees";
76
77 private static final String COLLECTION_DATE_AS_TEXT = "Collection date as text";
78
79 private static final String COLLECTION_DAY1 = "Collection day1";
80
81 private static final String COLLECTION_MONTH1 = "Collection month1";
82
83 private static final String COLLECTION_YEAR1 = "Collection year1";
84
85 private static final String COLLECTION_DAY2 = "Collection day2";
86
87 private static final String COLLECTION_MONTH2 = "Collection month2";
88
89 private static final String COLLECTION_YEAR2 = "Collection year2";
90
91 private static final String COLLECTION_NUMBER = "Collection number";
92
93 private static final String COLLECTOR_TEAM_AS_TEXT = "Collector team as text";
94
95 private static final String LOCALITY = "Locality";
96
97 private static final String TYPE_REMARKS = "Type remarks";
98
99 private static final Logger logger = Logger.getLogger(IpniService.class);
100
101 // GENERAL
102 public static String ID = "Id";
103 public static String VERSION = "Version";
104 public static final String REMARKS = "Remarks";
105
106 //NAMES
107 public static final String FULL_NAME_WITHOUT_FAMILY_AND_AUTHORS = "Full name without family and authors";
108 public static final String AUTHORS = "Authors";
109 public static final String FAMILY = "Family";
110 public static final String GENUS = "Genus";
111 public static final String INFRA_GENUS = "Infra genus";
112 public static final String SPECIES = "Species";
113 public static final String INFRA_SPECIFIC = "Infra species";
114 public static final String RANK = "Rank";
115 public static final String BASIONYM_AUTHOR = "Basionym author";
116 public static final String PUBLISHING_AUTHOR = "Publishing author";
117 public static final String PUBLICATION = "Publication";
118 public static final String PUBLICATION_YEAR_FULL = "Publication year full";
119 public static final String NAME_STATUS = "Name status";
120 public static final String BASIONYM = "Basionym";
121 public static final String REPLACED_SYNONYM = "Replaced synonym";
122
123
124 //AUTHORS
125
126 public static final String STANDARD_FORM = "Standard Form";
127
128 public static final String DEFAULT_AUTHOR_FORENAME = "Default author forename";
129 public static final String DEFAULT_AUTHOR_SURNAME = "Default author surname";
130 public static final String TAXON_GROUPS = "Taxon groups";
131 public static final String DATES = "Dates";
132 public static final String ALTERNATIVE_NAMES = "Alternative names";
133
134 public static final String DEFAULT_AUTHOR_NAME = "Default author name";
135
136 public static final String NAME_NOTES = "Name notes";
137 public static final String NAME_SOURCE = "Name source";
138 public static final String DATE_TYPE_CODE = "Date type code";
139 public static final String DATE_TYPE_STRING = "Date type string";
140
141 public static final String ALTERNATIVE_ABBREVIATIONS = "Alternative abbreviations";
142 public static final String EXAMPLE_OF_NAME_PUBLISHED = "Example of name published";
143
144
145 //PUBLICATIONS
146
147 public static final String ABBREVIATION = "Abbreviation";
148 public static final String TITLE = "Title";
149 public static final String BPH_NUMBER = "BPH number";
150 public static final String ISBN = "ISBN";
151 public static final String ISSN = "ISSN";
152 public static final String AUTHORS_ROLE = "Authors role";
153 public static final String EDITION = "Edition";
154 public static final String DATE = "Date";
155 public static final String IN_PUBLICATION_FACADE = "In publication facade";
156 public static final String LC_NUMBER = "LC number";
157 public static final String PLACE = "Place";
158 public static final String PUBLICATION_AUTHOR_TEAM = "Publication author team";
159 public static final String PRECEDED_BY = "Preceded";
160 public static final String TL2_AUTHOR = "TL2 author";
161 public static final String TL2_NUMBER = "TL2 number";
162 public static final String TDWG_ABBREVIATION = "TDWG abbreviation";
163
164 private enum ServiceType{
165 AUTHOR,
166 NAME,
167 PUBLICATION,
168 }
169
170 public enum IpniRank{
171 ALL ("All"),
172 FAMILIAL ("Familial"),
173 INFRA_FAMILIAL ("Infrafamilial"),
174 GENERIC("Generic"),
175 INFRA_GENERIC("Infrageneric"),
176 SPECIFIC ("Specific"),
177 INFRA_SPECIFIC("InfraSpecific");
178
179 String strRank;
180 IpniRank(String strRank){
181 this.strRank = strRank;
182 }
183
184 public static IpniRank valueOf(Rank rank){
185 if (rank == null){
186 return ALL;
187 }else if (rank.isInfraSpecific()){
188 return INFRA_SPECIFIC;
189 }else if (rank.isSpecies()){
190 return SPECIFIC;
191 }else if (rank.isInfraGeneric()){
192 return INFRA_GENERIC;
193 }else if (rank.isGenus()){
194 return GENERIC;
195 }else if (rank.isLower(Rank.FAMILY())){
196 return INFRA_FAMILIAL;
197 }else if (rank.isHigher(Rank.SUBFAMILY())){
198 return FAMILIAL;
199 }else{
200 logger.warn("Rank could not be transformed to ipni rank. Use ALL instead");
201 return ALL;
202 }
203 }
204 }
205
206
207 // private URL serviceUrl;
208
209 // ******************************** CONSTRUCTOR **************************************
210
211 // ****************************** METHODS ****************************************************/
212
213
214 /* (non-Javadoc)
215 * @see eu.etaxonomy.cdm.ext.ipni.IIpniService#getAuthors(java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration, eu.etaxonomy.cdm.ext.ipni.IpniServiceAuthorConfigurator)
216 */
217 public List<Person> getAuthors(String abbreviation, String surname, String forename, String isoCountry, ICdmApplicationConfiguration services, IpniServiceAuthorConfigurator config){
218 //config
219 if (config == null){
220 config = new IpniServiceAuthorConfigurator();
221 }
222
223
224 abbreviation = normalizeParameter(abbreviation);
225 surname = normalizeParameter(surname);
226 isoCountry = normalizeParameter(isoCountry);
227 forename = normalizeParameter(forename);
228
229 DelimitedFormat format = config.getFormat();
230
231 String request = "find_abbreviation=" + abbreviation +
232 "&find_surname=" + surname +
233 "&find_isoCountry=" + isoCountry +
234 "&find_forename=" + forename +
235 "&output_format=" + format.parameter;
236
237 return (List)queryService(request, services, getServiceUrl(IIpniService.AUTHOR_SERVICE_URL), config, ServiceType.AUTHOR);
238 }
239
240
241 /**
242 * FIXME rewrote this method to rely on {@link UriUtils}. The whole class should be adjusted to reflect this change.
243 * Also see comments in the class' documentation block.
244 *
245 * @param restRequest
246 * @return
247 */
248 private List<? extends IdentifiableEntity> queryService(String request, ICdmApplicationConfiguration services, URL serviceUrl, IIpniServiceConfigurator config, ServiceType serviceType){
249 if (config == null){
250 throw new NullPointerException("Ipni service configurator should not be null");
251 }
252 try {
253
254 // create the request url
255 URL newUrl = new URL(serviceUrl.getProtocol(),
256 serviceUrl.getHost(),
257 serviceUrl.getPort(),
258 serviceUrl.getPath()
259 + "?" + request);
260
261
262 URI newUri = newUrl.toURI();
263
264 logger.info("Firing request for URI: " + newUri);
265
266 HttpResponse response = UriUtils.getResponse(newUri, null);
267
268 int responseCode = response.getStatusLine().getStatusCode();
269
270 // get the content at the resource
271 InputStream content = response.getEntity().getContent();
272 // build the result
273 List<? extends IdentifiableEntity> result;
274 if (serviceType.equals(ServiceType.AUTHOR)){
275 result = buildAuthorList(content, services, config);
276 }else if (serviceType.equals(ServiceType.NAME)){
277 result = buildNameList(content, services, config);
278 }else{
279 result = buildPublicationList(content, services, config);
280 }
281 if(responseCode == HttpURLConnection.HTTP_OK){
282 return result;
283 }else{
284 //TODO error handling
285 logger.error("No Http_OK");
286 }
287
288 } catch (IOException e) {
289 logger.error("No content for request: " + request);
290 } catch (URISyntaxException e) {
291 logger.error("Given URL could not be transformed into URI", e);
292 }
293
294 // error
295 return null;
296 }
297
298 private List<Reference> buildPublicationList( InputStream content, ICdmApplicationConfiguration services, IIpniServiceConfigurator iConfig) throws IOException {
299 IpniServicePublicationConfigurator config = (IpniServicePublicationConfigurator)iConfig;
300
301 List<Reference> result = new ArrayList<Reference>();
302 BufferedReader reader = new BufferedReader (new InputStreamReader(content));
303
304 String headerLine = reader.readLine();
305 Map<Integer, String> parameterMap = getParameterMap(headerLine);
306
307 String line = reader.readLine();
308 while (StringUtils.isNotBlank(line)){
309 Reference reference = getPublicationFromLine(line, parameterMap, services, config);
310 result.add(reference);
311 line = reader.readLine();
312 }
313
314 return result;
315 }
316
317
318 /**
319 * @param line
320 * @param parameterMap
321 * @param appConfig
322 * @param config
323 * @return
324 */
325 private Reference getPublicationFromLine(String line, Map<Integer, String> parameterMap, ICdmApplicationConfiguration appConfig, IpniServicePublicationConfigurator config) {
326 //fill value map
327 String[] splits = line.split("%");
328
329 Map<String, String> valueMap = new HashMap<String, String>();
330 for (int i = 0; i < splits.length; i++){
331 valueMap.put(parameterMap.get(i), splits[i]);
332 }
333
334 //create reference object
335 Reference ref = ReferenceFactory.newGeneric();
336
337 //reference
338 if (config.isUseAbbreviationAsTitle() == true){
339 ref.setTitle(valueMap.get(ABBREVIATION));
340 //TODO handle title as extension
341 }else{
342 ref.setTitle(valueMap.get(TITLE));
343 //TODO handle abbreviation as extension
344 }
345 ref.setIsbn(valueMap.get(ISBN));
346 ref.setIssn(valueMap.get(ISSN));
347 ref.setEdition(valueMap.get(EDITION));
348 ref.setPlacePublished(valueMap.get(PLACE));
349
350 String author = valueMap.get(PUBLICATION_AUTHOR_TEAM);
351 if (StringUtils.isNotBlank(author)){
352 Team team = Team.NewTitledInstance(author, author);
353 ref.setAuthorTeam(team);
354 }
355
356 //remarks
357 String remarks = valueMap.get(REMARKS);
358 Annotation annotation = Annotation.NewInstance(remarks, AnnotationType.EDITORIAL(), Language.ENGLISH());
359 ref.addAnnotation(annotation);
360
361
362 String tl2AuthorString = valueMap.get(TL2_AUTHOR);
363 if (ref.getAuthorTeam() == null){
364 Team tl2Author = Team.NewTitledInstance(tl2AuthorString, null);
365 ref.setAuthorTeam(tl2Author);
366 }else{
367 //TODO parse name,
368 ref.getAuthorTeam().setTitleCache(tl2AuthorString, true);
369 ref.addAnnotation(Annotation.NewInstance(tl2AuthorString, AnnotationType.EDITORIAL(), Language.ENGLISH()));
370 }
371
372
373 //dates
374 TimePeriod date = TimePeriod.parseString(valueMap.get(DATE));
375 ref.setDatePublished(date);
376
377
378 //source
379 Reference citation = getIpniCitation(appConfig);
380 ref.addSource(valueMap.get(ID), "Publication", citation, valueMap.get(VERSION));
381
382
383
384 /* TODO
385 BPH number
386 Authors role
387 In publication facade
388 LC number
389 Preceded by
390 TL2 number
391 TDWG abbreviation
392 */
393
394 return ref;
395 }
396
397
398 private List<BotanicalName> buildNameList( InputStream content, ICdmApplicationConfiguration appConfig, IIpniServiceConfigurator iConfig) throws IOException {
399 IpniServiceNamesConfigurator config = (IpniServiceNamesConfigurator)iConfig;
400 List<BotanicalName> result = new ArrayList<BotanicalName>();
401 BufferedReader reader = new BufferedReader (new InputStreamReader(content));
402
403 String headerLine = reader.readLine();
404 Map<Integer, String> parameterMap = getParameterMap(headerLine);
405
406 String line = reader.readLine();
407 while (StringUtils.isNotBlank(line)){
408 BotanicalName name = getNameFromLine(line,parameterMap, appConfig);
409 result.add(name);
410 line = reader.readLine();
411 }
412
413
414 return result;
415 }
416
417
418 private BotanicalName getNameFromLine(String line, Map<Integer, String> parameterMap, ICdmApplicationConfiguration appConfig) {
419 //Id%Version%Standard form%Default author forename%Default author surname%Taxon groups%Dates%Alternative names
420 String[] splits = line.split("%");
421 Map<String, String> valueMap = new HashMap<String, String>();
422
423 for (int i = 0; i < splits.length; i++){
424 valueMap.put(parameterMap.get(i), splits[i]);
425 }
426
427 BotanicalName name = BotanicalName.NewInstance(null);
428
429 //caches
430 name.setNameCache(valueMap.get(FULL_NAME_WITHOUT_FAMILY_AND_AUTHORS), true);
431 name.setAuthorshipCache(valueMap.get(AUTHORS), true);
432
433 //epithets
434 name.setGenusOrUninomial(valueMap.get(GENUS));
435 name.setInfraGenericEpithet(valueMap.get(INFRA_GENUS));
436 name.setSpecificEpithet(valueMap.get(SPECIES));
437 name.setInfraSpecificEpithet(valueMap.get(INFRA_SPECIFIC));
438
439 //rank
440 try {
441 String rankStr = nomalizeRank(valueMap.get(RANK));
442 name.setRank(Rank.getRankByNameOrAbbreviation(rankStr, NomenclaturalCode.ICNAFP, true));
443 } catch (UnknownCdmTypeException e) {
444 logger.warn("Rank was unknown");
445 }
446
447 //authors
448 name.setBasionymAuthorTeam(Team.NewTitledInstance(valueMap.get(BASIONYM_AUTHOR), valueMap.get(BASIONYM_AUTHOR)));
449 name.setCombinationAuthorTeam(Team.NewTitledInstance(valueMap.get(PUBLISHING_AUTHOR), valueMap.get(PUBLISHING_AUTHOR)));
450
451 //publication
452 Reference ref = ReferenceFactory.newGeneric();
453 ref.setTitleCache(valueMap.get(PUBLICATION));
454 TimePeriod datePublished = TimePeriod.parseString(valueMap.get(PUBLICATION_YEAR_FULL));
455 name.setNomenclaturalReference(ref);
456
457 //name status
458 NomenclaturalStatusType statusType = null;
459 String statusString = valueMap.get(NAME_STATUS);
460 if (StringUtils.isNotBlank(statusString)){
461 try {
462 statusType = NomenclaturalStatusType.getNomenclaturalStatusTypeByAbbreviation(statusString);
463 NomenclaturalStatus nomStatus = NomenclaturalStatus.NewInstance(statusType);
464 name.addStatus(nomStatus);
465 } catch (UnknownCdmTypeException e) {
466 logger.warn("Name status not recognized: " + statusString);
467 }
468 }
469
470 //remarks
471 String remarks = valueMap.get(REMARKS);
472 Annotation annotation = Annotation.NewInstance(remarks, AnnotationType.EDITORIAL(), Language.ENGLISH());
473 name.addAnnotation(annotation);
474
475 //basionym
476 BotanicalName basionym = BotanicalName.NewInstance(null);
477 basionym.setTitleCache(valueMap.get(BASIONYM), true);
478 name.addBasionym(basionym);
479
480 //replaced synonym
481 BotanicalName replacedSynoynm = BotanicalName.NewInstance(null);
482 replacedSynoynm.setTitleCache(valueMap.get(REPLACED_SYNONYM), true);
483 name.addReplacedSynonym(replacedSynoynm, null, null, null);
484
485 //type information
486 DerivedUnitFacade specimen = DerivedUnitFacade.NewInstance(DerivedUnitType.Specimen);
487
488
489 //gathering period
490 String collectionDateAsText = valueMap.get(COLLECTION_DATE_AS_TEXT);
491 TimePeriod gatheringPeriod = TimePeriod.parseString(collectionDateAsText);
492
493 try {
494 gatheringPeriod.setStartDay(getIntegerDateValueOrNull(valueMap, COLLECTION_DAY1));
495 gatheringPeriod.setStartMonth(getIntegerDateValueOrNull(valueMap, COLLECTION_MONTH1));
496 gatheringPeriod.setStartYear(getIntegerDateValueOrNull(valueMap, COLLECTION_YEAR1));
497 gatheringPeriod.setEndDay(getIntegerDateValueOrNull(valueMap, COLLECTION_DAY2));
498 gatheringPeriod.setEndMonth(getIntegerDateValueOrNull(valueMap, COLLECTION_MONTH2));
499 gatheringPeriod.setEndYear(getIntegerDateValueOrNull(valueMap, COLLECTION_YEAR2));
500 } catch (IndexOutOfBoundsException e) {
501 logger.info("Exception occurred when trying to fill gathering period");
502 }
503 specimen.setGatheringPeriod(gatheringPeriod);
504
505 specimen.setFieldNumber(valueMap.get(COLLECTION_NUMBER));
506
507 //collector team
508 String team = valueMap.get(COLLECTOR_TEAM_AS_TEXT);
509 Team collectorTeam = Team.NewTitledInstance(team, team);
510 specimen.setCollector(collectorTeam);
511
512 specimen.setLocality(valueMap.get(LOCALITY));
513
514 try {
515 String latDegrees = CdmUtils.Nz(valueMap.get(LATITUDE_DEGREES));
516 String latMinutes = CdmUtils.Nz(valueMap.get(LATITUDE_MINUTES));
517 String latSeconds = CdmUtils.Nz(valueMap.get(LATITUDE_SECONDS));
518 String direction = CdmUtils.Nz(valueMap.get(NORTH_OR_SOUTH));
519 String latitude = latDegrees + "°" + latMinutes + "'" + latSeconds + "\"" + direction;
520
521 String lonDegrees = CdmUtils.Nz(valueMap.get(LATITUDE_DEGREES));
522 String lonMinutes = CdmUtils.Nz(valueMap.get(LATITUDE_MINUTES));
523 String lonSeconds = CdmUtils.Nz(valueMap.get(LATITUDE_SECONDS));
524 direction = CdmUtils.Nz(valueMap.get(EAST_OR_WEST));
525 String longitude = lonDegrees + "°" + lonMinutes + "'" + lonSeconds + "\"" + direction;
526
527
528 specimen.setExactLocationByParsing(longitude, latitude, null, null);
529 } catch (ParseException e) {
530 logger.info("Parsing exception occurred when trying to parse type exact location." + e.getMessage());
531 } catch (Exception e) {
532 logger.info("Exception occurred when trying to read type exact location." + e.getMessage());
533 }
534
535
536 //type annotation
537 Annotation typeAnnotation = Annotation.NewInstance(TYPE_REMARKS, AnnotationType.EDITORIAL(), Language.DEFAULT());
538 specimen.addAnnotation(typeAnnotation);
539
540
541 //TODO Type name
542 //TODO "Type locations" , eg. holotype CAT ,isotype CAT ,isotype FI
543
544 //TODO Geographic unit as text
545
546
547
548
549
550 //source
551 Reference citation = getIpniCitation(appConfig);
552 name.addSource(valueMap.get(ID), "Name", citation, valueMap.get(VERSION));
553
554
555 // //TODO
556 //SHORT Family, Infra family, Hybrid genus, Hybrid, Collation, Nomenclatural synonym, Distribution, Citation type
557 /* EXTENDED
558 * Species author,
559 * Standardised basionym author flag,
560 * Standardised publishing author flag
561 Full name
562 Full name without family
563 Full name without authors
564
565 Reference
566 Standardised publication flag
567 Publication year
568 publication year note
569 Publication year text
570 Volume
571 Start page
572 End page
573 Primary pagination
574 Secondary pagination
575 Reference remarks
576 Hybrid parents
577 Replaced synonym Author team
578 Other links
579 Same citation as
580 Bibliographic reference
581 Bibliographic type info
582
583 Original taxon name
584 Original taxon name author team
585 Original replaced synonym
586 Original replaced synonym author team
587 Original basionym
588 Original basionym author team
589 Original parent citation taxon name author team
590 Original taxon distribution
591 Original hybrid parentage
592 Original cited type
593 Original remarks
594
595 */
596 return name;
597 }
598
599 /**
600 * @param valueMap
601 * @return
602 */
603 private Integer getIntegerDateValueOrNull(Map<String, String> valueMap, String key) {
604 try {
605 Integer result = Integer.valueOf(valueMap.get(key));
606 if (result == 0){
607 result = null;
608 }
609 return result;
610 } catch (NumberFormatException e) {
611 if (logger.isDebugEnabled()){
612 logger.debug("Number Format exception for " + valueMap.get(key));
613 }
614 return null;
615 }
616 }
617
618
619 private String nomalizeRank(String string) {
620 String result = string.replace("spec.", "sp.");
621 return result;
622 }
623
624
625 private List<Person> buildAuthorList(InputStream content, ICdmApplicationConfiguration services, IIpniServiceConfigurator iConfig) throws IOException {
626 IpniServiceAuthorConfigurator config = (IpniServiceAuthorConfigurator)iConfig;
627 List<Person> result = new ArrayList<Person>();
628 BufferedReader reader = new BufferedReader (new InputStreamReader(content));
629
630 String headerLine = reader.readLine();
631 if (headerLine != null){
632 Map<Integer, String> parameterMap = getParameterMap(headerLine);
633
634 String line = reader.readLine();
635 while (StringUtils.isNotBlank(line)){
636 Person author = getAuthorFromLine(line,parameterMap, services, config);
637 result.add(author);
638 line = reader.readLine();
639 }
640 }
641
642 return result;
643 }
644
645
646
647 private Map<Integer, String> getParameterMap(String headerLine) {
648 Map<Integer, String> result = new HashMap<Integer, String>();
649 if ( headerLine != null ){
650 String[] splits = headerLine.split("%");
651 for (int i = 0; i < splits.length ; i ++){
652 result.put(i, splits[i]);
653 }
654 }
655 return result;
656 }
657
658
659 private Person getAuthorFromLine(String line, Map<Integer, String> categoryMap, ICdmApplicationConfiguration appConfig, IpniServiceAuthorConfigurator config) {
660 //Id%Version%Standard form%Default author forename%Default author surname%Taxon groups%Dates%Alternative names
661 String[] splits = line.split("%");
662 Map<String, String> valueMap = new HashMap<String, String>();
663
664 for (int i = 0; i < splits.length; i++){
665 valueMap.put(categoryMap.get(i), splits[i]);
666 }
667
668 Person person = Person.NewInstance();
669
670 person.setNomenclaturalTitle(valueMap.get(STANDARD_FORM));
671 person.setFirstname(valueMap.get(DEFAULT_AUTHOR_FORENAME));
672 person.setLastname(valueMap.get(DEFAULT_AUTHOR_SURNAME));
673
674 Reference citation = getIpniCitation(appConfig);
675
676 //id, version
677 person.addSource(valueMap.get(ID), "Author", citation, valueMap.get(VERSION));
678
679 //dates
680 TimePeriod lifespan = TimePeriod.parseString(valueMap.get(DATES));
681 person.setLifespan(lifespan);
682
683 //alternative_names
684 String alternativeNames = valueMap.get(ALTERNATIVE_NAMES);
685 if (StringUtils.isNotBlank(alternativeNames)){
686 String[] alternativeNameSplits = alternativeNames.split("%");
687 for (String alternativeName : alternativeNameSplits){
688 if (alternativeName.startsWith(">")){
689 alternativeName = alternativeName.substring(1);
690 }
691 Extension.NewInstance(person, alternativeName, ExtensionType.INFORMAL_CATEGORY());
692 }
693 }
694
695 //TODO taxonGroups
696
697 return person;
698 }
699
700
701 private Reference getIpniCitation(ICdmApplicationConfiguration appConfig) {
702 Reference ipniReference;
703 if (appConfig != null){
704 ipniReference = appConfig.getReferenceService().find(uuidIpni);
705 if (ipniReference == null){
706 ipniReference = getNewIpniReference();
707 ipniReference.setUuid(uuidIpni);
708 appConfig.getReferenceService().save(ipniReference);
709 }
710 }else{
711 ipniReference = getNewIpniReference();
712 }
713 return ipniReference;
714 }
715
716 /**
717 * @return
718 */
719 private Reference getNewIpniReference() {
720 Reference ipniReference;
721 ipniReference = ReferenceFactory.newDatabase();
722 ipniReference.setTitleCache("The International Plant Names Index (IPNI)");
723 return ipniReference;
724 }
725
726
727 /**
728 * @param parameter
729 */
730 private String normalizeParameter(String parameter) {
731 String result = CdmUtils.Nz(parameter).replace(" ", "+");
732 return result;
733 }
734
735 public List<BotanicalName> getNamesAdvanced(String family, String genus, String species, String infraFamily,
736 String infraGenus, String infraSpecies, String authorAbbrev, Boolean includePublicationAuthors,
737 Boolean includeBasionymAuthors,
738 String publicationTitle,
739 Boolean isAPNIRecord,
740 Boolean isGCIRecord,
741 Boolean isIKRecord,
742 Rank rankInRangeToReturn,
743 Boolean sortByFamily,
744 IpniServiceNamesConfigurator config,
745 ICdmApplicationConfiguration services){
746 IpniRank ipniRank = IpniRank.valueOf(rankInRangeToReturn);
747 return getNamesAdvanced(family, genus, species, infraFamily, infraGenus, infraSpecies, authorAbbrev, includePublicationAuthors, includeBasionymAuthors, publicationTitle, isAPNIRecord, isGCIRecord, isIKRecord, ipniRank, sortByFamily, config, services);
748 }
749
750 public List<BotanicalName> getNamesAdvanced(String family, String genus, String species, String infraFamily,
751 String infraGenus, String infraSpecies, String authorAbbrev, Boolean includePublicationAuthors,
752 Boolean includeBasionymAuthors,
753 String publicationTitle,
754 Boolean isAPNIRecord,
755 Boolean isGCIRecord,
756 Boolean isIKRecord,
757 IpniRank rankToReturn,
758 Boolean sortByFamily,
759 IpniServiceNamesConfigurator config,
760 ICdmApplicationConfiguration services) {
761
762 // find_rankToReturn=all&output_format=normal&find_sortByFamily=on&find_sortByFamily=off&query_type=by_query&back_page=plantsearch
763
764 //config
765 if (config == null){
766 config = new IpniServiceNamesConfigurator();
767 }
768
769
770 family = normalizeParameter(family);
771 genus = normalizeParameter(genus);
772 species = normalizeParameter(species);
773 infraFamily = normalizeParameter(infraFamily);
774 infraGenus = normalizeParameter(infraGenus);
775 infraSpecies = normalizeParameter(infraSpecies);
776 authorAbbrev = normalizeParameter(authorAbbrev);
777
778 publicationTitle = normalizeParameter(publicationTitle);
779
780 DelimitedFormat format = config.getFormat();
781
782 String request =
783 "find_family=" + family +
784 "&find_genus=" + genus +
785 "&find_species=" + species +
786 "&find_infrafamily=" + infraFamily +
787 "&find_infragenus=" + infraGenus +
788 "&find_infraspecies=" + infraSpecies +
789 "&find_authorAbbrev=" + authorAbbrev +
790 getBooleanParameter("&find_includePublicationAuthors=", includePublicationAuthors, "on", "off") +
791 getBooleanParameter("&find_includeBasionymAuthors=", includePublicationAuthors, "on", "off") +
792 getBooleanParameter("&find_find_isAPNIRecord=", includePublicationAuthors, "on", "false") +
793 getBooleanParameter("&find_isGCIRecord=", includePublicationAuthors, "on", "false") +
794 getBooleanParameter("&find_isIKRecord=", includePublicationAuthors, "on", "false") +
795
796
797 "&find_publicationTitle=" + publicationTitle +
798 "&output_format=" + format.parameter;
799
800 return (List)queryService(request, services, getServiceUrl(IIpniService.ADVANCED_NAME_SERVICE_URL), config, ServiceType.NAME);
801 }
802
803
804 private String getBooleanParameter(String urlParamString, Boolean booleanParameter, String trueString, String falseString) {
805 String result;
806 if (booleanParameter == null){
807 result = getBooleanParameter(urlParamString, true, trueString, falseString) + getBooleanParameter(urlParamString, false, trueString, falseString);
808 }else if (booleanParameter == true){
809 result = urlParamString + trueString;
810 }else {
811 result = urlParamString + falseString;
812 }
813 return result;
814 }
815
816
817 /* (non-Javadoc)
818 * @see eu.etaxonomy.cdm.ext.IIpniService#getNamesSimple(java.lang.String, eu.etaxonomy.cdm.ext.IIpniService.DelimitedFormat, eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration)
819 */
820 public List<BotanicalName> getNamesSimple(String wholeName, ICdmApplicationConfiguration services, IpniServiceNamesConfigurator config){
821 if (config == null){
822 config = new IpniServiceNamesConfigurator();
823 }
824
825
826 // query_type=by_query&back_page=query_ipni.html
827
828 wholeName = normalizeParameter(wholeName);
829
830 DelimitedFormat format = config.getFormat();
831
832 String request = "find_wholeName=" + wholeName +
833 "&output_format=" + format.parameter;
834
835 return (List)queryService(request, services, getServiceUrl(IIpniService.SIMPLE_NAME_SERVICE_URL), config, ServiceType.NAME);
836 }
837
838 /* (non-Javadoc)
839 * @see eu.etaxonomy.cdm.ext.IIpniService#getPublications(java.lang.String, java.lang.String, boolean, eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration)
840 */
841 public List<Reference> getPublications(String title, String abbreviation, ICdmApplicationConfiguration services, IpniServicePublicationConfigurator config){
842 // http://www.uk.ipni.org/ipni/advPublicationSearch.do?find_title=Spe*plant*&find_abbreviation=&output_format=normal&query_type=by_query&back_page=publicationsearch
843 // http://www.uk.ipni.org/ipni/advPublicationSearch.do?find_title=*Hortus+Britannicus*&find_abbreviation=&output_format=delimited-classic&output_format=delimited
844
845 if (config == null){
846 config = new IpniServicePublicationConfigurator();
847 }
848
849 title = normalizeParameter(title);
850 abbreviation = normalizeParameter(abbreviation);
851
852 String request = "find_title=" + title +
853 "&find_abbreviation=" + abbreviation +
854 "&output_format=" + DelimitedFormat.CLASSIC.parameter;
855
856 List<Reference> result = (List)queryService(request, services, getServiceUrl(IIpniService.PUBLICATION_SERVICE_URL), config, ServiceType.PUBLICATION);
857 return result;
858 }
859
860
861
862 /**
863 * @return
864 */
865 private DelimitedFormat getDefaultFormat() {
866 return DelimitedFormat.SHORT;
867 }
868
869
870 /**
871 * The service url
872 *
873 * @return the serviceUrl
874 */
875 public URL getServiceUrl(String url) {
876 URL serviceUrl;
877 try {
878 serviceUrl = new URL(url);
879 } catch (MalformedURLException e) {
880 throw new RuntimeException("This should not happen", e);
881 }
882 return serviceUrl;
883 }
884
885
886
887 }