Project

General

Profile

Download (38.7 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

    
10
package eu.etaxonomy.cdm.io.globis;
11

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

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

    
26
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
27
import eu.etaxonomy.cdm.common.CdmUtils;
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.common.OriginalSourceType;
40
import eu.etaxonomy.cdm.model.description.Feature;
41
import eu.etaxonomy.cdm.model.location.NamedArea;
42
import eu.etaxonomy.cdm.model.name.IZoologicalName;
43
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
44
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
45
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
46
import eu.etaxonomy.cdm.model.name.Rank;
47
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
48
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
49
import eu.etaxonomy.cdm.model.name.TaxonName;
50
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
51
import eu.etaxonomy.cdm.model.occurrence.Collection;
52
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
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.Reference;
57
import eu.etaxonomy.cdm.model.taxon.Synonym;
58
import eu.etaxonomy.cdm.model.taxon.SynonymType;
59
import eu.etaxonomy.cdm.model.taxon.Taxon;
60
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
62

    
63

    
64
/**
65
 * @author a.mueller
66
 * @created 20.02.2010
67
 */
68
@Component
69
public class GlobisSpecTaxImport  extends GlobisImportBase<Reference> implements IMappingImport<Reference, GlobisImportState>{
70
	private static final Logger logger = Logger.getLogger(GlobisSpecTaxImport.class);
71

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

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

    
80
	public GlobisSpecTaxImport(){
81
		super(pluralString, dbTableName, cdmTargetClass);
82
	}
83

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

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

    
102

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

    
107
		Set<TaxonBase> objectsToSave = new HashSet<>();
108
		Set<TaxonName> namesToSave = new HashSet<>();
109

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

    
113
		ResultSet rs = partitioner.getResultSet();
114

    
115
		try {
116

    
117
			int i = 0;
118

    
119
			//for each reference
120
            while (rs.next()){
121

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

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

    
128
        		//ignore: CountryDummy, currentSpecies, DepositoryDisplay, DepositoryDummy, ReferenceDisplay,
129
        		//        SpecDescriptionImageFile, all *Valid*
130

    
131
				try {
132

    
133
					//source ref
134
					Reference sourceRef = state.getTransactionalSourceReference();
135

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

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

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

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

    
176
					handleNomRef(state, referenceMap, rs, name, specTaxId);
177

    
178
					handleTypeInformation(state,rs, name, specTaxId);
179

    
180

    
181
//					this.doIdCreatedUpdatedNotes(state, ref, rs, refId, REFERENCE_NAMESPACE);
182

    
183
					if (acceptedTaxon != null){
184
						objectsToSave.add(acceptedTaxon);
185
					}
186

    
187
					//makeMarker1(state, rs, name);   //ignore!
188

    
189
					//make not available
190
					makeNotAvailable(state, rs, name, specTaxId);
191

    
192
					//maken invalid
193
					//TODO
194

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

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

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

    
212
					namesToSave.add(TaxonName.castAndDeproxy(name));
213

    
214

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

    
220
            }
221

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

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

    
233

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

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

    
253

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

    
274
	}
275

    
276

    
277

    
278

    
279

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

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

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

    
325
	}
326

    
327

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

    
333
	}
334

    
335

    
336
	private Pattern patternAll = Pattern.compile("(.+,\\s.+)(\\(.+\\))");
337

    
338

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

    
344
		FieldUnit fieldObservation = makeTypeFieldObservation(state, rs);
345

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

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

    
364
				//Specimen
365
				DerivedUnit specimen = makeSingleTypeSpecimen(fieldObservation);
366

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

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

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

    
397

    
398
	}
399

    
400

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

    
408

    
409

    
410

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

    
416

    
417

    
418

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

    
429

    
430

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

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

    
444

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

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

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

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

    
485
				}
486

    
487
				specimen.setCollection(collection);
488
			}
489
		}
490
		return collection;
491
	}
492

    
493

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

    
614
		return result;
615
	}
616

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

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

    
718

    
719
		return collection;
720
	}
721

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

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

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

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

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

    
761

    
762

    
763

    
764
	/**
765
	 * @param fieldObservation
766
	 * @return
767
	 */
768
	protected DerivedUnit makeSingleTypeSpecimen(FieldUnit fieldObservation) {
769
		DerivationEvent derivEvent = DerivationEvent.NewInstance();
770
//			derivEvent.setType(DerivationEventType.ACCESSIONING());
771
		fieldObservation.addDerivationEvent(derivEvent);
772
		DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
773
		specimen.setDerivedFrom(derivEvent);
774
		return specimen;
775
	}
776

    
777

    
778

    
779

    
780
	/**
781
	 * @param state
782
	 * @return
783
	 * @throws SQLException
784
	 */
785
	protected FieldUnit makeTypeFieldObservation(GlobisImportState state,
786
			ResultSet rs) throws SQLException {
787

    
788
		String countryString = rs.getString("SpecTypeCountry");
789

    
790
		SpecimenOrObservationType unitType = SpecimenOrObservationType.PreservedSpecimen;
791
		DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(unitType);
792

    
793
		NamedArea typeCountry = getCountry(state, countryString);
794
		facade.setCountry(typeCountry);
795
		FieldUnit fieldObservation = facade.innerFieldUnit();
796
		return fieldObservation;
797
	}
798

    
799

    
800

    
801

    
802
	/**
803
	 * @param name
804
	 * @param rs
805
	 * @param status
806
	 * @param specimen
807
	 * @param specTaxId
808
	 * @throws SQLException
809
	 */
810
	protected void makeTypeDesignation(IZoologicalName name, ResultSet rs, DerivedUnit specimen, Integer specTaxId) throws SQLException {
811
		//type
812
		String specType = rs.getString("SpecType");
813
		SpecimenTypeDesignationStatus status = getTypeDesigType(specType, specTaxId);
814

    
815
		SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
816
		typeDesignation.setTypeStatus(status);
817
		typeDesignation.setTypeSpecimen(specimen);
818

    
819
		name.addTypeDesignation(typeDesignation, true);
820
	}
821

    
822

    
823

    
824

    
825
	private SpecimenTypeDesignationStatus getTypeDesigType(String specType, Integer specTaxId) {
826
		if (isBlank(specType) ){
827
			return null;
828
		}else if (specType.matches("Holotype(\r*\n*Holotypus)?")){
829
			return SpecimenTypeDesignationStatus.HOLOTYPE();
830
		}else if (specType.matches("Neotype")){
831
			return SpecimenTypeDesignationStatus.NEOTYPE();
832
		}else if (specType.matches("Syntype(\\(s\\))?") || specType.matches("Syntype.*Syntype\\(s\\)\\s*") ){
833
			return SpecimenTypeDesignationStatus.SYNTYPE();
834
		}else if (specType.matches("Syntype\r*\n*Syntype.*") ){
835
			return SpecimenTypeDesignationStatus.SYNTYPE();
836
		}else if (specType.matches("Lectotype")){
837
			return SpecimenTypeDesignationStatus.LECTOTYPE();
838
		}else{
839
			logger.warn("SpecimenTypeDesignationStatus does not match: " + specType + " in specTaxId "  + specTaxId);
840
			return null;
841
		}
842
	}
843

    
844

    
845

    
846

    
847
	/**
848
	 * @param state
849
	 * @param referenceMap
850
	 * @param rs
851
	 * @param name
852
	 * @param specTaxId
853
	 * @return
854
	 * @throws SQLException
855
	 */
856
	private Reference handleNomRef(GlobisImportState state, Map<String, Reference> referenceMap, ResultSet rs,
857
			IZoologicalName name, Integer specTaxId) throws SQLException {
858
		//ref
859
		Integer refId = nullSafeInt(rs, "fiSpecRefID");
860
		Reference nomRef = null;
861
		if (refId != null){
862
			nomRef = referenceMap.get(String.valueOf(refId));
863
			if (nomRef == null && state.getConfig().getDoReferences().equals(state.getConfig().getDoReferences().ALL)){
864
				logger.warn("Reference " + refId + " could not be found. SpecTaxId: " + specTaxId);
865
			}else if (nomRef != null){
866
				name.setNomenclaturalReference(nomRef);
867
			}
868
		}
869

    
870
		//refDetail
871
		String refDetail = rs.getString("SpecPage");
872
		if (isNotBlank(refDetail)){
873
			name.setNomenclaturalMicroReference(refDetail);
874
		}
875
		return nomRef;
876
	}
877

    
878

    
879

    
880

    
881
	private void validateAcceptedTaxon(Taxon acceptedTaxon, ResultSet rs, Integer specTaxId, Integer acceptedTaxonId) throws SQLException {
882
		if (acceptedTaxon == null){
883
			logger.warn("Accepted taxon is null for taxon taxon to validate. SpecTaxId " + specTaxId + ", accTaxonId: " + acceptedTaxonId);
884
			return;
885
		}
886

    
887
		//TODO
888
		IZoologicalName name = acceptedTaxon.getName();
889

    
890
		String specName = rs.getString("SpecName");
891
		if (! name.getSpecificEpithet().equals(specName)){
892
			logger.warn(String.format("Species epithet is not equal for accepted taxon: %s - %s. SpecTaxId: %d", name.getSpecificEpithet(), specName, specTaxId));
893
		}
894
		//TODO
895
	}
896

    
897

    
898

    
899

    
900
	private Synonym getSynonym(GlobisImportState state, ResultSet rs, Integer specTaxId) throws SQLException {
901
		TaxonName<?,?> name = (TaxonName<?,?>)makeName(state, rs, specTaxId);
902

    
903
		Synonym synonym = Synonym.NewInstance(name, state.getTransactionalSourceReference());
904

    
905
		return synonym;
906
	}
907

    
908

    
909

    
910

    
911
	/**
912
	 * @param state
913
	 * @param rs
914
	 * @param specTaxId
915
	 * @return
916
	 * @throws SQLException
917
	 */
918
	protected IZoologicalName makeName(GlobisImportState state, ResultSet rs, Integer specTaxId)
919
			throws SQLException {
920
		//rank
921
		String rankStr = rs.getString("SpecRank");
922
		Rank rank = null;
923
		if (isNotBlank(rankStr)){
924
			try {
925
				rank = Rank.getRankByNameOrIdInVoc(rankStr, NomenclaturalCode.ICZN, true);
926
			} catch (UnknownCdmTypeException e) {
927
				e.printStackTrace();
928
			}
929
		}
930

    
931
		//name
932
		IZoologicalName name = TaxonNameFactory.NewZoologicalInstance(rank);
933
		makeNamePartsAndCache(state, rs, rankStr, name);
934

    
935

    
936
//		name.setGenusOrUninomial(genusOrUninomial);
937
		String authorStr = rs.getString("SpecAuthor");
938
		String yearStr = rs.getString("SpecYear");
939
		String authorAndYearStr = CdmUtils.concat(", ", authorStr, yearStr);
940
		handleAuthorAndYear(authorAndYearStr, name, specTaxId, state);
941

    
942
		return name;
943
	}
944

    
945

    
946

    
947

    
948
	private void makeNamePartsAndCache(GlobisImportState state, ResultSet rs, String rank, IZoologicalName name) throws SQLException {
949
		String citedFamily = rs.getString("SpecCitedFamily");
950
		String citedGenus = rs.getString("SpecCitedGenus");
951
		String citedSpecies = rs.getString("SpecCitedSpecies");
952
		String citedSubspecies = rs.getString("SpecCitedSubspecies");
953
		String lastEpithet = rs.getString("SpecName");
954

    
955

    
956
		String cache = CdmUtils.concat(" ", new String[]{citedFamily, citedGenus, citedSpecies, citedSubspecies, rank, lastEpithet});
957
		name.setGenusOrUninomial(citedGenus);
958
		//TODO separate authors
959
		if (isBlank(citedSpecies)){
960
			name.setSpecificEpithet(lastEpithet);
961
		}else{
962
			name.setSpecificEpithet(citedSpecies);
963
			if (isBlank(citedSubspecies)){
964
				name.setInfraSpecificEpithet(lastEpithet);
965
			}
966
		}
967

    
968
		//TODO check if cache needs protection
969
		name.setNameCache(cache, true);
970
	}
971

    
972
	@Override
973
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, GlobisImportState state) {
974
		String nameSpace;
975
		Class<?> cdmClass;
976
		Set<String> idSet;
977

    
978
		Set<AgentBase> agents = state.getAgents();
979
		getAgentService().saveOrUpdate(agents);
980

    
981
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
982
		try{
983
			Set<String> taxonIdSet = new HashSet<String>();
984
			Set<String> referenceIdSet = new HashSet<String>();
985

    
986
			while (rs.next()){
987
				handleForeignKey(rs, taxonIdSet, "SpecCurrspecID");
988
				handleForeignKey(rs, referenceIdSet, "fiSpecRefID");
989
			}
990

    
991
			//taxon map
992
			nameSpace = TAXON_NAMESPACE;
993
			cdmClass = Taxon.class;
994
			idSet = taxonIdSet;
995
			Map<String, Taxon> objectMap = (Map<String, Taxon>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
996
			result.put(nameSpace, objectMap);
997

    
998
			//reference map
999
			nameSpace = REFERENCE_NAMESPACE;
1000
			cdmClass = Reference.class;
1001
			idSet = referenceIdSet;
1002
			Map<String, Reference> referenceMap = (Map<String, Reference>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, idSet, nameSpace);
1003
			result.put(nameSpace, referenceMap);
1004

    
1005
			//collection map
1006
			nameSpace = COLLECTION_NAMESPACE;
1007
			List<Collection> listCollection = getCollectionService().list(Collection.class, null, null, null, null);
1008
			Map<String, Collection> collectionMap = new HashMap<String, Collection>();
1009
			for (Collection collection : listCollection){
1010
				if (isNotBlank(collection.getCode())){
1011
					collectionMap.put(collection.getCode(), collection);
1012
				}else if (isNotBlank(collection.getName())){
1013
					collectionMap.put(collection.getName(), collection);
1014
				}else{
1015
					logger.warn("Collection code and name are blank: " + collection);
1016
				}
1017
			}
1018
			result.put(nameSpace, collectionMap);
1019

    
1020
		} catch (SQLException e) {
1021
			throw new RuntimeException(e);
1022
		}
1023
		return result;
1024
	}
1025

    
1026
	/* (non-Javadoc)
1027
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IImportConfigurator)
1028
	 */
1029
	@Override
1030
	protected boolean doCheck(GlobisImportState state){
1031
		IOValidator<GlobisImportState> validator = new GlobisSpecTaxaImportValidator();
1032
		return validator.validate(state);
1033
	}
1034

    
1035

    
1036
	/* (non-Javadoc)
1037
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IImportConfigurator)
1038
	 */
1039
	@Override
1040
    protected boolean isIgnore(GlobisImportState state){
1041
		return ! state.getConfig().isDoSpecTaxa();
1042
	}
1043

    
1044

    
1045

    
1046

    
1047
	@Override
1048
	public Reference createObject(ResultSet rs, GlobisImportState state)
1049
			throws SQLException {
1050
		// not needed
1051
		return null;
1052
	}
1053

    
1054
}
(9-9/10)