Project

General

Profile

Download (31.9 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.NomenclaturalCode;
47
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
48
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
49
import eu.etaxonomy.cdm.model.name.Rank;
50
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
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
 * @author a.mueller
59
 * @created Aug 16, 2010
60
 *
61
 * TODO the whole ipni service should be refactored to use UriUtils. I did this for the queryService method only because we ran into timeout
62
 * problems in tests. UriUtils handles these problems already.
63
 *
64
 */
65
@Component
66
public class IpniService  implements IIpniService{
67
	private static final String EAST_OR_WEST = "East or west";
68

    
69
	private static final String NORTH_OR_SOUTH = "North or south";
70

    
71
	private static final String LATITUDE_SECONDS = "Latitude seconds";
72

    
73
	private static final String LATITUDE_MINUTES = "Latitude minutes";
74

    
75
	private static final String LATITUDE_DEGREES = "Latitude degrees";
76

    
77
	private static final String COLLECTION_DATE_AS_TEXT = "Collection date as text";
78

    
79
	private static final String COLLECTION_DAY1 = "Collection day1";
80

    
81
	private static final String COLLECTION_MONTH1 = "Collection month1";
82

    
83
	private static final String COLLECTION_YEAR1 = "Collection year1";
84

    
85
	private static final String COLLECTION_DAY2 = "Collection day2";
86

    
87
	private static final String COLLECTION_MONTH2 = "Collection month2";
88

    
89
	private static final String COLLECTION_YEAR2 = "Collection year2";
90

    
91
	private static final String COLLECTION_NUMBER = "Collection number";
92

    
93
	private static final String COLLECTOR_TEAM_AS_TEXT = "Collector team as text";
94

    
95
	private static final String LOCALITY = "Locality";
96

    
97
	private static final String TYPE_REMARKS = "Type remarks";
98

    
99
	private static final Logger logger = Logger.getLogger(IpniService.class);
100

    
101
	// GENERAL
102
	public static String ID = "Id";
103
	public static String VERSION = "Version";
104
	public static final String REMARKS = "Remarks";
105

    
106
	//NAMES
107
	public static final String FULL_NAME_WITHOUT_FAMILY_AND_AUTHORS = "Full name without family and authors";
108
	public static final String AUTHORS = "Authors";
109
	public static final String FAMILY = "Family";
110
	public static final String GENUS = "Genus";
111
	public static final String INFRA_GENUS = "Infra genus";
112
	public static final String SPECIES = "Species";
113
	public static final String INFRA_SPECIFIC = "Infra species";
114
	public static final String RANK = "Rank";
115
	public static final String BASIONYM_AUTHOR = "Basionym author";
116
	public static final String PUBLISHING_AUTHOR = "Publishing author";
117
	public static final String PUBLICATION = "Publication";
118
	public static final String PUBLICATION_YEAR_FULL = "Publication year full";
119
	public static final String NAME_STATUS = "Name status";
120
	public static final String BASIONYM = "Basionym";
121
	public static final String REPLACED_SYNONYM = "Replaced synonym";
122

    
123

    
124
	//AUTHORS
125

    
126
	public static final String STANDARD_FORM = "Standard Form";
127

    
128
	public static final String DEFAULT_AUTHOR_FORENAME = "Default author forename";
129
	public static final String DEFAULT_AUTHOR_SURNAME = "Default author surname";
130
	public static final String TAXON_GROUPS = "Taxon groups";
131
	public static final String DATES = "Dates";
132
	public static final String ALTERNATIVE_NAMES = "Alternative names";
133

    
134
	public static final String DEFAULT_AUTHOR_NAME = "Default author name";
135

    
136
	public static final String NAME_NOTES = "Name notes";
137
	public static final String NAME_SOURCE = "Name source";
138
	public static final String DATE_TYPE_CODE = "Date type code";
139
	public static final String DATE_TYPE_STRING = "Date type string";
140

    
141
	public static final String ALTERNATIVE_ABBREVIATIONS = "Alternative abbreviations";
142
	public static final String EXAMPLE_OF_NAME_PUBLISHED = "Example of name published";
143

    
144

    
145
	//PUBLICATIONS
146

    
147
	public static final String ABBREVIATION = "Abbreviation";
148
	public static final String TITLE = "Title";
149
	public static final String BPH_NUMBER = "BPH number";
150
	public static final String ISBN = "ISBN";
151
	public static final String ISSN = "ISSN";
152
	public static final String AUTHORS_ROLE = "Authors role";
153
	public static final String EDITION = "Edition";
154
	public static final String DATE = "Date";
155
	public static final String IN_PUBLICATION_FACADE = "In publication facade";
156
	public static final String LC_NUMBER = "LC number";
157
	public static final String PLACE = "Place";
158
	public static final String PUBLICATION_AUTHOR_TEAM = "Publication author team";
159
	public static final String PRECEDED_BY = "Preceded";
160
	public static final String TL2_AUTHOR = "TL2 author";
161
	public static final String TL2_NUMBER = "TL2 number";
162
	public static final String TDWG_ABBREVIATION = "TDWG abbreviation";
163

    
164
	private enum ServiceType{
165
		 AUTHOR,
166
		 NAME,
167
		 PUBLICATION,
168
		 ID
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
	@Override
219
    public List<Person> getAuthors(String abbreviation, String surname, String forename, String isoCountry, ICdmApplicationConfiguration 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, ICdmApplicationConfiguration 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
	public InputStream queryServiceForID (String request, URL serviceUrl){
301

    
302
		try {
303

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

    
311

    
312
            URI newUri = newUrl.toURI();
313

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

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

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

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

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

    
330
		return null;
331

    
332
	}
333

    
334

    
335

    
336

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

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

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

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

    
353
		return result;
354
	}
355

    
356

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

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

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

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

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

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

    
400

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

    
411

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

    
416

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

    
421

    
422

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

    
433
		return ref;
434
	}
435

    
436

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

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

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

    
448
			BotanicalName name = getNameFromLine(line,parameterMap, appConfig);
449
			result.add(name);
450
			line = reader.readLine();
451

    
452
		}
453

    
454

    
455
		return result;
456
	}
457

    
458

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

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

    
468
		BotanicalName name = TaxonNameFactory.NewBotanicalInstance(null);
469

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

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

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

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

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

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

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

    
516
		//basionym
517
		BotanicalName basionym = TaxonNameFactory.NewBotanicalInstance(null);
518
		basionym.setTitleCache(valueMap.get(BASIONYM), true);
519
		name.addBasionym(basionym);
520

    
521
		//replaced synonym
522
		BotanicalName replacedSynoynm = TaxonNameFactory.NewBotanicalInstance(null);
523
		replacedSynoynm.setTitleCache(valueMap.get(REPLACED_SYNONYM), true);
524
		name.addReplacedSynonym(replacedSynoynm, null, null, null);
525

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

    
529

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

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

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

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

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

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

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

    
568

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

    
576

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

    
581

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

    
585
		//TODO Geographic unit as text
586

    
587

    
588

    
589

    
590

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

    
595

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

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

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

    
636
		*/
637
		return name;
638
	}
639

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

    
659

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

    
665

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

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

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

    
683
		return result;
684
	}
685

    
686

    
687

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

    
699

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

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

    
709
		Person person = Person.NewInstance();
710

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

    
715
		Reference citation = getIpniCitation(appConfig);
716

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

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

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

    
736
		//TODO taxonGroups
737

    
738
		return person;
739
	}
740

    
741

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

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

    
767

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

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

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

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

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

    
812

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

    
821
		publicationTitle = normalizeParameter(publicationTitle);
822

    
823
		DelimitedFormat format = config.getFormat();
824

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

    
839

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

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

    
846

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

    
859

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

    
869

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

    
872
		wholeName = normalizeParameter(wholeName);
873

    
874
		DelimitedFormat format = config.getFormat();
875

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

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

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

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

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

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

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

    
905

    
906

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

    
914

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

    
931

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

    
935

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

    
939
	}
940

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

    
944

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

    
948
	}
949

    
950

    
951

    
952
}
(3-3/7)