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