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