Project

General

Profile

Download (38 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2007 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.io.globis;
10

    
11
import java.sql.ResultSet;
12
import java.sql.SQLException;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.Set;
18
import java.util.UUID;
19
import java.util.regex.Matcher;
20
import java.util.regex.Pattern;
21

    
22
import org.apache.logging.log4j.LogManager;
23
import org.apache.logging.log4j.Logger;
24
import org.springframework.stereotype.Component;
25

    
26
import eu.etaxonomy.cdm.common.CdmUtils;
27
import eu.etaxonomy.cdm.facade.DerivedUnitFacade;
28
import eu.etaxonomy.cdm.io.common.IOValidator;
29
import eu.etaxonomy.cdm.io.common.ResultSetPartitioner;
30
import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
31
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
32
import eu.etaxonomy.cdm.io.globis.validation.GlobisSpecTaxaImportValidator;
33
import eu.etaxonomy.cdm.model.agent.AgentBase;
34
import eu.etaxonomy.cdm.model.common.CdmBase;
35
import eu.etaxonomy.cdm.model.common.ExtensionType;
36
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
37
import eu.etaxonomy.cdm.model.common.Marker;
38
import eu.etaxonomy.cdm.model.common.MarkerType;
39
import eu.etaxonomy.cdm.model.description.Feature;
40
import eu.etaxonomy.cdm.model.location.NamedArea;
41
import eu.etaxonomy.cdm.model.name.IZoologicalName;
42
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
43
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
44
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
45
import eu.etaxonomy.cdm.model.name.Rank;
46
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
47
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
48
import eu.etaxonomy.cdm.model.name.TaxonName;
49
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
50
import eu.etaxonomy.cdm.model.occurrence.Collection;
51
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
52
import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
53
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
54
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
55
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
56
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
57
import eu.etaxonomy.cdm.model.reference.Reference;
58
import eu.etaxonomy.cdm.model.taxon.Synonym;
59
import eu.etaxonomy.cdm.model.taxon.SynonymType;
60
import eu.etaxonomy.cdm.model.taxon.Taxon;
61
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
62
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
63

    
64
/**
65
 * @author a.mueller
66
 * @since 20.02.2010
67
 */
68
@Component
69
public class GlobisSpecTaxImport  extends GlobisImportBase<Reference> implements IMappingImport<Reference, GlobisImportState>{
70

    
71
    private static final long serialVersionUID = 1L;
72
    private static final Logger logger = LogManager.getLogger();
73

    
74
	private int modCount = 10000;
75
	private static final String pluralString = "taxa";
76
	private static final String dbTableName = "specTax";
77
	private static final Class<?> cdmTargetClass = Reference.class;
78

    
79
	private static UUID uuidCitedTypeLocality = UUID.fromString("ca431e0a-84ec-4828-935f-df4c8f5cf880");
80
	private static UUID uuidCitedTypeMaterial = UUID.fromString("8395021a-e596-4a55-9794-8c03aaad9e16");
81

    
82
	public GlobisSpecTaxImport(){
83
		super(pluralString, dbTableName, cdmTargetClass);
84
	}
85

    
86
	@Override
87
	protected String getIdQuery() {
88
		String strRecordQuery =
89
			" SELECT specTaxId " +
90
			" FROM " + dbTableName;
91
		return strRecordQuery;
92
	}
93

    
94
	@Override
95
	protected String getRecordQuery(GlobisImportConfigurator config) {
96
		String strRecordQuery =
97
			" SELECT t.*, t.DateCreated as Created_When, t.CreatedBy as Created_Who," +
98
			"        t.ModifiedBy as Updated_who, t.DateModified as Updated_When, t.SpecRemarks as Notes " +
99
			" FROM " + getTableName() + " t " +
100
			" WHERE ( t.specTaxId IN (" + ID_LIST_TOKEN + ") )";
101
		return strRecordQuery;
102
	}
103

    
104

    
105
	@Override
106
	public boolean doPartition(ResultSetPartitioner partitioner, GlobisImportState state) {
107
		boolean success = true;
108

    
109
		Set<TaxonBase> objectsToSave = new HashSet<>();
110
		Set<TaxonName> namesToSave = new HashSet<>();
111

    
112
		Map<String, Taxon> taxonMap = partitioner.getObjectMap(TAXON_NAMESPACE);
113
		Map<String, Reference> referenceMap = partitioner.getObjectMap(REFERENCE_NAMESPACE);
114

    
115
		ResultSet rs = partitioner.getResultSet();
116

    
117
		try {
118

    
119
			int i = 0;
120

    
121
			//for each reference
122
            while (rs.next()){
123

    
124
        		if ((i++ % modCount) == 0 && i!= 1 ){ logger.info(pluralString + " handled: " + (i-1));}
125

    
126
        		Integer specTaxId = rs.getInt("SpecTaxId");
127
        		Integer acceptedTaxonId = nullSafeInt(rs, "SpecCurrspecID");
128
        		String specSystaxRank = rs.getString("SpecSystaxRank");
129

    
130
        		//ignore: CountryDummy, currentSpecies, DepositoryDisplay, DepositoryDummy, ReferenceDisplay,
131
        		//        SpecDescriptionImageFile, all *Valid*
132

    
133
				try {
134

    
135
					//source ref
136
					Reference sourceRef = state.getTransactionalSourceReference();
137

    
138
					Taxon acceptedTaxon =  taxonMap.get(String.valueOf(acceptedTaxonId));
139
					TaxonBase<?> thisTaxon = null;
140

    
141
					IZoologicalName name = null;
142
					if (isBlank(specSystaxRank) ){
143
						name = makeName(state, rs, specTaxId);
144
					}else if (specSystaxRank.equals("synonym")){
145
						Synonym synonym = getSynonym(state, rs, specTaxId);
146
						if (acceptedTaxon == null){
147
							if (acceptedTaxonId == null){
148
								logger.warn("Synonym has no accepted taxon defined. SpecTaxId: "+ specTaxId);
149
							}else{
150
								logger.warn("Accepted taxon (" + acceptedTaxonId + ") not found for synonym "+ specTaxId);
151
							}
152
						}else{
153
							acceptedTaxon.addSynonym(synonym, SynonymType.SYNONYM_OF);
154
							thisTaxon = synonym;
155
						}
156
					}else if (specSystaxRank.equals("species")){
157
						validateAcceptedTaxon(acceptedTaxon, rs, specTaxId, acceptedTaxonId);
158
						thisTaxon = acceptedTaxon;
159
					}else{
160
						logger.warn(String.format("Unhandled specSystaxRank %s in specTaxId %d", specSystaxRank, specTaxId));
161
						name = makeName(state, rs, specTaxId);
162
					}
163

    
164
					if (thisTaxon != null){
165
						name = CdmBase.deproxy(thisTaxon.getName(), TaxonName.class);
166
					}else{
167
						if (name == null){
168
							name = makeName(state, rs, specTaxId);
169
						}
170

    
171
						thisTaxon = Taxon.NewInstance(name, sourceRef);
172
						objectsToSave.add(thisTaxon);
173
					}
174
					if (name == null){
175
						throw new RuntimeException("Name is still null");
176
					}
177

    
178
					handleNomRef(state, referenceMap, rs, name, specTaxId);
179

    
180
					handleTypeInformation(state,rs, name, specTaxId);
181

    
182

    
183
//					this.doIdCreatedUpdatedNotes(state, ref, rs, refId, REFERENCE_NAMESPACE);
184

    
185
					if (acceptedTaxon != null){
186
						objectsToSave.add(acceptedTaxon);
187
					}
188

    
189
					//makeMarker1(state, rs, name);   //ignore!
190

    
191
					//make not available
192
					makeNotAvailable(state, rs, name, specTaxId);
193

    
194
					//maken invalid
195
					//TODO
196

    
197
					//SpecCitedTypeLocality
198
					String citedTypeLocality = rs.getString("SpecCitedTypeLocality");
199
					if (isNotBlank(citedTypeLocality)){
200
//						ExtensionType exTypeCitedTypeLoc = getExtensionType(state, uuidCitedTypeLocality, "Type locality as cited in original description", "Type locality as cited in original description", null, ExtensionType.DOI().getVocabulary());
201
//						name.addExtension(citedTypeLocality, exTypeCitedTypeLoc);
202
						addNameDescription(state, name, uuidCitedTypeLocality, citedTypeLocality, "Type locality as cited in original description");
203
					}
204

    
205
					//SpecCitedTypeMaterial
206
					String citedTypeMaterial = rs.getString("SpecCitedTypeMaterial");
207
					if (isNotBlank(citedTypeMaterial)){
208
						ExtensionType exTypeCitedTypeLoc = getExtensionType(state, uuidCitedTypeMaterial, "Type material as cited in original description", "Type material as cited in original description", null, ExtensionType.DOI().getVocabulary());
209
						name.addExtension(citedTypeLocality, exTypeCitedTypeLoc);
210
					}
211

    
212
					name.addSource(OriginalSourceType.Import, String.valueOf(specTaxId), SPEC_TAX_NAMESPACE, state.getTransactionalSourceReference(), null);
213

    
214
					namesToSave.add(TaxonName.castAndDeproxy(name));
215

    
216

    
217
				} catch (Exception e) {
218
					logger.warn("Exception in specTax: SpecTaxId " + specTaxId + ". " + e.getMessage());
219
					e.printStackTrace();
220
				}
221

    
222
            }
223

    
224
			logger.warn(pluralString + " to save: " + objectsToSave.size());
225
			getTaxonService().save(objectsToSave);
226
			getNameService().save(namesToSave);
227

    
228
			return success;
229
		} catch (Exception e) {
230
			logger.error("Exception: " +  e);
231
			return false;
232
		}
233
	}
234

    
235

    
236
	private void makeNotAvailable(GlobisImportState state, ResultSet rs, IZoologicalName name, int id) throws SQLException {
237
		String notAvailableStr = rs.getString("SpecNotAvailable");
238
		String notAvailableReason = rs.getString("SpecNotAvailableReason");
239

    
240
		if (isNotBlank(notAvailableStr) && notAvailableStr.contains("not available")){
241
			if (isBlank(notAvailableReason)){
242
				logger.warn("Blank notAvailableReason has available: " + id);
243
			}
244
			NomenclaturalStatus nomStatus = getNomStatus(state, notAvailableReason, id);
245
			if (nomStatus != null){
246
				name.addStatus(nomStatus);
247
			}
248
		}else{
249
			if (isNotBlank(notAvailableReason)){
250
				logger.warn("Blank notAvailable has reason: " + id);
251
			}
252
			//Do nothing
253
		}
254

    
255

    
256
		//OLD
257
//				if (notAvailableStr.contains("not available") ){
258
//					UUID uuidNotAvailableMarkerType = state.getTransformer().getMarkerTypeUuid("not available");
259
//
260
//					MarkerType markerType = getMarkerType(state, uuidNotAvailableMarkerType, "not available", "not available", null);
261
//					name.addMarker(Marker.NewInstance(markerType, true));
262
//				}
263
//		//Not available reason
264
//		String notAvailableReason = rs.getString("SpecNotAvailableReason");
265
//		if (isNotBlank(notAvailableReason)){
266
//			UUID uuidNotAvailableReason;
267
//			try {
268
//				uuidNotAvailableReason = state.getTransformer().getExtensionTypeUuid("not available reason");
269
//				ExtensionType notAvailableReasonExtType = getExtensionType(state, uuidNotAvailableReason, "Not available reason", "Not available reason", null, null);
270
//				name.addExtension(notAvailableReason, notAvailableReasonExtType);
271
//			} catch (UndefinedTransformerMethodException e) {
272
//				e.printStackTrace();
273
//			}
274
//		}
275

    
276
	}
277

    
278

    
279

    
280

    
281

    
282
	private NomenclaturalStatus getNomStatus(GlobisImportState state, String notAvailableReason, int id) {
283
		NomenclaturalStatus status = NomenclaturalStatus.NewInstance(NomenclaturalStatusType.ZOO_NOT_AVAILABLE());
284
		status.setRuleConsidered(notAvailableReason);
285
		if (notAvailableReason.equalsIgnoreCase("hybrid")){
286
			logger.warn("Check hybrid for correctnes. Is there a better status?");
287
		}else if (notAvailableReason.equalsIgnoreCase("infrasubspecific name") || notAvailableReason.equalsIgnoreCase("infrasubspeciic name")){
288
			logger.warn("Check infrasubspecific name for correctnes. Is there a better status?");
289
		}else if (notAvailableReason.equalsIgnoreCase("by ruling of the Commission")){
290
			logger.warn("Check by ruling of the Commission for correctnes. Is there a better status?");
291
		}else{
292
			logger.warn("Unknown non available reason. ");
293
		}
294
		return status;
295
	}
296

    
297
	/**
298
	 * This method is not used anymore as according to Alexander Marker1 should be ignored.
299
	 * @param state
300
	 * @param rs
301
	 * @param name
302
	 * @throws SQLException
303
	 */
304
	@SuppressWarnings("unused")
305
	private void makeMarker1(GlobisImportState state, ResultSet rs, IZoologicalName name) throws SQLException {
306
		String marker1Str = rs.getString("Marker1");
307
		try {
308
			if (isNotBlank(marker1Str)){
309
				marker1Str = marker1Str.trim();
310
				if (marker1Str.contains("checked") || marker1Str.contains("berpr") ){ //überprüft
311
					UUID uuidCheckedMarkerType;
312
						uuidCheckedMarkerType = state.getTransformer().getMarkerTypeUuid("checked");
313

    
314
					MarkerType markerType = getMarkerType(state, uuidCheckedMarkerType, "checked", "checked", null);
315
					name.addMarker(Marker.NewInstance(markerType, true));
316
				}
317
				if (marker1Str.contains("old record") || marker1Str.contains("alte Angabe") ){
318
					UUID uuidOldRecordMarkerType = state.getTransformer().getMarkerTypeUuid("old record");
319
					MarkerType markerType = getMarkerType(state, uuidOldRecordMarkerType, "checked", "checked", null);
320
					name.addMarker(Marker.NewInstance(markerType, true));
321
				}
322
			}
323
		} catch (UndefinedTransformerMethodException e) {
324
			e.printStackTrace();
325
		}
326

    
327
	}
328

    
329

    
330
	private void addNameDescription(GlobisImportState state, IZoologicalName name, UUID featureUuid,
331
			String citedTypeLocality, String featureLabel) {
332
		Feature feature = getFeature(state, featureUuid,featureLabel,featureLabel, null, null);
333
		getTaxonNameDescription((TaxonName)name, false, true);
334

    
335
	}
336

    
337

    
338
	private Pattern patternAll = Pattern.compile("(.+,\\s.+)(\\(.+\\))");
339

    
340

    
341
	private void handleTypeInformation(GlobisImportState state, ResultSet rs, IZoologicalName name, Integer specTaxId) throws SQLException {
342
		if (! hasTypeInformation(rs)){
343
			return;
344
		}
345

    
346
		FieldUnit fieldObservation = makeTypeFieldObservation(state, rs);
347

    
348
		//typeDepository
349
		String specTypeDepositoriesStr = rs.getString("SpecTypeDepository");
350
		String[] specTypeDepositories;
351
		if (isNotBlank(specTypeDepositoriesStr) ){
352
			specTypeDepositories = specTypeDepositoriesStr.trim().split(";");
353
		}else{
354
			specTypeDepositories = new String[0];
355
		}
356

    
357
		//TODO several issues
358
		if (specTypeDepositories.length == 0){
359
			DerivedUnit specimen = makeSingleTypeSpecimen(fieldObservation);
360
			makeTypeDesignation(name, rs, specimen, specTaxId);
361
			makeTypeIdInSource(state, specimen, "null", specTaxId);
362
		}else{
363
			for (String specTypeDepositoryStr : specTypeDepositories){
364
				specTypeDepositoryStr = specTypeDepositoryStr.trim();
365

    
366
				//Specimen
367
				DerivedUnit specimen = makeSingleTypeSpecimen(fieldObservation);
368

    
369
				if (specTypeDepositoryStr.equals("??")){
370
					//unknown
371
					//TODO marker unknown ?
372
					specimen.setTitleCache("??", true);
373
					makeTypeIdInSource(state, specimen, "??", specTaxId);
374
				}else if (specTypeDepositoryStr.equals("[lost]")){
375
					//lost
376
					//TODO marker lost ?
377
					specimen.setTitleCache("lost", true);
378
					makeTypeIdInSource(state, specimen, "[lost]", specTaxId);
379
				}else{
380
					specTypeDepositoryStr = makeAdditionalSpecimenInformation(
381
							specTypeDepositoryStr, specimen, specTaxId);
382

    
383
					Collection collection = makeCollection(state, specTypeDepositoryStr, specimen, specTaxId);
384
					String collectionCode = collection.getCode();
385
					if (isBlank(collectionCode)){
386
						collectionCode = collection.getName();
387
					}
388
					if (isBlank(collectionCode)){
389
						logger.warn("Collection has empty representation: " + specTypeDepositoryStr + ", specTaxId" +  specTaxId);
390
					}
391
					makeTypeIdInSource(state, specimen, collectionCode , specTaxId);
392
				}
393

    
394
				//type Designation
395
				makeTypeDesignation(name, rs, specimen, specTaxId);
396
			}
397
		}
398

    
399

    
400
	}
401

    
402

    
403
	private void makeTypeIdInSource(GlobisImportState state, DerivedUnit specimen, String collectionCode, Integer specTaxId) {
404
		String namespace = TYPE_NAMESPACE;
405
		String id = getTypeId(specTaxId, collectionCode);
406
		IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, namespace, state.getTransactionalSourceReference(), null);
407
		specimen.addSource(source);
408
	}
409

    
410

    
411

    
412

    
413
	public static String getTypeId(Integer specTaxId, String collectionCode) {
414
		String result = String.valueOf(specTaxId) + "@" + collectionCode;
415
		return result;
416
	}
417

    
418

    
419

    
420

    
421
	private boolean hasTypeInformation(ResultSet rs) throws SQLException {
422
		String specTypeDepositoriesStr = rs.getString("SpecTypeDepository");
423
		String countryString = rs.getString("SpecTypeCountry");
424
		String specType = rs.getString("SpecType");
425
		boolean result = false;
426
		result |= isNotBlank(specTypeDepositoriesStr) || isNotBlank(countryString)
427
			|| isNotBlank(specType);
428
		return result;
429
	}
430

    
431

    
432

    
433
	/**
434
	 * @param state
435
	 * @param specTypeDepositoryStr
436
	 * @param specimen
437
	 * @param specTaxId
438
	 */
439
	protected Collection makeCollection(GlobisImportState state, String specTypeDepositoryStr, DerivedUnit specimen, Integer specTaxId) {
440

    
441
		//Collection
442
		specTypeDepositoryStr = specTypeDepositoryStr.replace("Washington, D.C.", "Washington@ D.C.")
443
				.replace("St.-Raymond, Quebec", "St.-Raymond@ Quebec")
444
				.replace("St.Petersburg", "St. Petersburg");
445

    
446

    
447
		Collection collection = handleSpecialCase(specTypeDepositoryStr, state, specimen);
448
		if (collection == null){
449
			String[] split = specTypeDepositoryStr.split(",");
450
			if (split.length != 2){
451
				if (split.length == 1 && (split[0].startsWith("coll.")|| split[0].startsWith("Coll.") )){
452
					collection = state.getRelatedObject(COLLECTION_NAMESPACE, split[0], Collection.class);
453
					if (collection == null){
454
						collection = Collection.NewInstance();
455
						collection.setName(split[0]);
456
						state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
457
					}
458
					specimen.setCollection(collection);
459
				}else{
460
					logger.warn("Split size in SpecTypeDepository is not 2: " + specTypeDepositoryStr + " (specTaxID:" + specTaxId + ")");
461
					collection = Collection.NewInstance();
462
					collection.setCode("??");
463
					//TODO deduplicate ??
464
				}
465

    
466
			}else{
467
				String collectionStr = split[0];
468
				String location = split[1].replace("@", ",").trim();
469

    
470
				collection = state.getRelatedObject(COLLECTION_NAMESPACE, collectionStr, Collection.class);
471
				if (collection == null){
472
					collection = Collection.NewInstance();
473
					if (collectionStr != null && collectionStr.startsWith("coll.")){
474
						collection.setName(collectionStr);
475
					}else{
476
						collection.setCode(collectionStr);
477
					}
478
					collection.setTownOrLocation(location);
479
					state.addRelatedObject(COLLECTION_NAMESPACE, collection.getCode(), collection);
480

    
481
				}else if (! CdmUtils.nullSafeEqual(location, collection.getTownOrLocation())){
482
					if (! normalizeTownOrLocation(location, collection)){
483
						String message = "Location (%s) is not equal to location (%s) of existing collection, specTaxId: " + specTaxId;
484
						logger.warn(String.format(message, location, collection.getTownOrLocation(), collection.getCode()));
485
					}
486

    
487
				}
488

    
489
				specimen.setCollection(collection);
490
			}
491
		}
492
		return collection;
493
	}
494

    
495

    
496
	private boolean normalizeTownOrLocation(String location, Collection collection) {
497
		boolean result = false;
498
		if (location != null){
499
			if ("coll. C. G. Treadaway".equals(collection.getName())){
500
				if (! "Frankfurt am Main".equals(collection.getTownOrLocation())){
501
					collection.setTownOrLocation("Frankfurt am Main");
502
					getCollectionService().saveOrUpdate(collection);
503
				}
504
				return true;
505
			}	else if ("coll. K. Rose".equals(collection.getName())){
506
				if (! "Mainz-Bretzenheim".equals(collection.getTownOrLocation())){
507
					collection.setTownOrLocation("Mainz-Bretzenheim");
508
					getCollectionService().saveOrUpdate(collection);
509
				}
510
				return true;
511
			} else if ("NMPN".equals(collection.getCode())){
512
				if (! "Prague".equals(collection.getTownOrLocation())){
513
					collection.setTownOrLocation("Prague");
514
					getCollectionService().saveOrUpdate(collection);
515
				}
516
				return true;
517
			} else if ("USNM".equals(collection.getCode())){
518
				if (! "Washington, D.C.".equals(collection.getTownOrLocation())){
519
					collection.setTownOrLocation("Washington, D.C.");
520
					getCollectionService().saveOrUpdate(collection);
521
				}
522
				return true;
523
			} else if ("coll. S. Kocman".equals(collection.getName())){
524
				if (! "Ostrava-Zabreh".equals(collection.getTownOrLocation())){
525
					collection.setTownOrLocation("Ostrava-Zabreh");
526
					getCollectionService().saveOrUpdate(collection);
527
				}
528
				return true;
529
			} else if ("ZSSM".equals(collection.getCode())){
530
				if (! "Munich".equals(collection.getTownOrLocation())){
531
					collection.setTownOrLocation("Munich");
532
					getCollectionService().saveOrUpdate(collection);
533
				}
534
				return true;
535
			} else if ("coll. R. H. Anken".equals(collection.getName())){
536
				if (! "Stuttgart".equals(collection.getTownOrLocation())){
537
					collection.setTownOrLocation("Stuttgart");
538
					getCollectionService().saveOrUpdate(collection);
539
				}
540
				return true;
541
			} else if ("coll. S.-I. Murayama".equals(collection.getName())){
542
				if (! "Aichi-Gakuin".equals(collection.getTownOrLocation())){
543
					collection.setTownOrLocation("Aichi-Gakuin");
544
					getCollectionService().saveOrUpdate(collection);
545
				}
546
				return true;
547
			} else if ("coll. Y. Sorimachi".equals(collection.getName())){
548
				if (! "Saitama".equals(collection.getTownOrLocation())){
549
					collection.setTownOrLocation("Saitama");
550
					getCollectionService().saveOrUpdate(collection);
551
				}
552
				return true;
553
			} else if ("coll. K. Yoshino".equals(collection.getName())){
554
				if (! "Saitama".equals(collection.getTownOrLocation())){
555
					collection.setTownOrLocation("Saitama");
556
					getCollectionService().saveOrUpdate(collection);
557
				}
558
				return true;
559
			} else if ("coll. U. Eitschberger".equals(collection.getName())){
560
				if (! "Marktleuthen".equals(collection.getTownOrLocation())){
561
					collection.setTownOrLocation("Marktleuthen");
562
					getCollectionService().saveOrUpdate(collection);
563
				}
564
				return true;
565
			} else if ("coll. G. Sala".equals(collection.getName())){
566
				if (! "Sal\u00f2".equals(collection.getTownOrLocation())){
567
					collection.setTownOrLocation("Sal\u00f2");  //LATIN SMALL LETTER O WITH GRAVE
568
					getCollectionService().saveOrUpdate(collection);
569
				}
570
				return true;
571
			} else if ("coll. Dantchenko".equals(collection.getName())){
572
				if (! "Moscow".equals(collection.getTownOrLocation())){
573
					collection.setTownOrLocation("Moscow");
574
					getCollectionService().saveOrUpdate(collection);
575
				}
576
				return true;
577
			} else if ("coll. S. Nakano".equals(collection.getName())){
578
				if (! "Tokyo".equals(collection.getTownOrLocation())){
579
					collection.setTownOrLocation("Tokyo");
580
					getCollectionService().saveOrUpdate(collection);
581
				}
582
				return true;
583
			} else if ("coll. A. Yagishita".equals(collection.getName())){
584
				if (! "Toride Ibaraki".equals(collection.getTownOrLocation())){
585
					collection.setTownOrLocation("Toride Ibaraki");
586
					getCollectionService().saveOrUpdate(collection);
587
				}
588
				return true;
589
			} else if ("coll. H. Sugiyama".equals(collection.getName())){
590
				if (! "Gifu".equals(collection.getTownOrLocation())){
591
					collection.setTownOrLocation("Gifu");
592
					getCollectionService().saveOrUpdate(collection);
593
				}
594
				return true;
595
			} else if ("MDMO".equals(collection.getCode())){
596
				if (! "Moscow".equals(collection.getTownOrLocation())){
597
					collection.setTownOrLocation("Moscow");
598
					getCollectionService().saveOrUpdate(collection);
599
				}
600
				return true;
601
			} else if ("coll. W. Eckweiler".equals(collection.getName())){
602
				if (! "Frankfurt am Main".equals(collection.getTownOrLocation())){
603
					collection.setTownOrLocation("Frankfurt am Main");
604
					getCollectionService().saveOrUpdate(collection);
605
				}
606
				return true;
607
			} else if ("coll. T. Frankenbach".equals(collection.getName())){
608
				if (! "Wangen".equals(collection.getTownOrLocation())){
609
					collection.setTownOrLocation("Wangen");
610
					getCollectionService().saveOrUpdate(collection);
611
				}
612
				return true;
613
			}
614
		}
615

    
616
		return result;
617
	}
618

    
619
	private Collection handleSpecialCase(String specTypeDepositoryStr, GlobisImportState state, DerivedUnit specimen) {
620
		Collection collection;
621
		if (specTypeDepositoryStr.equals("BMNH, London and/or MNHN, Paris")){
622
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
623
			if (collection == null){
624
				collection = Collection.NewInstance();
625
				collection.setName(specTypeDepositoryStr);
626
				collection.setTownOrLocation("London or Paris");
627
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
628
			}
629
			specimen.setCollection(collection);
630
		}else if (specTypeDepositoryStr.equals("ZISP, St. Petersburg, and/or BMNH, London ?")){
631
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
632
			if (collection == null){
633
				collection = Collection.NewInstance();
634
				collection.setName(specTypeDepositoryStr);
635
				collection.setTownOrLocation("St. Petersburg or London");
636
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
637
			}
638
			specimen.setCollection(collection);
639
		}else if (specTypeDepositoryStr.equals("MFNB, Berlin or SMTD, Dresden")){
640
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
641
			if (collection == null){
642
				collection = Collection.NewInstance();
643
				collection.setName(specTypeDepositoryStr);
644
				collection.setTownOrLocation("Berlin or Dresden");
645
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
646
			}
647
			specimen.setCollection(collection);
648
		}else if (specTypeDepositoryStr.equals("coll. S. Ianoka, Yamanashi, coll. A. Shinkai, Tokyo or coll. S. Morita")){
649
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
650
			if (collection == null){
651
				collection = Collection.NewInstance();
652
				collection.setName(specTypeDepositoryStr);
653
				collection.setTownOrLocation("Yamanashi or Tokyo or ?");
654
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
655
			}
656
			specimen.setCollection(collection);
657
		}else if (specTypeDepositoryStr.equals("coll. S. Inaoka, Yamanashi, coll. A. Shinkai, Tokyo, coll. H. Mikami, coll. S. Koiwaya, coll S. Morita or coll. Y. Nose")){
658
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
659
			if (collection == null){
660
				collection = Collection.NewInstance();
661
				collection.setName(specTypeDepositoryStr);
662
				collection.setTownOrLocation("Yamanashi or Tokyo or ?");
663
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
664
			}
665
			specimen.setCollection(collection);
666
		}else if (specTypeDepositoryStr.equals("S. Morita, Tokyo or coll. Y. Sorimachi, Saitama")){
667
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, specTypeDepositoryStr, Collection.class);
668
			if (collection == null){
669
				collection = Collection.NewInstance();
670
				collection.setName(specTypeDepositoryStr);
671
				collection.setTownOrLocation("Tokyo or Saitama");
672
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
673
			}
674
			specimen.setCollection(collection);
675

    
676
		}else if (specTypeDepositoryStr.equals("coll. L. V. Kaabak, A .V. Sotshivko & V. V. Titov, Moscow")){
677
			String colName = "coll. L. V. Kaabak, A .V. Sotshivko & V. V. Titov";
678
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, colName, Collection.class);
679
			if (collection == null){
680
				collection = Collection.NewInstance();
681
				collection.setName(colName);
682
				collection.setTownOrLocation("Moscow");
683
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
684
			}
685
			specimen.setCollection(collection);
686
		}else if (specTypeDepositoryStr.matches("coll. R. E. Parrott?, Port Hope, Ontario")){
687
			String colName = "coll. R. E. Parrott";
688
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, colName, Collection.class);
689
			if (collection == null){
690
				collection = Collection.NewInstance();
691
				collection.setName(colName);
692
				collection.setTownOrLocation("Port Hope, Ontario");
693
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
694
			}
695
			specimen.setCollection(collection);
696
		}else if (specTypeDepositoryStr.matches("coll. O. Slaby, Plzen, Tschechei")){
697
			String colName = "coll. O. Slaby";
698
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, colName, Collection.class);
699
			if (collection == null){
700
				collection = Collection.NewInstance();
701
				collection.setName(colName);
702
				collection.setTownOrLocation("Plzen, Tschechei");
703
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
704
			}
705
			specimen.setCollection(collection);
706
		}else if (specTypeDepositoryStr.matches("coll. K. Okubo")){
707
			String colName = "coll. K. Okubo";
708
			collection = state.getRelatedObject(COLLECTION_NAMESPACE, colName, Collection.class);
709
			if (collection == null){
710
				collection = Collection.NewInstance();
711
				collection.setName(colName);
712
				collection.setTownOrLocation("Nichinomiya city, Hyogo");
713
				state.addRelatedObject(COLLECTION_NAMESPACE, collection.getName(), collection);
714
			}
715
			specimen.setCollection(collection);
716
		}else{
717
			collection = null;
718
		}
719

    
720

    
721
		return collection;
722
	}
723

    
724
	/**
725
	 * @param specTypeDepositoriesStr
726
	 * @param specTypeDepositoryStr
727
	 * @param specimen
728
	 * @param specTaxId
729
	 * @return
730
	 */
731
	protected String makeAdditionalSpecimenInformation( String specTypeDepositoryStr, DerivedUnit specimen, Integer specTaxId) {
732
		//doubful
733
		if (specTypeDepositoryStr.endsWith("?")){
734
			Marker.NewInstance(specimen, true, MarkerType.IS_DOUBTFUL());
735
			specTypeDepositoryStr = specTypeDepositoryStr.substring(0, specTypeDepositoryStr.length() -1).trim();
736
		}
737

    
738
		//brackets
739
		Matcher matcher = patternAll.matcher(specTypeDepositoryStr);
740
		if (matcher.find()){
741
			//has brackets
742
			String brackets = matcher.group(2);
743
			brackets = brackets.substring(1, brackets.length()-1);
744

    
745
			//TODO this is unwanted according to Alexander
746
			brackets = brackets.replace("[mm]", "\u2642\u2642");
747
			brackets = brackets.replace("[m]", "\u2642");
748
			brackets = brackets.replace("[ff]", "\u2640\u2640");
749
			brackets = brackets.replace("[f]", "\u2640");
750
			brackets = brackets.replace("[m/f]", "\u26a5");
751

    
752
			if (brackets.contains("[") || brackets.contains("]")){
753
				logger.warn ("There are still '[', ']' in the bracket part: " + brackets + "; specTaxId: " + specTaxId);
754
			}
755

    
756
			//TODO replace mm/ff by Unicode male
757
			specimen.setTitleCache(brackets, true);
758
			specTypeDepositoryStr = matcher.group(1).trim();
759
		}
760
		return specTypeDepositoryStr;
761
	}
762

    
763
	protected DerivedUnit makeSingleTypeSpecimen(FieldUnit fieldObservation) {
764
		DerivationEvent derivEvent = DerivationEvent.NewInstance(DerivationEventType.ACCESSIONING());
765
//			derivEvent.setType(DerivationEventType.ACCESSIONING());
766
		fieldObservation.addDerivationEvent(derivEvent);
767
		DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
768
		specimen.setDerivedFrom(derivEvent);
769
		return specimen;
770
	}
771

    
772
	protected FieldUnit makeTypeFieldObservation(GlobisImportState state,
773
			ResultSet rs) throws SQLException {
774

    
775
		String countryString = rs.getString("SpecTypeCountry");
776

    
777
		SpecimenOrObservationType unitType = SpecimenOrObservationType.PreservedSpecimen;
778
		DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(unitType);
779

    
780
		NamedArea typeCountry = getCountry(state, countryString);
781
		facade.setCountry(typeCountry);
782
		FieldUnit fieldObservation = facade.innerFieldUnit();
783
		return fieldObservation;
784
	}
785

    
786

    
787
	protected void makeTypeDesignation(IZoologicalName name, ResultSet rs, DerivedUnit specimen, Integer specTaxId) throws SQLException {
788
		//type
789
		String specType = rs.getString("SpecType");
790
		SpecimenTypeDesignationStatus status = getTypeDesigType(specType, specTaxId);
791

    
792
		SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
793
		typeDesignation.setTypeStatus(status);
794
		typeDesignation.setTypeSpecimen(specimen);
795

    
796
		name.addTypeDesignation(typeDesignation, true);
797
	}
798

    
799
	private SpecimenTypeDesignationStatus getTypeDesigType(String specType, Integer specTaxId) {
800
		if (isBlank(specType) ){
801
			return null;
802
		}else if (specType.matches("Holotype(\r*\n*Holotypus)?")){
803
			return SpecimenTypeDesignationStatus.HOLOTYPE();
804
		}else if (specType.matches("Neotype")){
805
			return SpecimenTypeDesignationStatus.NEOTYPE();
806
		}else if (specType.matches("Syntype(\\(s\\))?") || specType.matches("Syntype.*Syntype\\(s\\)\\s*") ){
807
			return SpecimenTypeDesignationStatus.SYNTYPE();
808
		}else if (specType.matches("Syntype\r*\n*Syntype.*") ){
809
			return SpecimenTypeDesignationStatus.SYNTYPE();
810
		}else if (specType.matches("Lectotype")){
811
			return SpecimenTypeDesignationStatus.LECTOTYPE();
812
		}else{
813
			logger.warn("SpecimenTypeDesignationStatus does not match: " + specType + " in specTaxId "  + specTaxId);
814
			return null;
815
		}
816
	}
817

    
818
	private Reference handleNomRef(GlobisImportState state, Map<String, Reference> referenceMap, ResultSet rs,
819
			IZoologicalName name, Integer specTaxId) throws SQLException {
820
		//ref
821
		Integer refId = nullSafeInt(rs, "fiSpecRefID");
822
		Reference nomRef = null;
823
		if (refId != null){
824
			nomRef = referenceMap.get(String.valueOf(refId));
825
			if (nomRef == null && state.getConfig().getDoReferences().equals(state.getConfig().getDoReferences().ALL)){
826
				logger.warn("Reference " + refId + " could not be found. SpecTaxId: " + specTaxId);
827
			}else if (nomRef != null){
828
				name.setNomenclaturalReference(nomRef);
829
			}
830
		}
831

    
832
		//refDetail
833
		String refDetail = rs.getString("SpecPage");
834
		if (isNotBlank(refDetail)){
835
			name.setNomenclaturalMicroReference(refDetail);
836
		}
837
		return nomRef;
838
	}
839

    
840
	private void validateAcceptedTaxon(Taxon acceptedTaxon, ResultSet rs, Integer specTaxId, Integer acceptedTaxonId) throws SQLException {
841
		if (acceptedTaxon == null){
842
			logger.warn("Accepted taxon is null for taxon taxon to validate. SpecTaxId " + specTaxId + ", accTaxonId: " + acceptedTaxonId);
843
			return;
844
		}
845

    
846
		//TODO
847
		IZoologicalName name = acceptedTaxon.getName();
848

    
849
		String specName = rs.getString("SpecName");
850
		if (! name.getSpecificEpithet().equals(specName)){
851
			logger.warn(String.format("Species epithet is not equal for accepted taxon: %s - %s. SpecTaxId: %d", name.getSpecificEpithet(), specName, specTaxId));
852
		}
853
		//TODO
854
	}
855

    
856
	private Synonym getSynonym(GlobisImportState state, ResultSet rs, Integer specTaxId) throws SQLException {
857
		TaxonName name = (TaxonName)makeName(state, rs, specTaxId);
858

    
859
		Synonym synonym = Synonym.NewInstance(name, state.getTransactionalSourceReference());
860

    
861
		return synonym;
862
	}
863

    
864
	/**
865
	 * @param state
866
	 * @param rs
867
	 * @param specTaxId
868
	 * @return
869
	 * @throws SQLException
870
	 */
871
	protected IZoologicalName makeName(GlobisImportState state, ResultSet rs, Integer specTaxId)
872
			throws SQLException {
873
		//rank
874
		String rankStr = rs.getString("SpecRank");
875
		Rank rank = null;
876
		if (isNotBlank(rankStr)){
877
			try {
878
				rank = Rank.getRankByLatinNameOrIdInVoc(rankStr, NomenclaturalCode.ICZN, true);
879
			} catch (UnknownCdmTypeException e) {
880
				e.printStackTrace();
881
			}
882
		}
883

    
884
		//name
885
		IZoologicalName name = TaxonNameFactory.NewZoologicalInstance(rank);
886
		makeNamePartsAndCache(state, rs, rankStr, name);
887

    
888

    
889
//		name.setGenusOrUninomial(genusOrUninomial);
890
		String authorStr = rs.getString("SpecAuthor");
891
		String yearStr = rs.getString("SpecYear");
892
		String authorAndYearStr = CdmUtils.concat(", ", authorStr, yearStr);
893
		handleAuthorAndYear(authorAndYearStr, name, specTaxId, state);
894

    
895
		return name;
896
	}
897

    
898
	private void makeNamePartsAndCache(GlobisImportState state, ResultSet rs, String rank, IZoologicalName name) throws SQLException {
899
		String citedFamily = rs.getString("SpecCitedFamily");
900
		String citedGenus = rs.getString("SpecCitedGenus");
901
		String citedSpecies = rs.getString("SpecCitedSpecies");
902
		String citedSubspecies = rs.getString("SpecCitedSubspecies");
903
		String lastEpithet = rs.getString("SpecName");
904

    
905

    
906
		String cache = CdmUtils.concat(" ", new String[]{citedFamily, citedGenus, citedSpecies, citedSubspecies, rank, lastEpithet});
907
		name.setGenusOrUninomial(citedGenus);
908
		//TODO separate authors
909
		if (isBlank(citedSpecies)){
910
			name.setSpecificEpithet(lastEpithet);
911
		}else{
912
			name.setSpecificEpithet(citedSpecies);
913
			if (isBlank(citedSubspecies)){
914
				name.setInfraSpecificEpithet(lastEpithet);
915
			}
916
		}
917

    
918
		//TODO check if cache needs protection
919
		name.setNameCache(cache, true);
920
	}
921

    
922
	@Override
923
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, GlobisImportState state) {
924

    
925
	    String nameSpace;
926
		Set<String> idSet;
927

    
928
		@SuppressWarnings("rawtypes")
929
        Set<AgentBase> agents = state.getAgents();
930
		getAgentService().saveOrUpdate(agents);
931

    
932
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<>();
933
		try{
934
			Set<String> taxonIdSet = new HashSet<>();
935
			Set<String> referenceIdSet = new HashSet<>();
936

    
937
			while (rs.next()){
938
				handleForeignKey(rs, taxonIdSet, "SpecCurrspecID");
939
				handleForeignKey(rs, referenceIdSet, "fiSpecRefID");
940
			}
941

    
942
			//taxon map
943
			nameSpace = TAXON_NAMESPACE;
944
			idSet = taxonIdSet;
945
			Map<String, Taxon> objectMap = getCommonService().getSourcedObjectsByIdInSourceC(Taxon.class, idSet, nameSpace);
946
			result.put(nameSpace, objectMap);
947

    
948
			//reference map
949
			nameSpace = REFERENCE_NAMESPACE;
950
			idSet = referenceIdSet;
951
			Map<String, Reference> referenceMap = getCommonService().getSourcedObjectsByIdInSourceC(Reference.class, idSet, nameSpace);
952
			result.put(nameSpace, referenceMap);
953

    
954
			//collection map
955
			nameSpace = COLLECTION_NAMESPACE;
956
			List<Collection> listCollection = getCollectionService().list(Collection.class, null, null, null, null);
957
			Map<String, Collection> collectionMap = new HashMap<>();
958
			for (Collection collection : listCollection){
959
				if (isNotBlank(collection.getCode())){
960
					collectionMap.put(collection.getCode(), collection);
961
				}else if (isNotBlank(collection.getName())){
962
					collectionMap.put(collection.getName(), collection);
963
				}else{
964
					logger.warn("Collection code and name are blank: " + collection);
965
				}
966
			}
967
			result.put(nameSpace, collectionMap);
968

    
969
		} catch (SQLException e) {
970
			throw new RuntimeException(e);
971
		}
972
		return result;
973
	}
974

    
975
	@Override
976
	protected boolean doCheck(GlobisImportState state){
977
		IOValidator<GlobisImportState> validator = new GlobisSpecTaxaImportValidator();
978
		return validator.validate(state);
979
	}
980

    
981
	@Override
982
    protected boolean isIgnore(GlobisImportState state){
983
		return ! state.getConfig().isDoSpecTaxa();
984
	}
985

    
986
	@Override
987
	public Reference createObject(ResultSet rs, GlobisImportState state)
988
			throws SQLException {
989
		// not needed
990
		return null;
991
	}
992

    
993
}
(9-9/10)