Project

General

Profile

Download (32.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2009 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.ext.ipni;
10

    
11
import java.io.BufferedReader;
12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.io.InputStreamReader;
15
import java.net.HttpURLConnection;
16
import java.net.MalformedURLException;
17
import java.net.URI;
18
import java.net.URISyntaxException;
19
import java.net.URL;
20
import java.text.ParseException;
21
import java.util.ArrayList;
22
import java.util.HashMap;
23
import java.util.List;
24
import java.util.Map;
25

    
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.http.HttpResponse;
28
import org.apache.log4j.Logger;
29
import org.springframework.stereotype.Component;
30

    
31
import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
32
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
33
import eu.etaxonomy.cdm.common.CdmUtils;
34
import eu.etaxonomy.cdm.common.UriUtils;
35
import eu.etaxonomy.cdm.model.agent.Person;
36
import eu.etaxonomy.cdm.model.agent.Team;
37
import eu.etaxonomy.cdm.model.common.Annotation;
38
import eu.etaxonomy.cdm.model.common.AnnotationType;
39
import eu.etaxonomy.cdm.model.common.Extension;
40
import eu.etaxonomy.cdm.model.common.ExtensionType;
41
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
42
import eu.etaxonomy.cdm.model.common.Language;
43
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
44
import eu.etaxonomy.cdm.model.common.TimePeriod;
45
import eu.etaxonomy.cdm.model.name.BotanicalName;
46
import eu.etaxonomy.cdm.model.name.IBotanicalName;
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.name.TaxonNameBase;
52
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
53
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
56
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
57
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
58

    
59
/**
60
 * @author a.mueller
61
 * @created Aug 16, 2010
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
		 ID
171
	}
172

    
173
	public enum IpniRank{
174
		ALL ("All"),
175
		FAMILIAL ("Familial"),
176
		INFRA_FAMILIAL ("Infrafamilial"),
177
		GENERIC("Generic"),
178
		INFRA_GENERIC("Infrageneric"),
179
		SPECIFIC ("Specific"),
180
		INFRA_SPECIFIC("InfraSpecific");
181

    
182
		String strRank;
183
		IpniRank(String strRank){
184
			this.strRank = strRank;
185
		}
186

    
187
		public static IpniRank valueOf(Rank rank){
188
			if (rank == null){
189
				return ALL;
190
			}else if (rank.isInfraSpecific()){
191
				return INFRA_SPECIFIC;
192
			}else if (rank.isSpecies()){
193
				return SPECIFIC;
194
			}else if (rank.isInfraGeneric()){
195
				return INFRA_GENERIC;
196
			}else if (rank.isGenus()){
197
				return GENERIC;
198
			}else if (rank.isLower(Rank.FAMILY())){
199
				return INFRA_FAMILIAL;
200
			}else if (rank.isHigher(Rank.SUBFAMILY())){
201
				return FAMILIAL;
202
			}else{
203
				logger.warn("Rank could not be transformed to ipni rank. Use ALL instead");
204
				return ALL;
205
			}
206
		}
207
	}
208

    
209

    
210
//	private URL serviceUrl;
211

    
212
// ******************************** CONSTRUCTOR **************************************
213

    
214
// ****************************** METHODS ****************************************************/
215

    
216

    
217
	/* (non-Javadoc)
218
	 * @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)
219
	 */
220
	@Override
221
    public List<Person> getAuthors(String abbreviation, String surname, String forename, String isoCountry, ICdmApplicationConfiguration services, IpniServiceAuthorConfigurator config){
222
		//config
223
		if (config == null){
224
			config = new IpniServiceAuthorConfigurator();
225
		}
226

    
227

    
228
		abbreviation = normalizeParameter(abbreviation);
229
		surname = normalizeParameter(surname);
230
		isoCountry = normalizeParameter(isoCountry);
231
		forename = normalizeParameter(forename);
232

    
233
		DelimitedFormat format = config.getFormat();
234

    
235
		String request = "find_abbreviation=" + abbreviation +
236
						"&find_surname=" + surname +
237
						"&find_isoCountry=" + isoCountry +
238
						"&find_forename=" + forename +
239
						"&output_format=" + format.parameter;
240

    
241
		return (List)queryService(request, services, getServiceUrl(IIpniService.AUTHOR_SERVICE_URL), config, ServiceType.AUTHOR);
242
	}
243

    
244

    
245
	/**
246
	 *	FIXME rewrote this method to rely on {@link UriUtils}. The whole class should be adjusted to reflect this change.
247
	 *	Also see comments in the class' documentation block.
248
	 *
249
	 * @param restRequest
250
	 * @return
251
	*/
252
	private List<? extends IdentifiableEntity> queryService(String request, ICdmApplicationConfiguration services, URL serviceUrl, IIpniServiceConfigurator config, ServiceType serviceType){
253
		if (config == null){
254
			throw new NullPointerException("Ipni service configurator should not be null");
255
		}
256
		try {
257

    
258
            // create the request url
259
            URL newUrl = new URL(serviceUrl.getProtocol(),
260
                                                     serviceUrl.getHost(),
261
                                                     serviceUrl.getPort(),
262
                                                     serviceUrl.getPath()
263
                                                     + "?" + request);
264

    
265
            URI newUri = newUrl.toURI();
266
            logger.info("Firing request for URI: " + newUri);
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
	public InputStream queryServiceForID (String request, URL serviceUrl){
300

    
301
		try {
302

    
303
            // create the request url
304
            URL newUrl = new URL(serviceUrl.getProtocol(),
305
                                                     serviceUrl.getHost(),
306
                                                     serviceUrl.getPort(),
307
                                                     serviceUrl.getPath()
308
                                                     + "?" + request);
309

    
310

    
311
            URI newUri = newUrl.toURI();
312

    
313
            logger.info("Firing request for URI: " + newUri);
314

    
315
            HttpResponse response = UriUtils.getResponse(newUri, null);
316

    
317
            int responseCode = response.getStatusLine().getStatusCode();
318

    
319
            // get the content at the resource
320
            InputStream content = response.getEntity().getContent();
321
            return content;
322

    
323
		   } catch (IOException e) {
324
	            logger.error("No content for request: " + request);
325
	        } catch (URISyntaxException e) {
326
				logger.error("Given URL could not be transformed into URI", e);
327
			}
328

    
329
		return null;
330

    
331
	}
332

    
333

    
334

    
335

    
336
	private List<Reference> buildPublicationList( InputStream content, ICdmApplicationConfiguration services, IIpniServiceConfigurator iConfig) throws IOException {
337
		IpniServicePublicationConfigurator config = (IpniServicePublicationConfigurator)iConfig;
338

    
339
		List<Reference> result = new ArrayList<Reference>();
340
		BufferedReader reader = new BufferedReader (new InputStreamReader(content));
341

    
342
		String headerLine = reader.readLine();
343
		Map<Integer, String> parameterMap = getParameterMap(headerLine);
344

    
345
		String line = reader.readLine();
346
		while (StringUtils.isNotBlank(line)){
347
			Reference reference = getPublicationFromLine(line, parameterMap, services, config);
348
			result.add(reference);
349
			line = reader.readLine();
350
		}
351

    
352
		return result;
353
	}
354

    
355

    
356
	/**
357
	 * @param line
358
	 * @param parameterMap
359
	 * @param appConfig
360
	 * @param config
361
	 * @return
362
	 */
363
	private Reference getPublicationFromLine(String line, Map<Integer, String> parameterMap, ICdmApplicationConfiguration appConfig, IpniServicePublicationConfigurator config) {
364
		//fill value map
365
		String[] splits = line.split("%");
366

    
367
		Map<String, String> valueMap = new HashMap<String, String>();
368
		for (int i = 0; i < splits.length; i++){
369
			valueMap.put(parameterMap.get(i), splits[i]);
370
		}
371

    
372
		//create reference object
373
		Reference ref = ReferenceFactory.newGeneric();
374

    
375
		//reference
376
		if (config.isUseAbbreviationAsTitle() == true){
377
			ref.setTitle(valueMap.get(ABBREVIATION));
378
			//TODO handle title as extension
379
		}else{
380
			ref.setTitle(valueMap.get(TITLE));
381
			//TODO handle abbreviation as extension
382
		}
383
		ref.setIsbn(valueMap.get(ISBN));
384
		ref.setIssn(valueMap.get(ISSN));
385
		ref.setEdition(valueMap.get(EDITION));
386
		ref.setPlacePublished(valueMap.get(PLACE));
387

    
388
		String author = valueMap.get(PUBLICATION_AUTHOR_TEAM);
389
		if (StringUtils.isNotBlank(author)){
390
			Team team = Team.NewTitledInstance(author, author);
391
			ref.setAuthorship(team);
392
		}
393

    
394
		//remarks
395
		String remarks = valueMap.get(REMARKS);
396
		Annotation annotation = Annotation.NewInstance(remarks, AnnotationType.EDITORIAL(), Language.ENGLISH());
397
		ref.addAnnotation(annotation);
398

    
399

    
400
		String tl2AuthorString = valueMap.get(TL2_AUTHOR);
401
		if (ref.getAuthorship() == null){
402
			Team tl2Author = Team.NewTitledInstance(tl2AuthorString, null);
403
			ref.setAuthorship(tl2Author);
404
		}else{
405
			//TODO parse name,
406
			ref.getAuthorship().setTitleCache(tl2AuthorString, true);
407
			ref.addAnnotation(Annotation.NewInstance(tl2AuthorString, AnnotationType.EDITORIAL(), Language.ENGLISH()));
408
		}
409

    
410

    
411
		//dates
412
		TimePeriod date = TimePeriodParser.parseString(valueMap.get(DATE));
413
		ref.setDatePublished(date);
414

    
415

    
416
		//source
417
		Reference citation = getIpniCitation(appConfig);
418
		ref.addSource(OriginalSourceType.Lineage, valueMap.get(ID), "Publication", citation, valueMap.get(VERSION));
419

    
420

    
421

    
422
/*		TODO
423
		BPH number
424
		Authors role
425
		In publication facade
426
		LC number
427
		Preceded by
428
		TL2 number
429
		TDWG abbreviation
430
	*/
431

    
432
		return ref;
433
	}
434

    
435

    
436
	private List<TaxonNameBase<?,?>> buildNameList( InputStream content, ICdmApplicationConfiguration appConfig, IIpniServiceConfigurator iConfig) throws IOException {
437
		IpniServiceNamesConfigurator config = (IpniServiceNamesConfigurator)iConfig;
438
		List<TaxonNameBase<?,?>> result = new ArrayList<>();
439
		BufferedReader reader = new BufferedReader (new InputStreamReader(content));
440

    
441
		String headerLine = reader.readLine();
442
		Map<Integer, String> parameterMap = getParameterMap(headerLine);
443

    
444
		String line = reader.readLine();
445
		while (StringUtils.isNotBlank(line)){
446

    
447
		    TaxonNameBase<?,?> name = (TaxonNameBase<?,?>)getNameFromLine(line,parameterMap, appConfig);
448
			result.add(name);
449
			line = reader.readLine();
450

    
451
		}
452

    
453

    
454
		return result;
455
	}
456

    
457

    
458
	private IBotanicalName getNameFromLine(String line, Map<Integer, String> parameterMap, ICdmApplicationConfiguration appConfig) {
459
		//Id%Version%Standard form%Default author forename%Default author surname%Taxon groups%Dates%Alternative names
460
		String[] splits = line.split("%");
461
		Map<String, String> valueMap = new HashMap<String, String>();
462

    
463
		for (int i = 0; i < splits.length; i++){
464
			valueMap.put(parameterMap.get(i), splits[i]);
465
		}
466

    
467
		IBotanicalName name = TaxonNameFactory.NewBotanicalInstance(null);
468

    
469
		//caches
470
		name.setNameCache(valueMap.get(FULL_NAME_WITHOUT_FAMILY_AND_AUTHORS), true);
471
		name.setAuthorshipCache(valueMap.get(AUTHORS), true);
472

    
473
		//epithets
474
		name.setGenusOrUninomial(valueMap.get(GENUS));
475
		name.setInfraGenericEpithet(valueMap.get(INFRA_GENUS));
476
		name.setSpecificEpithet(valueMap.get(SPECIES));
477
		name.setInfraSpecificEpithet(valueMap.get(INFRA_SPECIFIC));
478

    
479
		//rank
480
		try {
481
			String rankStr = nomalizeRank(valueMap.get(RANK));
482
			name.setRank(Rank.getRankByNameOrIdInVoc(rankStr, NomenclaturalCode.ICNAFP, true));
483
		} catch (UnknownCdmTypeException e) {
484
			logger.warn("Rank was unknown");
485
		}
486

    
487
		//authors
488
		name.setBasionymAuthorship(Team.NewTitledInstance(valueMap.get(BASIONYM_AUTHOR), valueMap.get(BASIONYM_AUTHOR)));
489
		name.setCombinationAuthorship(Team.NewTitledInstance(valueMap.get(PUBLISHING_AUTHOR), valueMap.get(PUBLISHING_AUTHOR)));
490

    
491
		//publication
492
		Reference ref = ReferenceFactory.newGeneric();
493
		ref.setTitleCache(valueMap.get(PUBLICATION), true);
494
		TimePeriod datePublished = TimePeriodParser.parseString(valueMap.get(PUBLICATION_YEAR_FULL));
495
		name.setNomenclaturalReference(ref);
496

    
497
		//name status
498
		NomenclaturalStatusType statusType = null;
499
		String statusString = valueMap.get(NAME_STATUS);
500
		if (StringUtils.isNotBlank(statusString)){
501
			try {
502
				statusType = NomenclaturalStatusType.getNomenclaturalStatusTypeByAbbreviation(statusString, name);
503
				NomenclaturalStatus nomStatus = NomenclaturalStatus.NewInstance(statusType);
504
				name.addStatus(nomStatus);
505
			} catch (UnknownCdmTypeException e) {
506
				logger.warn("Name status not recognized: " + statusString);
507
			}
508
		}
509

    
510
		//remarks
511
		String remarks = valueMap.get(REMARKS);
512
		Annotation annotation = Annotation.NewInstance(remarks, AnnotationType.EDITORIAL(), Language.ENGLISH());
513
		name.addAnnotation(annotation);
514

    
515
		//basionym
516
		TaxonNameBase<?,?> basionym = TaxonNameFactory.NewBotanicalInstance(null);
517
		basionym.setTitleCache(valueMap.get(BASIONYM), true);
518
		name.addBasionym(basionym);
519

    
520
		//replaced synonym
521
		TaxonNameBase<?,?> replacedSynoynm = TaxonNameFactory.NewBotanicalInstance(null);
522
		replacedSynoynm.setTitleCache(valueMap.get(REPLACED_SYNONYM), true);
523
		name.addReplacedSynonym(replacedSynoynm, null, null, null);
524

    
525
		//type information
526
		DerivedUnitFacade specimen = DerivedUnitFacade.NewInstance(SpecimenOrObservationType.PreservedSpecimen);
527

    
528

    
529
		//gathering period
530
		String collectionDateAsText = valueMap.get(COLLECTION_DATE_AS_TEXT);
531
		TimePeriod gatheringPeriod = TimePeriodParser.parseString(collectionDateAsText);
532

    
533
		try {
534
			gatheringPeriod.setStartDay(getIntegerDateValueOrNull(valueMap, COLLECTION_DAY1));
535
			gatheringPeriod.setStartMonth(getIntegerDateValueOrNull(valueMap, COLLECTION_MONTH1));
536
			gatheringPeriod.setStartYear(getIntegerDateValueOrNull(valueMap, COLLECTION_YEAR1));
537
			gatheringPeriod.setEndDay(getIntegerDateValueOrNull(valueMap, COLLECTION_DAY2));
538
			gatheringPeriod.setEndMonth(getIntegerDateValueOrNull(valueMap, COLLECTION_MONTH2));
539
			gatheringPeriod.setEndYear(getIntegerDateValueOrNull(valueMap, COLLECTION_YEAR2));
540
		} catch (IndexOutOfBoundsException e) {
541
			logger.info("Exception occurred when trying to fill gathering period");
542
		}
543
		specimen.setGatheringPeriod(gatheringPeriod);
544

    
545
		specimen.setFieldNumber(valueMap.get(COLLECTION_NUMBER));
546

    
547
		//collector team
548
		String team = valueMap.get(COLLECTOR_TEAM_AS_TEXT);
549
		Team collectorTeam = Team.NewTitledInstance(team, team);
550
		specimen.setCollector(collectorTeam);
551

    
552
		specimen.setLocality(valueMap.get(LOCALITY));
553

    
554
		try {
555
			String latDegrees = CdmUtils.Nz(valueMap.get(LATITUDE_DEGREES));
556
			String latMinutes = CdmUtils.Nz(valueMap.get(LATITUDE_MINUTES));
557
			String latSeconds = CdmUtils.Nz(valueMap.get(LATITUDE_SECONDS));
558
			String direction = CdmUtils.Nz(valueMap.get(NORTH_OR_SOUTH));
559
			String latitude = latDegrees + "°" + latMinutes + "'" + latSeconds + "\"" + direction;
560

    
561
			String lonDegrees = CdmUtils.Nz(valueMap.get(LATITUDE_DEGREES));
562
			String lonMinutes = CdmUtils.Nz(valueMap.get(LATITUDE_MINUTES));
563
			String lonSeconds = CdmUtils.Nz(valueMap.get(LATITUDE_SECONDS));
564
			direction = CdmUtils.Nz(valueMap.get(EAST_OR_WEST));
565
			String longitude = lonDegrees + "°" + lonMinutes + "'" + lonSeconds + "\"" + direction;
566

    
567

    
568
			specimen.setExactLocationByParsing(longitude, latitude, null, null);
569
		} catch (ParseException e) {
570
			logger.info("Parsing exception occurred when trying to parse type exact location."  + e.getMessage());
571
		} catch (Exception e) {
572
			logger.info("Exception occurred when trying to read type exact location."  + e.getMessage());
573
		}
574

    
575

    
576
		//type annotation
577
		Annotation typeAnnotation = Annotation.NewInstance(TYPE_REMARKS, AnnotationType.EDITORIAL(), Language.DEFAULT());
578
		specimen.addAnnotation(typeAnnotation);
579

    
580

    
581
		//TODO  Type name
582
		//TODO "Type locations"  , eg. holotype   CAT  ,isotype   CAT  ,isotype   FI
583

    
584
		//TODO Geographic unit as text
585

    
586

    
587

    
588

    
589

    
590
		//source
591
		Reference citation = getIpniCitation(appConfig);
592
		name.addSource(OriginalSourceType.Lineage, valueMap.get(ID), "Name", citation, valueMap.get(VERSION));
593

    
594

    
595
//		//TODO
596
		//SHORT Family, Infra family, Hybrid genus, Hybrid, Collation, Nomenclatural synonym, Distribution, Citation type
597
/*		EXTENDED
598
 *      Species author,
599
 *       Standardised basionym author flag,
600
 *       Standardised publishing author flag
601
	      Full name
602
	      Full name without family
603
	      Full name without authors
604

    
605
	      Reference
606
	      Standardised publication flag
607
	      Publication year
608
	      publication year note
609
	      Publication year text
610
	      Volume
611
	      Start page
612
	      End page
613
	      Primary pagination
614
	      Secondary pagination
615
	      Reference remarks
616
	      Hybrid parents
617
	      Replaced synonym Author team
618
	      Other links
619
	      Same citation as
620
	      Bibliographic reference
621
	      Bibliographic type info
622

    
623
	      Original taxon name
624
	      Original taxon name author team
625
	      Original replaced synonym
626
	      Original replaced synonym author team
627
	      Original basionym
628
	      Original basionym author team
629
	      Original parent citation taxon name author team
630
	      Original taxon distribution
631
	      Original hybrid parentage
632
	      Original cited type
633
	      Original remarks
634

    
635
		*/
636
		return name;
637
	}
638

    
639
	/**
640
	 * @param valueMap
641
	 * @return
642
	 */
643
	private Integer getIntegerDateValueOrNull(Map<String, String> valueMap, String key) {
644
		try {
645
			Integer result = Integer.valueOf(valueMap.get(key));
646
			if (result == 0){
647
				result = null;
648
			}
649
			return result;
650
		} catch (NumberFormatException e) {
651
			if (logger.isDebugEnabled()){
652
				logger.debug("Number Format exception for " + valueMap.get(key));
653
			}
654
			return null;
655
		}
656
	}
657

    
658

    
659
	private String nomalizeRank(String string) {
660
		String result = string.replace("spec.", "sp.");
661
		return result;
662
	}
663

    
664

    
665
	private List<Person> buildAuthorList(InputStream content, ICdmApplicationConfiguration services, IIpniServiceConfigurator iConfig) throws IOException {
666
		IpniServiceAuthorConfigurator config = (IpniServiceAuthorConfigurator)iConfig;
667
		List<Person> result = new ArrayList<Person>();
668
		BufferedReader reader = new BufferedReader (new InputStreamReader(content));
669

    
670
		String headerLine = reader.readLine();
671
		if (headerLine != null){
672
			Map<Integer, String> parameterMap = getParameterMap(headerLine);
673

    
674
			String line = reader.readLine();
675
			while (StringUtils.isNotBlank(line)){
676
				Person author = getAuthorFromLine(line,parameterMap, services, config);
677
				result.add(author);
678
				line = reader.readLine();
679
			}
680
		}
681

    
682
		return result;
683
	}
684

    
685

    
686

    
687
	private Map<Integer, String> getParameterMap(String headerLine) {
688
		Map<Integer, String> result = new HashMap<Integer, String>();
689
		if ( headerLine != null ){
690
			String[] splits = headerLine.split("%");
691
			for (int i = 0; i < splits.length ; i ++){
692
				result.put(i, splits[i]);
693
			}
694
		}
695
		return result;
696
	}
697

    
698

    
699
	private Person getAuthorFromLine(String line, Map<Integer, String> categoryMap, ICdmApplicationConfiguration appConfig, IpniServiceAuthorConfigurator config) {
700
		//Id%Version%Standard form%Default author forename%Default author surname%Taxon groups%Dates%Alternative names
701
		String[] splits = line.split("%");
702
		Map<String, String> valueMap = new HashMap<String, String>();
703

    
704
		for (int i = 0; i < splits.length; i++){
705
			valueMap.put(categoryMap.get(i), splits[i]);
706
		}
707

    
708
		Person person = Person.NewInstance();
709

    
710
		person.setNomenclaturalTitle(valueMap.get(STANDARD_FORM));
711
		person.setFirstname(valueMap.get(DEFAULT_AUTHOR_FORENAME));
712
		person.setLastname(valueMap.get(DEFAULT_AUTHOR_SURNAME));
713

    
714
		Reference citation = getIpniCitation(appConfig);
715

    
716
		//id, version
717
		person.addSource(OriginalSourceType.Lineage, valueMap.get(ID), "Author", citation, valueMap.get(VERSION));
718

    
719
		//dates
720
		TimePeriod lifespan = TimePeriodParser.parseString(valueMap.get(DATES));
721
		person.setLifespan(lifespan);
722

    
723
		//alternative_names
724
		String alternativeNames = valueMap.get(ALTERNATIVE_NAMES);
725
		if (StringUtils.isNotBlank(alternativeNames)){
726
			String[] alternativeNameSplits = alternativeNames.split("%");
727
			for (String alternativeName : alternativeNameSplits){
728
				if (alternativeName.startsWith(">")){
729
					alternativeName = alternativeName.substring(1);
730
				}
731
				Extension.NewInstance(person, alternativeName, ExtensionType.INFORMAL_CATEGORY());
732
			}
733
		}
734

    
735
		//TODO taxonGroups
736

    
737
		return person;
738
	}
739

    
740

    
741
	private Reference getIpniCitation(ICdmApplicationConfiguration appConfig) {
742
		Reference ipniReference;
743
		if (appConfig != null){
744
			ipniReference = appConfig.getReferenceService().find(uuidIpni);
745
			if (ipniReference == null){
746
				ipniReference = getNewIpniReference();
747
				ipniReference.setUuid(uuidIpni);
748
				appConfig.getReferenceService().save(ipniReference);
749
			}
750
		}else{
751
			ipniReference = getNewIpniReference();
752
		}
753
		return ipniReference;
754
	}
755

    
756
	/**
757
	 * @return
758
	 */
759
	private Reference getNewIpniReference() {
760
		Reference ipniReference;
761
		ipniReference = ReferenceFactory.newDatabase();
762
		ipniReference.setTitleCache("The International Plant Names Index (IPNI)");
763
		return ipniReference;
764
	}
765

    
766

    
767
	/**
768
	 * @param parameter
769
	 */
770
	private String normalizeParameter(String parameter) {
771
		String result = CdmUtils.Nz(parameter).replace(" ", "+");
772
		return result;
773
	}
774

    
775
	@Override
776
    public List<BotanicalName> getNamesAdvanced(String family, String genus, String species, String infraFamily,
777
			String infraGenus, String infraSpecies, String authorAbbrev, Boolean includePublicationAuthors,
778
			Boolean includeBasionymAuthors,
779
			String publicationTitle,
780
			Boolean isAPNIRecord,
781
			Boolean isGCIRecord,
782
			Boolean isIKRecord,
783
			Rank rankInRangeToReturn,
784
			Boolean sortByFamily,
785
			IpniServiceNamesConfigurator config,
786
			ICdmApplicationConfiguration services){
787
		IpniRank ipniRank = IpniRank.valueOf(rankInRangeToReturn);
788
		return getNamesAdvanced(family, genus, species, infraFamily, infraGenus, infraSpecies, authorAbbrev, includePublicationAuthors, includeBasionymAuthors, publicationTitle, isAPNIRecord, isGCIRecord, isIKRecord, ipniRank, sortByFamily, config, services);
789
	}
790

    
791
	@Override
792
    public List<BotanicalName> getNamesAdvanced(String family, String genus, String species, String infraFamily,
793
			String infraGenus, String infraSpecies, String authorAbbrev, Boolean includePublicationAuthors,
794
			Boolean includeBasionymAuthors,
795
			String publicationTitle,
796
			Boolean isAPNIRecord,
797
			Boolean isGCIRecord,
798
			Boolean isIKRecord,
799
			IpniRank rankToReturn,
800
			Boolean sortByFamily,
801
			IpniServiceNamesConfigurator config,
802
			ICdmApplicationConfiguration services) {
803

    
804
//		find_rankToReturn=all&output_format=normal&find_sortByFamily=on&find_sortByFamily=off&query_type=by_query&back_page=plantsearch
805

    
806
		//config
807
		if (config == null){
808
			config = new IpniServiceNamesConfigurator();
809
		}
810

    
811

    
812
		family = normalizeParameter(family);
813
		genus = normalizeParameter(genus);
814
		species = normalizeParameter(species);
815
		infraFamily = normalizeParameter(infraFamily);
816
		infraGenus = normalizeParameter(infraGenus);
817
		infraSpecies = normalizeParameter(infraSpecies);
818
		authorAbbrev = normalizeParameter(authorAbbrev);
819

    
820
		publicationTitle = normalizeParameter(publicationTitle);
821

    
822
		DelimitedFormat format = config.getFormat();
823

    
824
		String request =
825
				"find_family=" + family +
826
				"&find_genus=" + genus +
827
				"&find_species=" + species +
828
				"&find_infrafamily=" + infraFamily +
829
				"&find_infragenus=" + infraGenus +
830
				"&find_infraspecies=" + infraSpecies +
831
				"&find_authorAbbrev=" + authorAbbrev +
832
				getBooleanParameter("&find_includePublicationAuthors=", includePublicationAuthors, "on", "off") +
833
				getBooleanParameter("&find_includeBasionymAuthors=", includePublicationAuthors, "on", "off") +
834
				getBooleanParameter("&find_find_isAPNIRecord=", includePublicationAuthors, "on", "false") +
835
				getBooleanParameter("&find_isGCIRecord=", includePublicationAuthors, "on", "false") +
836
				getBooleanParameter("&find_isIKRecord=", includePublicationAuthors, "on", "false") +
837

    
838

    
839
				"&find_publicationTitle=" + publicationTitle +
840
				"&output_format=" + format.parameter;
841

    
842
		return (List)queryService(request, services, getServiceUrl(IIpniService.ADVANCED_NAME_SERVICE_URL), config, ServiceType.NAME);
843
	}
844

    
845

    
846
	private String getBooleanParameter(String urlParamString, Boolean booleanParameter, String trueString, String falseString) {
847
		String result;
848
		if (booleanParameter == null){
849
			result = getBooleanParameter(urlParamString, true, trueString, falseString) + getBooleanParameter(urlParamString, false, trueString, falseString);
850
		}else if (booleanParameter == true){
851
			result = urlParamString + trueString;
852
		}else {
853
			result = urlParamString + falseString;
854
		}
855
		return result;
856
	}
857

    
858

    
859
	/* (non-Javadoc)
860
	 * @see eu.etaxonomy.cdm.ext.IIpniService#getNamesSimple(java.lang.String, eu.etaxonomy.cdm.ext.IIpniService.DelimitedFormat, eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration)
861
	 */
862
	@Override
863
    public List<BotanicalName> getNamesSimple(String wholeName, ICdmApplicationConfiguration services, IpniServiceNamesConfigurator config){
864
		if (config == null){
865
			config = new IpniServiceNamesConfigurator();
866
		}
867

    
868

    
869
//		query_type=by_query&back_page=query_ipni.html
870

    
871
		wholeName = normalizeParameter(wholeName);
872

    
873
		DelimitedFormat format = config.getFormat();
874

    
875
		String request = "find_wholeName=" + wholeName +
876
						"&output_format=" + format.parameter;
877

    
878
		return (List)queryService(request, services, getServiceUrl(IIpniService.SIMPLE_NAME_SERVICE_URL), config, ServiceType.NAME);
879
	}
880

    
881
	/* (non-Javadoc)
882
	 * @see eu.etaxonomy.cdm.ext.IIpniService#getPublications(java.lang.String, java.lang.String, boolean, eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration)
883
	 */
884
	@Override
885
    public List<Reference> getPublications(String title, String abbreviation, ICdmApplicationConfiguration services, IpniServicePublicationConfigurator config){
886
//		http://www.uk.ipni.org/ipni/advPublicationSearch.do?find_title=Spe*plant*&find_abbreviation=&output_format=normal&query_type=by_query&back_page=publicationsearch
887
//		http://www.uk.ipni.org/ipni/advPublicationSearch.do?find_title=*Hortus+Britannicus*&find_abbreviation=&output_format=delimited-classic&output_format=delimited
888

    
889
		if (config == null){
890
			config = new IpniServicePublicationConfigurator();
891
		}
892

    
893
		title = normalizeParameter(title);
894
		abbreviation = normalizeParameter(abbreviation);
895

    
896
		String request = "find_title=" + title +
897
						"&find_abbreviation=" + abbreviation +
898
						"&output_format=" + DelimitedFormat.CLASSIC.parameter;
899

    
900
		List<Reference> result = (List)queryService(request, services, getServiceUrl(IIpniService.PUBLICATION_SERVICE_URL), config, ServiceType.PUBLICATION);
901
		return result;
902
	}
903

    
904

    
905

    
906
	/**
907
	 * @return
908
	 */
909
	private DelimitedFormat getDefaultFormat() {
910
		return DelimitedFormat.SHORT;
911
	}
912

    
913

    
914
	/**
915
	 * The service url
916
	 *
917
	 * @return the serviceUrl
918
	 */
919
	@Override
920
    public URL getServiceUrl(String url) {
921
		URL serviceUrl;
922
		try {
923
			serviceUrl = new URL(url);
924
		} catch (MalformedURLException e) {
925
			throw new RuntimeException("This should not happen", e);
926
		}
927
		return serviceUrl;
928
	}
929

    
930

    
931
	@Override
932
	public InputStream getNamesById(String id) {
933

    
934

    
935
		String request = "id="+id + "&output_format=lsid-metadata";
936
		return queryServiceForID(request, getServiceUrl(IIpniService.ID_NAMESEARCH_SERVICE_URL));
937

    
938
	}
939

    
940
	@Override
941
	public InputStream getPublicationsById(String id) {
942

    
943

    
944
		String request = "id="+id ;
945
		return queryServiceForID(request, getServiceUrl(IIpniService.ID_PUBLICATION_SERVICE_URL));
946

    
947
	}
948

    
949

    
950

    
951
}
(3-3/7)