Project

General

Profile

Download (63.1 KB) Statistics
| Branch: | Tag: | 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.common;
11

    
12
import java.net.MalformedURLException;
13
import java.net.URISyntaxException;
14
import java.sql.ResultSet;
15
import java.sql.SQLException;
16
import java.util.ArrayList;
17
import java.util.Arrays;
18
import java.util.List;
19
import java.util.Set;
20
import java.util.UUID;
21

    
22
import org.apache.logging.log4j.LogManager;
23
import org.apache.logging.log4j.Logger;
24

    
25
import eu.etaxonomy.cdm.api.application.ICdmRepository;
26
import eu.etaxonomy.cdm.api.service.pager.Pager;
27
import eu.etaxonomy.cdm.common.URI;
28
import eu.etaxonomy.cdm.common.media.CdmImageInfo;
29
import eu.etaxonomy.cdm.io.common.mapping.IInputTransformer;
30
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
31
import eu.etaxonomy.cdm.io.common.utils.ImportDeduplicationHelper;
32
import eu.etaxonomy.cdm.io.markup.MarkupTransformer;
33
import eu.etaxonomy.cdm.model.agent.Person;
34
import eu.etaxonomy.cdm.model.agent.Team;
35
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
36
import eu.etaxonomy.cdm.model.common.AnnotationType;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.ExtensionType;
39
import eu.etaxonomy.cdm.model.common.ICdmBase;
40
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
41
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
42
import eu.etaxonomy.cdm.model.common.Language;
43
import eu.etaxonomy.cdm.model.common.Marker;
44
import eu.etaxonomy.cdm.model.common.MarkerType;
45
import eu.etaxonomy.cdm.model.description.DescriptionBase;
46
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
47
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
48
import eu.etaxonomy.cdm.model.description.Feature;
49
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
50
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
51
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
52
import eu.etaxonomy.cdm.model.description.State;
53
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
54
import eu.etaxonomy.cdm.model.description.TaxonDescription;
55
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
56
import eu.etaxonomy.cdm.model.description.TextData;
57
import eu.etaxonomy.cdm.model.location.Country;
58
import eu.etaxonomy.cdm.model.location.NamedArea;
59
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
60
import eu.etaxonomy.cdm.model.location.NamedAreaType;
61
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
62
import eu.etaxonomy.cdm.model.media.ImageFile;
63
import eu.etaxonomy.cdm.model.media.Media;
64
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
65
import eu.etaxonomy.cdm.model.name.HybridRelationship;
66
import eu.etaxonomy.cdm.model.name.INonViralName;
67
import eu.etaxonomy.cdm.model.name.NameRelationship;
68
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
69
import eu.etaxonomy.cdm.model.name.Rank;
70
import eu.etaxonomy.cdm.model.name.RankClass;
71
import eu.etaxonomy.cdm.model.name.TaxonName;
72
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
73
import eu.etaxonomy.cdm.model.reference.ISourceable;
74
import eu.etaxonomy.cdm.model.reference.OriginalSourceBase;
75
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
76
import eu.etaxonomy.cdm.model.reference.Reference;
77
import eu.etaxonomy.cdm.model.taxon.Classification;
78
import eu.etaxonomy.cdm.model.taxon.Synonym;
79
import eu.etaxonomy.cdm.model.taxon.Taxon;
80
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
81
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
82
import eu.etaxonomy.cdm.model.term.DefinedTerm;
83
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
84
import eu.etaxonomy.cdm.model.term.OrderedTermVocabulary;
85
import eu.etaxonomy.cdm.model.term.Representation;
86
import eu.etaxonomy.cdm.model.term.TermType;
87
import eu.etaxonomy.cdm.model.term.TermVocabulary;
88

    
89
/**
90
 * @author a.mueller
91
 * @since 01.07.2008
92
 */
93
public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE extends ImportStateBase>
94
            extends CdmIoBase<STATE, ImportResult>
95
            implements ICdmImport<CONFIG, STATE>{
96

    
97
    private static final long serialVersionUID = 8730012744209195616L;
98
    private static final Logger logger = LogManager.getLogger(CdmImportBase.class);
99

    
100
	protected static final boolean CREATE = true;
101
	protected static final boolean IMAGE_GALLERY = true;
102
	protected static final boolean READ_MEDIA_DATA = true;
103

    
104
	public static final UUID uuidUserDefinedNamedAreaLevelVocabulary = UUID.fromString("255144da-8d95-457e-a327-9752a8f85e5a");
105
	public static final UUID uuidUserDefinedNamedAreaVocabulary = UUID.fromString("b2238399-a3af-4f6d-b7eb-ff5d0899bf1b");
106
	public static final UUID uuidUserDefinedExtensionTypeVocabulary = UUID.fromString("e28c1394-1be8-4847-8b81-ab44eb6d5bc8");
107
	public static final UUID uuidUserDefinedIdentifierTypeVocabulary = UUID.fromString("194b173b-e2c8-49f1-bbfa-d5d51556cf68");
108
	public static final UUID uuidUserDefinedReferenceSystemVocabulary = UUID.fromString("467591a3-10b4-4bf1-9239-f06ece33e90a");
109
	public static final UUID uuidUserDefinedFeatureVocabulary = UUID.fromString("fe5fccb3-a2f2-4b97-b199-6e2743cf1627");
110
	public static final UUID uuidUserDefinedMeasurementUnitVocabulary = UUID.fromString("d5e72bb7-f312-4080-bb86-c695d04a6e66");
111
	public static final UUID uuidUserDefinedStatisticalMeasureVocabulary = UUID.fromString("62a89836-c730-4b4f-a904-3d859dbfc400");
112
	public static final UUID uuidUserDefinedStateVocabulary = UUID.fromString("f7cddb49-8392-4db1-8640-65b48a0e6d13");
113
	public static final UUID uuidUserDefinedTaxonRelationshipTypeVocabulary = UUID.fromString("31a324dc-408d-4877-891f-098db21744c6");
114
	public static final UUID uuidUserDefinedAnnotationTypeVocabulary = UUID.fromString("cd9ecdd2-9cae-4890-9032-ad83293ae883");
115
	public static final UUID uuidUserDefinedMarkerTypeVocabulary = UUID.fromString("5f02a261-fd7d-4fce-bbe4-21472de8cd51");
116
	public static final UUID uuidUserDefinedRankVocabulary = UUID.fromString("4dc57931-38e2-46c3-974d-413b087646ba");
117
	public static final UUID uuidUserDefinedPresenceAbsenceVocabulary = UUID.fromString("6b8a2581-1471-4ea6-b8ad-b2d931cfbc23");
118
	public static final UUID uuidUserDefinedModifierVocabulary = UUID.fromString("2a8b3838-3a95-49ea-9ab2-3049614b5884");
119
	public static final UUID uuidUserDefinedKindOfUnitVocabulary = UUID.fromString("e7c5deb2-f485-4a66-9104-0c5398efd481");
120
	public static final UUID uuidUserDefinedLanguageVocabulary = UUID.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
121
	public static final UUID uuidUserDefinedNomenclaturalStatusTypeVocabulary = UUID.fromString("1a5c7745-5588-4151-bc43-9ca22561977b");
122

    
123

    
124
	private static final String UuidOnly = "UUIDOnly";
125
	private static final String UuidLabel = "UUID or label";
126
	private static final String UuidLabelAbbrev = "UUID, label or abbreviation";
127
	private static final String UuidAbbrev = "UUID or abbreviation";
128

    
129
	private final static String authorSeparator = ", ";
130
    private final static String lastAuthorSeparator = " & ";
131

    
132
    public enum TermMatchMode{
133
		UUID_ONLY(0, UuidOnly)
134
		,UUID_LABEL(1, UuidLabel)
135
		,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev)
136
		,UUID_ABBREVLABEL(3, UuidAbbrev)
137
		;
138

    
139
		private final int id;
140
		private final String representation;
141
		private TermMatchMode(int id, String representation){
142
			this.id = id;
143
			this.representation = representation;
144
		}
145
		public int getId() {
146
			return id;
147
		}
148
		public String getRepresentation() {
149
			return representation;
150
		}
151
		public TermMatchMode valueOf(int id){
152
			switch (id){
153
				case 0: return UUID_ONLY;
154
				case 1: return UUID_LABEL;
155
				case 2: return UUID_LABEL_ABBREVLABEL;
156
				case 3: return UUID_ABBREVLABEL;
157
				default: return UUID_ONLY;
158
			}
159
 		}
160
	}
161

    
162
    protected ICdmRepository repository;
163

    
164
    @Override
165
    public void setRepository(ICdmRepository repository){
166
        this.repository = repository;
167
    }
168

    
169
    @Override
170
    protected ImportResult getNoDataResult(STATE state) {
171
        return ImportResult.NewNoDataInstance();
172
    }
173

    
174
    @Override
175
    protected ImportResult getDefaultResult(STATE state) {
176
        return ImportResult.NewInstance();
177
    }
178

    
179
	protected Classification makeTree(STATE state, Reference reference){
180
		String treeName = "Classification (Import)";
181
		if (reference != null && isNotBlank(reference.getTitleCache())){
182
			treeName = reference.getTitleCache();
183
		}
184
		Classification tree = Classification.NewInstance(treeName);
185
		tree.setReference(reference);
186

    
187
		// use defined uuid for first tree
188
		CONFIG config = (CONFIG)state.getConfig();
189
		if (state.countTrees() < 1 ){
190
			tree.setUuid(config.getClassificationUuid());
191
		}
192
		getClassificationService().save(tree);
193
		state.putTree(reference, tree);
194
		return tree;
195
	}
196

    
197

    
198
	/**
199
	 * Alternative memory saving method variant of
200
	 * {@link #makeTree(STATE state, Reference ref)} which stores only the
201
	 * UUID instead of the full tree in the <code>ImportStateBase</code> by
202
	 * using <code>state.putTreeUuid(ref, tree);</code>
203
	 *
204
	 * @param state
205
	 * @param ref
206
	 * @return
207
	 */
208
	protected Classification makeTreeMemSave(STATE state, Reference ref){
209
		String treeName = "Classification (Import)";
210
		if (ref != null && isNotBlank(ref.getTitleCache())){
211
			treeName = ref.getTitleCache();
212
		}
213
		Classification tree = Classification.NewInstance(treeName);
214
		tree.setReference(ref);
215

    
216

    
217
		// use defined uuid for first tree
218
		CONFIG config = (CONFIG)state.getConfig();
219
		if (state.countTrees() < 1 ){
220
			tree.setUuid(config.getClassificationUuid());
221
		}
222
		getClassificationService().save(tree);
223
		state.putTreeUuid(ref, tree);
224
		return tree;
225
	}
226

    
227
	protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
228
		return getExtensionType(state, uuid, label, text, labelAbbrev, null);
229
	}
230
	protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<ExtensionType> voc){
231
		if (uuid == null){
232
			uuid = UUID.randomUUID();
233
		}
234
		ExtensionType extensionType = state.getExtensionType(uuid);
235
		if (extensionType == null){
236
			extensionType = (ExtensionType)getTermService().find(uuid);
237
			if (extensionType == null){
238
				extensionType = ExtensionType.NewInstance(text, label, labelAbbrev);
239
				extensionType.setUuid(uuid);
240
				if (voc == null){
241
					boolean isOrdered = false;
242
					voc = getVocabulary(state, TermType.ExtensionType, uuidUserDefinedExtensionTypeVocabulary, "User defined vocabulary for extension types", "User Defined Extension Types", null, null, isOrdered, extensionType);
243
				}
244
				voc.addTerm(extensionType);
245
				getTermService().saveOrUpdate(extensionType);
246
			}
247
			state.putExtensionType(extensionType);
248
		}
249
		return extensionType;
250
	}
251

    
252
	protected DefinedTerm getIdentiferType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
253
		if (uuid == null){
254
			uuid = UUID.randomUUID();
255
		}
256
		DefinedTerm identifierType = state.getIdentifierType(uuid);
257
		if (identifierType == null){
258
			identifierType = (DefinedTerm)getTermService().find(uuid);
259
			if (identifierType == null){
260
				identifierType = DefinedTerm .NewIdentifierTypeInstance(text, label, labelAbbrev);
261
				identifierType.setUuid(uuid);
262
				if (voc == null){
263
					boolean isOrdered = false;
264
					voc = getVocabulary(state, TermType.IdentifierType, uuidUserDefinedIdentifierTypeVocabulary, "User defined vocabulary for identifier types", "User Defined Identifier Types", null, null, isOrdered, identifierType);
265
				}
266
				voc.addTerm(identifierType);
267
				getTermService().saveOrUpdate(identifierType);
268
			}
269
			state.putIdentifierType(identifierType);
270
		}
271
		return identifierType;
272
	}
273

    
274

    
275
	protected MarkerType getMarkerType(STATE state, String keyString) {
276
		IInputTransformer transformer = state.getTransformer();
277
		MarkerType markerType = null;
278
		try {
279
			markerType = transformer.getMarkerTypeByKey(keyString);
280
		} catch (UndefinedTransformerMethodException e) {
281
			logger.info("getMarkerTypeByKey not yet implemented for this import");
282
		}
283
		if (markerType == null ){
284
			UUID uuid;
285
			try {
286
				uuid = transformer.getMarkerTypeUuid(keyString);
287
				return getMarkerType(state, uuid, keyString, keyString, keyString);
288
			} catch (UndefinedTransformerMethodException e) {
289
				logger.warn("getMarkerTypeUuid not yet implemented for this import");
290
			}
291
		}
292
		return null;
293
	}
294

    
295
	/**
296
	 * @see #getMarkerType(ImportStateBase, UUID, String, String, String, TermVocabulary, Language)
297
	 */
298
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev){
299
		return getMarkerType(state, uuid, label, description, labelAbbrev, null, null);
300
	}
301

    
302
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MarkerType> voc){
303
	    return this.getMarkerType(state, uuid, label, description, labelAbbrev, voc, null);
304
	}
305

    
306

    
307
	/**
308
     * @see #getMarkerType(ImportStateBase, UUID, String, String, String, TermVocabulary, Language)
309
     */
310
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MarkerType> voc, Language language){
311
		if (uuid == null){
312
			uuid = UUID.randomUUID();
313
		}
314
		MarkerType markerType = state.getMarkerType(uuid);
315
		if (markerType == null){
316
			markerType = (MarkerType)getTermService().find(uuid);
317
			if (markerType == null){
318
				markerType = MarkerType.NewInstance(description, label, labelAbbrev);
319
				if (language != null){
320
				    markerType.getRepresentations().iterator().next().setLanguage(language);
321
				}
322
				markerType.setUuid(uuid);
323
				if (voc == null){
324
					boolean isOrdered = false;
325
					voc = getVocabulary(state, TermType.MarkerType, uuidUserDefinedMarkerTypeVocabulary, "User defined vocabulary for marker types", "User Defined Marker Types", null, null, isOrdered, markerType);
326
				}
327
				voc.addTerm(markerType);
328
				getTermService().save(markerType);
329
			}
330
			state.putMarkerType(markerType);
331
		}
332
		return markerType;
333
	}
334

    
335
	protected AnnotationType getAnnotationType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){
336
		if (uuid == null){
337
			uuid = UUID.randomUUID();
338
		}
339
		AnnotationType annotationType = state.getAnnotationType(uuid);
340
		if (annotationType == null){
341
			annotationType = (AnnotationType)getTermService().find(uuid);
342
			if (annotationType == null){
343
				annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
344
				annotationType.setUuid(uuid);
345
				if (voc == null){
346
					boolean isOrdered = false;
347
					voc = getVocabulary(state, TermType.AnnotationType, uuidUserDefinedAnnotationTypeVocabulary, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered, annotationType);
348
				}
349

    
350
				voc.addTerm(annotationType);
351
				getTermService().save(annotationType);
352
			}
353
			state.putAnnotationType(annotationType);
354
		}
355
		return annotationType;
356
	}
357

    
358

    
359
	protected ReferenceSystem getReferenceSystem(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
360
		if (uuid == null){
361
			uuid = UUID.randomUUID();
362
		}
363
		ReferenceSystem refSystem = state.getReferenceSystem(uuid);
364
		if (refSystem == null){
365
			refSystem = (ReferenceSystem)getTermService().find(uuid);
366
			if (refSystem == null){
367
				refSystem = ReferenceSystem.NewInstance(text, label, labelAbbrev);
368
				if (voc == null){
369
					boolean isOrdered = false;
370
					voc = getVocabulary(state, TermType.ReferenceSystem, uuidUserDefinedReferenceSystemVocabulary, "User defined vocabulary for reference systems", "User Defined Reference System", null, null, isOrdered, refSystem);
371
				}
372
				voc.addTerm(refSystem);
373
				refSystem.setUuid(uuid);
374
				getTermService().save(refSystem);
375
			}
376
			state.putReferenceSystem(refSystem);
377
		}
378
		return refSystem;
379

    
380
	}
381

    
382

    
383

    
384
	protected Rank getRank(STATE state, UUID uuid, String label, String text, String labelAbbrev,OrderedTermVocabulary<Rank> voc, Rank lowerRank, RankClass rankClass){
385
		if (uuid == null){
386
			uuid = UUID.randomUUID();
387
		}
388
		Rank rank = state.getRank(uuid);
389
		if (rank == null){
390
			rank = (Rank)getTermService().find(uuid);
391
			if (rank == null){
392
				rank = Rank.NewInstance(rankClass, text, label, labelAbbrev);
393
				if (voc == null){
394
					boolean isOrdered = true;
395
					voc = (OrderedTermVocabulary)getVocabulary(state, TermType.Rank, uuidUserDefinedRankVocabulary, "User defined vocabulary for ranks", "User Defined Reference System", null, null, isOrdered, rank);
396
				}
397
				if (lowerRank == null){
398
					voc.addTerm(rank);
399
				}else{
400
					voc.addTermAbove(rank, lowerRank);
401
				}
402
				rank.setUuid(uuid);
403
				getTermService().save(rank);
404
			}
405
			state.putRank(rank);
406
		}
407
		return rank;
408

    
409
	}
410

    
411
	/**
412
	 * Returns a named area for a given uuid by first . If the named area does not
413
	 */
414
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level){
415
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, null, null);
416
	}
417

    
418
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType,
419
	        NamedAreaLevel level, TermVocabulary<?> voc, TermMatchMode matchMode){
420
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode, null);
421
	}
422

    
423
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType,
424
	        NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode, List<TermVocabulary<NamedArea>> vocabularyPreference){
425
		Class<NamedArea> clazz = NamedArea.class;
426
		if (uuid == null){
427
			uuid = UUID.randomUUID();
428
		}
429
		if (matchMode == null){
430
			matchMode = TermMatchMode.UUID_ONLY;
431
		}
432
		NamedArea namedArea = state.getNamedArea(uuid);
433
		if (namedArea == null){
434
			DefinedTermBase<?> term = getTermService().find(uuid);
435
			namedArea = CdmBase.deproxy(term,NamedArea.class);
436

    
437
			if (vocabularyPreference == null){
438
				vocabularyPreference =  new ArrayList<>();
439
			}
440
			if (vocabularyPreference.isEmpty()){  //add TDWG vocabulary if preferences are empty
441
				vocabularyPreference.add(Country.GERMANY().getVocabulary());
442
				vocabularyPreference.add(TdwgAreaProvider.getAreaByTdwgAbbreviation("GER").getVocabulary());
443
			}
444

    
445
			//TODO matching still experimental
446
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_LABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL ))){
447
				//TODO test
448
				Pager<NamedArea> areaPager = getTermService().findByTitleWithRestrictions(clazz, label, null, null, null, null, null, null);
449
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
450
			}
451
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_ABBREVLABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL))){
452
				Pager<NamedArea> areaPager = getTermService().findByRepresentationAbbreviation(labelAbbrev, clazz, null, null);
453
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
454
			}
455

    
456
			if (namedArea == null){
457
				namedArea = NamedArea.NewInstance(text, label, labelAbbrev);
458
				if (voc == null){
459
					boolean isOrdered = true;
460
					voc = getVocabulary(state, TermType.NamedArea, uuidUserDefinedNamedAreaVocabulary, "User defined vocabulary for named areas", "User Defined Named Areas", null, null, isOrdered, namedArea);
461
				}
462
				voc.addTerm(namedArea);
463
				namedArea.setType(areaType);
464
				namedArea.setLevel(level);
465
				namedArea.setUuid(uuid);
466
				getTermService().saveOrUpdate(namedArea);
467
			}
468
			state.putNamedArea(namedArea);
469
		}
470
		return namedArea;
471
	}
472

    
473

    
474
	private NamedArea findBestMatchingArea(Pager<NamedArea> areaPager, UUID uuid, String label, String text, String abbrev, List<TermVocabulary<NamedArea>> vocabularyPreference) {
475
		// TODO preliminary implementation
476
		List<NamedArea> list = areaPager.getRecords();
477
		if (list.size() == 0){
478
			return null;
479
		}else if (list.size() == 1){
480
			return list.get(0);
481
		}else if (list.size() > 1){
482
			List<NamedArea> preferredList = new ArrayList<>();
483
			for (TermVocabulary<NamedArea> voc: vocabularyPreference){
484
				for (NamedArea area : list){
485
					if (voc.equals(area.getVocabulary())){
486
						preferredList.add(area);
487
					}
488
				}
489
				if (preferredList.size() > 0){
490
					break;
491
				}
492
			}
493
			if (preferredList.size() > 1 ){
494
				preferredList = getHighestLevelAreas(preferredList);
495
			}else if (preferredList.size() == 0 ){
496
				preferredList = list;
497
			}
498
			if (preferredList.size() == 1 ){
499
				return preferredList.get(0);
500
			}else if (preferredList.size() > 1 ){
501
				String message = "There is more than 1 matching area for %s, %s, %s. As a preliminary implementation I take the first";
502
				message = String.format(message, label, abbrev, text);
503
				logger.warn(message);
504
				return list.get(0);
505
			}
506
		}
507
		return null;
508
	}
509

    
510

    
511
	private List<NamedArea> getHighestLevelAreas(List<NamedArea> preferredList) {
512
		List<NamedArea> result = new ArrayList<>();
513
		for (NamedArea area : preferredList){
514
			if (result.isEmpty()){
515
				result.add(area);
516
			}else {
517
				int compare = compareAreaLevel(area, result.get(0));
518
				if (compare > 0){
519
					result = new ArrayList<>();
520
					result.add(area);
521
				}else if (compare == 0){
522
					result.add(area);
523
				}else{
524
					//do nothing
525
				}
526
			}
527
		}
528

    
529
		return result;
530
	}
531

    
532

    
533
	private int compareAreaLevel(NamedArea area1, NamedArea area2) {
534
		NamedAreaLevel level1 = area1.getLevel();
535
		NamedAreaLevel level2 = area2.getLevel();
536
		if (level1 == null){
537
			return (level2 == null)? 0 : 1;
538
		}else if (level2 == null){
539
			return -1;
540
		}else{
541
			return level1.compareTo(level2);
542
		}
543
	}
544

    
545

    
546
	protected NamedAreaLevel getNamedAreaLevel(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<NamedAreaLevel> voc){
547
		if (uuid == null){
548
			uuid = UUID.randomUUID();
549
		}
550
		NamedAreaLevel namedAreaLevel = state.getNamedAreaLevel(uuid);
551
		if (namedAreaLevel == null){
552
			//TODO propPath just for testing
553
			List<String> propPath = Arrays.asList("vocabulary");
554
			DefinedTermBase<NamedAreaLevel> term = getTermService().load(uuid, propPath);
555
			namedAreaLevel = CdmBase.deproxy(term, NamedAreaLevel.class);
556
			if (namedAreaLevel == null){
557
				namedAreaLevel = NamedAreaLevel.NewInstance(text, label, labelAbbrev);
558
				if (voc == null){
559
					boolean isOrdered = true;
560
					voc = getVocabulary(state, TermType.NamedAreaLevel, uuidUserDefinedNamedAreaLevelVocabulary, "User defined vocabulary for named area levels", "User Defined Named Area Levels", null, null, isOrdered, namedAreaLevel);
561
				}
562
				//FIXME only for debugging
563
				Set<NamedAreaLevel> terms = voc.getTerms();
564
				for (NamedAreaLevel level : terms){
565
					TermVocabulary<NamedAreaLevel> levelVoc = level.getVocabulary();
566
					if (levelVoc == null){
567
						logger.error("ONLY FOR DEBUG: Level voc is null");
568
					}else{
569
						logger.info("ONLY FOR DEBUG: Level voc is not null");
570
					}
571
				}
572
				voc.addTerm(namedAreaLevel);
573
				namedAreaLevel.setUuid(uuid);
574
				getTermService().save(namedAreaLevel);
575
			}
576
			state.putNamedAreaLevel(namedAreaLevel);
577
		}
578
		return namedAreaLevel;
579
	}
580

    
581
	/**
582
	 * Returns a {@link State} if it exists. <code>null</code> otherwise.
583
	 * @param state
584
	 * @param uuid
585
	 * @return {@link State}
586
	 */
587
	protected State getStateTerm(STATE state, UUID uuid){
588
		return getStateTerm(state, uuid, null, null, null, null);
589
	}
590

    
591

    
592
	/**
593
	 * Returns a {@link State} for a given uuid by first checking if the uuid has already been used in this import, if not
594
	 * checking if the state exists in the database, if not creating it anew (with vocabulary etc.).
595
	 * If label, text and labelAbbrev are all <code>null</code> no state is created.
596
	 * @param importState
597
	 * @param uuid
598
	 * @param label
599
	 * @param text
600
	 * @param labelAbbrev
601
	 * @param voc
602
	 * @return
603
	 */
604
	protected State getStateTerm(STATE importState, UUID uuid, String label, String text, String labelAbbrev, OrderedTermVocabulary<State> voc) {
605
		if (uuid == null){
606
			return null;
607
		}
608
		State stateTerm = importState.getStateTerm(uuid);
609
		if (stateTerm == null){
610
			stateTerm = CdmBase.deproxy(getTermService().find(uuid), State.class);
611
			if (stateTerm == null && ! hasNoLabel(label, text, labelAbbrev)){
612
				stateTerm = State.NewInstance(text, label, labelAbbrev);
613
				stateTerm.setUuid(uuid);
614
				if (voc == null){
615
					boolean isOrdered = true;
616
					TermVocabulary<State> orderedVoc = getVocabulary(importState, TermType.State, uuidUserDefinedStateVocabulary, "User defined vocabulary for states used by Categorical Data", "User Defined States", null, null, isOrdered, stateTerm);
617
					voc = CdmBase.deproxy(orderedVoc, OrderedTermVocabulary.class);
618
				}
619
				voc.addTerm(stateTerm);
620
				getTermService().save(stateTerm);
621
			}else if (stateTerm == null){
622
                logger.warn("No label provided for new state with uuid " + uuid);
623
            }
624
			importState.putStateTerm(stateTerm);
625
		}
626
		return stateTerm;
627
	}
628

    
629
	/**
630
	 * Returns a feature if it exists, null otherwise.
631
	 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
632
	 */
633
	protected Feature getFeature(STATE state, UUID uuid){
634
		return getFeature(state, uuid, null, null, null, null);
635
	}
636

    
637
	/**
638
	 * Returns a feature for a given uuid by first checking if the uuid has already been used in this import, if not
639
	 * checking if the feature exists in the database, if not creating it anew (with vocabulary etc.).
640
	 * If label, text and labelAbbrev are all <code>null</code> no feature is created.
641
	 */
642
	protected Feature getFeature(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<Feature> voc){
643
		if (uuid == null){
644
			return null;
645
		}
646
		Feature feature = state.getFeature(uuid);
647
		if (feature == null){
648
			feature = (Feature)getTermService().find(uuid);
649
			if (feature == null && ! hasNoLabel(label, description, labelAbbrev)){
650
				feature = Feature.NewInstance(description, label, labelAbbrev);
651
				feature.setUuid(uuid);
652
				feature.setSupportsTextData(true);
653
				if (voc == null){
654
					boolean isOrdered = false;
655
					voc = getVocabulary(state, TermType.Feature, uuidUserDefinedFeatureVocabulary, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered, feature);
656
				}
657
				voc.addTerm(feature);
658
				getTermService().save(feature);
659
				state.putFeature(feature);
660
			}
661
		}
662
		return feature;
663
	}
664

    
665
	/**
666
     * Returns a {@link NomenclaturalStatusType nomenclatural status type} for a given uuid by first
667
     * checking if the uuid has already been used in this import, if not
668
     * checking if the status type exists in the database, if not creating it anew (with vocabulary etc.).
669
     * If label, text and labelAbbrev are all <code>null</code> no status type is created.
670
     */
671
    protected NomenclaturalStatusType getNomenclaturalStatusType(STATE state, UUID uuid, String label,
672
            String description, String labelAbbrev, Language language, TermVocabulary<NomenclaturalStatusType> voc){
673
        if (uuid == null){
674
            return null;
675
        }
676
        NomenclaturalStatusType nomStatusType = state.getNomenclaturalStatusType(uuid);
677
        if (nomStatusType == null){
678
            nomStatusType = (NomenclaturalStatusType)getTermService().find(uuid);
679
            if (nomStatusType == null && ! hasNoLabel(label, description, labelAbbrev)){
680
                if (language == null){
681
                    language = Language.UNKNOWN_LANGUAGE();
682
                }
683
                nomStatusType = NomenclaturalStatusType.NewInstance(description, label, labelAbbrev, language);
684
                nomStatusType.setUuid(uuid);
685
                if (voc == null){
686
                    boolean isOrdered = false;
687
                    voc = getVocabulary(state, TermType.NomenclaturalStatusType, uuidUserDefinedNomenclaturalStatusTypeVocabulary, "User defined vocabulary for nomenclatural status type", "User Defined NomenclaturalStatusTypes", null, null, isOrdered, nomStatusType);
688
                }
689
                voc.addTerm(nomStatusType);
690
                getTermService().save(nomStatusType);
691
                state.putNomenclaturalStatusType(nomStatusType);
692
            }
693
        }
694
        return nomStatusType;
695
    }
696

    
697
	protected DefinedTerm getKindOfUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
698
		if (uuid == null){
699
		    uuid = UUID.randomUUID();
700
		}
701
		DefinedTerm unit = state.getKindOfUnit(uuid);
702
		if (unit == null){
703
			unit = (DefinedTerm)getTermService().find(uuid);
704
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
705
				unit = DefinedTerm.NewKindOfUnitInstance(description, label, labelAbbrev);
706
				unit.setUuid(uuid);
707
				if (voc == null){
708
					boolean isOrdered = false;
709
					voc = getVocabulary(state, TermType.KindOfUnit, uuidUserDefinedKindOfUnitVocabulary, "User defined vocabulary for kind-of-units", "User Defined Measurement kind-of-units", null, null, isOrdered, unit);
710
				}
711
				voc.addTerm(unit);
712
				getTermService().save(unit);
713
			}
714
			state.putKindOfUnit(unit);
715
		}
716
		return unit;
717
	}
718

    
719
	/**
720
	 * Returns a {@link MeasurementUnit} for a given uuid by first checking if the uuid has already been used in this import, if not
721
	 * checking if the {@link MeasurementUnit} exists in the database, if not creating it anew (with vocabulary etc.).
722
	 * If label, text and labelAbbrev are all <code>null</code> no {@link MeasurementUnit} is created.
723
	 * @param state
724
	 * @param uuid
725
	 * @param label
726
	 * @param text
727
	 * @param labelAbbrev
728
	 * @return
729
	 */
730
	protected MeasurementUnit getMeasurementUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MeasurementUnit> voc){
731
		if (uuid == null){
732
			return null;
733
		}
734
		MeasurementUnit unit = state.getMeasurementUnit(uuid);
735
		if (unit == null){
736
			unit = (MeasurementUnit)getTermService().find(uuid);
737
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
738
				unit = MeasurementUnit.NewInstance(description, label, labelAbbrev);
739
				unit.setUuid(uuid);
740
				if (voc == null){
741
					boolean isOrdered = false;
742
					voc = getVocabulary(state, TermType.MeasurementUnit, uuidUserDefinedMeasurementUnitVocabulary, "User defined vocabulary for measurement units", "User Defined Measurement Units", null, null, isOrdered, unit);
743
				}
744
				voc.addTerm(unit);
745
				getTermService().save(unit);
746
			}
747
			state.putMeasurementUnit(unit);
748
		}
749
		return unit;
750
	}
751

    
752
	/**
753
	 * Returns a {@link StatisticalMeasure} for a given uuid by first checking if the uuid has already been used in this import, if not
754
	 * checking if the {@link StatisticalMeasure} exists in the database, if not creating it anew (with vocabulary etc.).
755
	 * If label, text and labelAbbrev are all <code>null</code> no {@link StatisticalMeasure} is created.
756
	 * @param state
757
	 * @param uuid
758
	 * @param label
759
	 * @param text
760
	 * @param labelAbbrev
761
	 * @return
762
	 */
763
	protected StatisticalMeasure getStatisticalMeasure(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<StatisticalMeasure> voc){
764
		if (uuid == null){
765
			return null;
766
		}
767
		StatisticalMeasure statisticalMeasure = state.getStatisticalMeasure(uuid);
768
		if (statisticalMeasure == null){
769
			statisticalMeasure = (StatisticalMeasure)getTermService().find(uuid);
770
			if (statisticalMeasure == null && ! hasNoLabel(label, description, labelAbbrev)){
771
				statisticalMeasure = StatisticalMeasure.NewInstance(description, label, labelAbbrev);
772
				statisticalMeasure.setUuid(uuid);
773
				if (voc == null){
774
					boolean isOrdered = false;
775
					voc = getVocabulary(state, TermType.StatisticalMeasure, uuidUserDefinedStatisticalMeasureVocabulary, "User defined vocabulary for statistical measures", "User Defined Statistical Measures", null, null, isOrdered, statisticalMeasure);
776
				}
777
				voc.addTerm(statisticalMeasure);
778
				getTermService().save(statisticalMeasure);
779
			}
780
			state.putStatisticalMeasure(statisticalMeasure);
781
		}
782
		return statisticalMeasure;
783
	}
784

    
785
	/**
786
	 * Returns a {@link Modifier} for a given uuid by first checking if the uuid has already been used in this import, if not
787
	 * checking if the {@link Modifier} exists in the database, if not creating it anew (with vocabulary etc.).
788
	 * If label, text and labelAbbrev are all <code>null</code> no {@link Modifier} is created.
789
	 * @param state
790
	 * @param uuid
791
	 * @param label
792
	 * @param text
793
	 * @param labelAbbrev
794
	 * @return
795
	 */
796
	protected DefinedTerm getModifier(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
797
		if (uuid == null){
798
			return null;
799
		}
800
		DefinedTerm modifier = state.getModifier(uuid);
801
		if (modifier == null){
802
			modifier = (DefinedTerm)getTermService().find(uuid);
803
			if (modifier == null && ! hasNoLabel(label, description, labelAbbrev)){
804
				modifier = DefinedTerm.NewModifierInstance(description, label, labelAbbrev);
805
				modifier.setUuid(uuid);
806
				if (voc == null){
807
					boolean isOrdered = false;
808
					voc = getVocabulary(state, TermType.Modifier, uuidUserDefinedModifierVocabulary, "User defined vocabulary for modifier", "User Defined Modifier", null, null, isOrdered, modifier);
809
				}
810
				voc.addTerm(modifier);
811
				getTermService().save(modifier);
812
			}
813
			state.putModifier(modifier);
814
		}
815
		return modifier;
816
	}
817

    
818
	/**
819
	 * Returns a taxon relationship type for a given uuid by first checking if the uuid has already been used in this import, if not
820
	 * checking if the taxon relationship type exists in the database, if not creating it anew (with vocabulary etc.).
821
	 * If label, text and labelAbbrev are all <code>null</code> no taxon relationship type is created.
822
	 * @return
823
	 */
824
	public TaxonRelationshipType getTaxonRelationshipType(STATE state, UUID uuid, String label, String description, String labelAbbrev,
825
	        String reverseLabel, String reverseDescription, String reverseAbbrev, TermVocabulary<TaxonRelationshipType> voc){
826
		if (uuid == null){
827
			return null;
828
		}
829
		TaxonRelationshipType relType = state.getTaxonRelationshipType(uuid);
830
		if (relType == null){
831
			relType = (TaxonRelationshipType)getTermService().find(uuid);
832
			if (relType == null && ! hasNoLabel(label, description, labelAbbrev)){
833
				relType = TaxonRelationshipType.NewInstance(description, label, labelAbbrev, false, false);
834
				if (!hasNoLabel(reverseLabel, reverseDescription, reverseAbbrev)){
835
				    relType.addInverseRepresentation(Representation.NewInstance(reverseDescription, reverseLabel, reverseAbbrev, Language.DEFAULT()));
836
				}
837
				relType.setUuid(uuid);
838
				if (voc == null){
839
					boolean isOrdered = true;
840
					voc = getVocabulary(state, TermType.TaxonRelationshipType, uuidUserDefinedTaxonRelationshipTypeVocabulary, "User defined vocabulary for taxon relationship types", "User Defined Taxon Relationship Types", null, null, isOrdered, relType);
841
				}
842
				voc.addTerm(relType);
843
				getTermService().save(relType);
844
			}
845
			state.putTaxonRelationshipType(relType);
846
		}
847
		return relType;
848
	}
849

    
850
	private boolean hasNoLabel(String label, String text, String labelAbbrev) {
851
		return label == null && text == null && labelAbbrev == null;
852
	}
853

    
854
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm){
855
	    return getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, null);
856
	}
857

    
858

    
859
	/**
860
	 * Returns a presence term for a given uuid by first ...
861
	 * @param state
862
	 * @param uuid
863
	 * @param label
864
	 * @param text
865
	 * @param labelAbbrev
866
	 * @return
867
	 */
868
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm, TermVocabulary<PresenceAbsenceTerm> voc){
869
		if (uuid == null){
870
			return null;
871
		}
872
		PresenceAbsenceTerm presenceTerm = state.getPresenceAbsenceTerm(uuid);
873
		if (presenceTerm == null){
874
			presenceTerm = (PresenceAbsenceTerm)getTermService().find(uuid);
875
			if (presenceTerm == null){
876
				presenceTerm = PresenceAbsenceTerm.NewPresenceInstance(text, label, labelAbbrev);
877
				presenceTerm.setUuid(uuid);
878
				presenceTerm.setAbsenceTerm(isAbsenceTerm);
879
				//set vocabulary ; FIXME use another user-defined vocabulary
880
				if (voc == null){
881
                    boolean isOrdered = true;
882
                    voc = getVocabulary(state, TermType.PresenceAbsenceTerm, uuidUserDefinedPresenceAbsenceVocabulary, "User defined vocabulary for distribution status", "User Defined Distribution Status", null, null, isOrdered, presenceTerm);
883
                }
884
				voc.addTerm(presenceTerm);
885
				getTermService().save(presenceTerm);
886
			}
887
			state.putPresenceAbsenceTerm(presenceTerm);
888
		}
889
		return presenceTerm;
890
	}
891

    
892
    /**
893
     * Retrieves the language for the given uuid. If language does not exist
894
     * <code>null</code> is returned.
895
     */
896
    protected Language getLanguage(STATE state, UUID uuid){
897
        return getLanguage(state, uuid, null, null, null, null);
898
    }
899

    
900
	/**
901
	 * Returns a language for a given uuid by first ...
902
	 * @param state
903
	 * @param uuid
904
	 * @param label
905
	 * @param text
906
	 * @param labelAbbrev
907
	 * @return
908
	 */
909
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
910
		return getLanguage(state, uuid, label, text, labelAbbrev, null);
911
	}
912

    
913
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
914
		if (uuid == null){
915
			return null;
916
		}
917
		Language language = state.getLanguage(uuid);
918
		if (language == null){
919
			language = (Language)getTermService().find(uuid);
920
			if (language == null){
921
				if (text == null && label == null && labelAbbrev == null){
922
				    return null;
923
				}
924
			    language = Language.NewInstance(text, label, labelAbbrev);
925

    
926
				language.setUuid(uuid);
927
				if (voc == null){
928
					UUID uuidLanguageVoc = uuidUserDefinedLanguageVocabulary;
929
					boolean isOrdered = false;
930
					voc = getVocabulary(state, TermType.Language, uuidLanguageVoc, "User defined languages", "User defined languages", "User defined languages", null, isOrdered, language);
931
				}
932
				//set vocabulary ; FIXME use another user-defined vocabulary
933

    
934
				voc.addTerm(language);
935
				getTermService().save(language);
936
			}
937
			state.putLanguage(language);
938
		}
939
		return language;
940
	}
941

    
942
    protected <T extends DefinedTermBase> TermVocabulary<T> getVocabulary(STATE state, TermType termType, UUID uuid, String description, String label,
943
            String abbrev, URI termSourceUri, boolean isOrdered, T type) {
944

    
945
        TermVocabulary<T> voc = state != null? state.getTermedVocabulary(uuid): null;
946
        if (voc != null){
947
            return voc;
948
        }
949

    
950
        List<String> propPath = Arrays.asList(new String[]{"terms"});
951
        voc = getVocabularyService().load(uuid, propPath);
952
        Class<T> clazz = null;
953
        if (voc == null){
954
            if (isOrdered){
955
                voc = OrderedTermVocabulary.NewInstance(termType, description, label, abbrev, termSourceUri);
956
            }else{
957
                voc = TermVocabulary.NewInstance(termType, clazz, description, label, abbrev, termSourceUri);
958
            }
959
            voc.setUuid(uuid);
960
            getVocabularyService().save(voc);
961
        }
962
        state.putTermedVocabularyMap(voc);
963
        return voc;
964
    }
965

    
966
    public OriginalSourceBase addOriginalSource(ICdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation, String originalInformation)  {
967
        OriginalSourceBase source = addOriginalSource(cdmBase, idAttributeValue, namespace, citation);
968
        if (source != null && isNotBlank(originalInformation)){
969
            source.setOriginalNameString(originalInformation);
970
        }
971
        return source;
972
    }
973

    
974
	/**
975
	 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
976
	 * If cdmBase is not sourceable nothing happens.
977
	 * TODO Move to DbImportBase once this exists.
978
	 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
979
	 * @param rs
980
	 * @param cdmBase
981
	 * @param dbIdAttribute
982
	 * @param namespace
983
	 * @param citation
984
	 * @throws SQLException
985
	 */
986
	public OriginalSourceBase addOriginalSource(ICdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation)  {
987
		if (cdmBase instanceof ISourceable ){
988
			OriginalSourceBase source;
989
			ISourceable sourceable = (ISourceable<?>)cdmBase;
990
			Object id = idAttributeValue;
991
			String strId = String.valueOf(id);
992
			String microCitation = null;
993
			OriginalSourceType type = OriginalSourceType.Import;
994
			if (cdmBase instanceof IdentifiableEntity){
995
				source = IdentifiableSource.NewInstance(type, strId, namespace, citation, microCitation);
996
			}else if (cdmBase instanceof DescriptionElementBase){
997
				source = DescriptionElementSource.NewInstance(type, strId, namespace, citation, microCitation);
998
			}else{
999
				logger.warn("ISourceable not beeing identifiable entities or description element base are not yet supported. CdmBase is of type " + cdmBase.getClass().getName() + ". Original source not added.");
1000
				return null;
1001
			}
1002
			sourceable.addSource(source);
1003
			return source;
1004
		}else if (cdmBase != null){
1005
			logger.warn("Sourced object does not implement ISourceable: " + cdmBase.getClass() + "," + cdmBase.getUuid());
1006
		}else{
1007
			logger.warn("Sourced object is null");
1008
		}
1009
		return null;
1010
	}
1011

    
1012
	/**
1013
	 * @see #addOriginalSource(CdmBase, Object, String, Reference)
1014
	 * @param rs
1015
	 * @param cdmBase
1016
	 * @param dbIdAttribute
1017
	 * @param namespace
1018
	 * @param citation
1019
	 * @throws SQLException
1020
	 */
1021
	public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
1022
		Object id = rs.getObject(dbIdAttribute);
1023
		addOriginalSource(cdmBase, id, namespace, citation);
1024
	}
1025

    
1026

    
1027
	/**
1028
	 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
1029
	 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
1030
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
1031
	 * the parent.
1032
	 * @param parentTaxon
1033
	 * @param childTaxon
1034
	 */
1035
	protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
1036
		if (parentTaxon == null){
1037
			logger.warn("Parent taxon is null. Missing name parts can not be taken from parent");
1038
			return;
1039
		}
1040
		INonViralName parentName = parentTaxon.getName();
1041
		INonViralName childName = childTaxon.getName();
1042
		fillMissingEpithets(parentName, childName);
1043
	}
1044

    
1045
	/**
1046
	 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
1047
	 * or <i>species</i> respectively the according epithets are taken from the parent name.
1048
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
1049
	 * the parent.
1050
	 * @param parentTaxon
1051
	 * @param childTaxon
1052
	 */
1053
	protected void fillMissingEpithets(INonViralName parentName, INonViralName childName) {
1054
		if (isBlank(childName.getGenusOrUninomial()) && childName.getRank().isLower(Rank.GENUS()) ){
1055
			childName.setGenusOrUninomial(parentName.getGenusOrUninomial());
1056
		}
1057

    
1058
		if (isBlank(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
1059
			childName.setSpecificEpithet(parentName.getSpecificEpithet());
1060
		}
1061
		if (childName.isAutonym() && childName.getCombinationAuthorship() == null && childName.getBasionymAuthorship() == null ){
1062
			childName.setCombinationAuthorship(parentName.getCombinationAuthorship());
1063
			childName.setBasionymAuthorship(parentName.getBasionymAuthorship());
1064
		}
1065
	}
1066

    
1067
	/**
1068
	 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
1069
	 * an arbitrary one is chosen.
1070
	 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
1071
	 * is <code>true</code>.
1072
	 * @param createNewIfNotExists
1073
	 * @param isImageGallery if true only taxon description being image galleries are considered.
1074
	 * If false only taxon description being no image galleries are considered.
1075
	 * @return
1076
	 */
1077
	public TaxonNameDescription getTaxonNameDescription(TaxonName name, boolean isImageGallery, boolean createNewIfNotExists) {
1078
		Reference ref = null;
1079
		return getTaxonNameDescription(name, ref, isImageGallery, createNewIfNotExists);
1080
	}
1081

    
1082
	/**
1083
	 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
1084
	 * Only matches a description if the given reference is a source of the description.<BR>
1085
	 * If a new description is created the given reference will be added as a source.
1086
	 *
1087
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1088
	 */
1089
	public TaxonNameDescription getTaxonNameDescription(TaxonName name, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1090
		TaxonNameDescription result = null;
1091
		Set<TaxonNameDescription> descriptions= name.getDescriptions();
1092
		for (TaxonNameDescription description : descriptions){
1093
			if (description.isImageGallery() == isImageGallery){
1094
				if (hasCorrespondingSource(ref, description)){
1095
					result = description;
1096
					break;
1097
				}
1098
			}
1099
		}
1100
		if (result == null && createNewIfNotExists){
1101
			result = TaxonNameDescription.NewInstance(name);
1102
			result.setImageGallery(isImageGallery);
1103
			if (ref != null){
1104
				result.addImportSource(null, null, ref, null);
1105
			}
1106
		}
1107
		return result;
1108
	}
1109

    
1110
	/**
1111
	 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
1112
	 * an arbitrary one is chosen.
1113
	 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
1114
	 * is <code>true</code>.
1115
	 * @param createNewIfNotExists
1116
	 * @param isImageGallery if true only taxon description being image galleries are considered.
1117
	 * If false only taxon description being no image galleries are considered.
1118
	 * @return
1119
	 */
1120
	public TaxonDescription getTaxonDescription(Taxon taxon, boolean isImageGallery, boolean createNewIfNotExists) {
1121
		Reference ref = null;
1122
		return getTaxonDescription(taxon, ref, isImageGallery, createNewIfNotExists);
1123
	}
1124

    
1125
	/**
1126
	 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
1127
	 * Only matches a description if the given reference is a source of the description.<BR>
1128
	 * If a new description is created the given reference will be added as a source.
1129
	 *
1130
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1131
	 * @see #getDefaultTaxonDescription(Taxon, boolean, boolean, Reference)
1132
	 */
1133
	public TaxonDescription getTaxonDescription(Taxon taxon, Reference ref, boolean isImageGallery,
1134
	        boolean createNewIfNotExists) {
1135
		TaxonDescription result = null;
1136
		Set<TaxonDescription> descriptions = taxon.getDescriptions();
1137
		for (TaxonDescription description : descriptions){
1138
			if (description.isImageGallery() == isImageGallery){
1139
				if (hasCorrespondingSource(ref, description)){
1140
					result = description;
1141
					break;
1142
				}
1143
			}
1144
		}
1145
		if (result == null && createNewIfNotExists){
1146
			result = TaxonDescription.NewInstance(taxon);
1147
			result.setImageGallery(isImageGallery);
1148
			if (ref != null){
1149
				result.addImportSource(null, null, ref, null);
1150
			}
1151
		}
1152
		return result;
1153
	}
1154

    
1155
	/**
1156
	 * Returns the default taxon description. If no default taxon description exists,
1157
	 * a new one is created, the default flag is set to true and, if a source is passed,
1158
	 * it is added to the new description. Otherwise source has no influence.
1159
	 * @param taxon
1160
	 * @param isImageGallery
1161
	 * @param createNewIfNotExists
1162
	 * @param source
1163
	 * @return the default description
1164
	 * @see #getTaxonDescription(Taxon, Reference, boolean, boolean)
1165
	 */
1166
	public TaxonDescription getDefaultTaxonDescription(Taxon taxon, boolean isImageGallery,
1167
            boolean createNewIfNotExists, Reference source) {
1168
        TaxonDescription result = null;
1169
        Set<TaxonDescription> descriptions= taxon.getDescriptions();
1170
        for (TaxonDescription description : descriptions){
1171
            if (description.isImageGallery() == isImageGallery){
1172
                if (description.isDefault()){
1173
                    result = description;
1174
                    break;
1175
                }
1176
            }
1177
        }
1178
        if (result == null && createNewIfNotExists){
1179
            result = TaxonDescription.NewInstance(taxon);
1180
            result.setImageGallery(isImageGallery);
1181
            result.setDefault(true);
1182
            if (source != null){
1183
                result.addImportSource(null, null, source, null);
1184
            }
1185
        }
1186
        return result;
1187
    }
1188

    
1189
    /**
1190
     * Returns the taxon description with marked as <code>true</code> with the given marker type.
1191
     * If createNewIfNotExists a new description is created if it does not yet exist.
1192
     * For the new description the source and the title are set if not <code>null</code>.
1193
     * @param taxon
1194
     * @param markerType
1195
     * @param isImageGallery
1196
     * @param createNewIfNotExists
1197
     * @param source
1198
     * @param title
1199
     * @return the existing or new taxon description
1200
     */
1201
   public TaxonDescription getMarkedTaxonDescription(Taxon taxon, MarkerType markerType, boolean isImageGallery,
1202
            boolean createNewIfNotExists, Reference source, String title) {
1203
        TaxonDescription result = null;
1204
        Set<TaxonDescription> descriptions= taxon.getDescriptions();
1205
        for (TaxonDescription description : descriptions){
1206
            if (description.isImageGallery() == isImageGallery){
1207
                if (description.hasMarker(markerType, true)){
1208
                    result = description;
1209
                    break;
1210
                }
1211
            }
1212
        }
1213
        if (result == null && createNewIfNotExists){
1214
            result = TaxonDescription.NewInstance(taxon);
1215
            result.setImageGallery(isImageGallery);
1216
            result.addMarker(Marker.NewInstance(markerType, true));
1217
            if (source != null){
1218
                result.addImportSource(null, null, source, null);
1219
            }
1220
            if (isNotBlank(title)){
1221
                result.setTitleCache(title, true);
1222
            }
1223
        }
1224
        return result;
1225
    }
1226

    
1227

    
1228
	/**
1229
	 * Returns the {@link SpecimenDescription specimen description} for a {@link SpecimenOrObservationBase specimen or observation}.
1230
	 * If there are multiple specimen descriptions an arbitrary one is chosen.
1231
	 * If no specimen description exists, a new one is created if <code>createNewIfNotExists</code> is <code>true</code>.
1232
	 * @param createNewIfNotExists
1233
	 * @param isImageGallery if true only specimen description being image galleries are considered.
1234
	 * If false only specimen description being no image galleries are considered.
1235
	 * @return
1236
	 */
1237
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, boolean isImageGallery, boolean createNewIfNotExists) {
1238
		Reference ref = null;
1239
		return getSpecimenDescription(specimen, ref, isImageGallery, createNewIfNotExists);
1240
	}
1241

    
1242
	/**
1243
	 * Like {@link #getSpecimenDescription(SpecimenOrObservationBase, boolean, boolean)}
1244
	 * Only matches a description if the given reference is a source of the description.<BR>
1245
	 * If a new description is created the given reference will be added as a source.
1246
	 *
1247
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1248
	 */
1249
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1250
		SpecimenDescription result = null;
1251
		@SuppressWarnings("unchecked")
1252
        Set<SpecimenDescription> descriptions= specimen.getDescriptions();
1253
		for (SpecimenDescription description : descriptions){
1254
			if (description.isImageGallery() == isImageGallery){
1255
				if (hasCorrespondingSource(ref, description)){
1256
					result = description;
1257
					break;
1258
				}
1259
			}
1260
		}
1261
		if (result == null && createNewIfNotExists){
1262
			result = SpecimenDescription.NewInstance(specimen);
1263
			result.setImageGallery(isImageGallery);
1264
			if (ref != null){
1265
				result.addImportSource(null, null, ref, null);
1266
			}
1267
		}
1268
		return result;
1269
	}
1270

    
1271

    
1272
	/**
1273
	 * Returns the textdata that holds general information about a feature for a taxon description.
1274
	 * This is mainly necessary for descriptions that have more than one description element for
1275
	 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
1276
	 * for hierarchical features where no description element exists for a higher hierarchy level.
1277
	 * Example: the description feature has subfeatures. But some information like authorship, figures,
1278
	 * sources need to be added to the description itself.
1279
	 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
1280
	 * there will be a boolean marker in the TextData class itself.
1281
	 *
1282
	 * @param state
1283
	 * @param feature
1284
	 * @param taxon
1285
	 * @param ref
1286
	 * @param createIfNotExists
1287
	 * @return
1288
	 */
1289
	protected TextData getFeaturePlaceholder(STATE state, DescriptionBase<?> description, Feature feature, boolean createIfNotExists) {
1290
		UUID featurePlaceholderUuid = MarkupTransformer.uuidMarkerFeaturePlaceholder;
1291
		for (DescriptionElementBase element : description.getElements()){
1292
			if (element.isInstanceOf(TextData.class)){
1293
				TextData textData = CdmBase.deproxy(element, TextData.class);
1294
				if (textData.getFeature() == null || ! textData.getFeature().equals(feature)){
1295
					continue;
1296
				}
1297
				for (Marker marker : textData.getMarkers()){
1298
					MarkerType markerType = marker.getMarkerType();
1299
					if (markerType != null &&
1300
							markerType.getUuid().equals(featurePlaceholderUuid) &&
1301
							marker.getValue() == true){
1302
						return textData;
1303
					}
1304
				}
1305
			}
1306
		}
1307
		if (createIfNotExists){
1308
			TextData newPlaceholder = TextData.NewInstance(feature);
1309
			MarkerType placeholderMarkerType = getMarkerType(state, featurePlaceholderUuid, "Feature Placeholder", "Feature Placeholder", null);
1310
			Marker marker = Marker.NewInstance(placeholderMarkerType, true);
1311
			newPlaceholder.addMarker(marker);
1312
			description.addElement(newPlaceholder);
1313
			return newPlaceholder;
1314
		}else{
1315
			return null;
1316
		}
1317
	}
1318

    
1319

    
1320

    
1321
	/**
1322
	 * Returns true, if this description has a source with a citation equal to the given reference.
1323
	 * Returns true if the given reference is null.
1324
	 * @param ref
1325
	 * @param description
1326
	 */
1327
	private boolean hasCorrespondingSource(Reference ref, DescriptionBase<?> description) {
1328
		if (ref != null){
1329
			for (IdentifiableSource source : description.getSources()){
1330
				if (ref.equals(source.getCitation())){
1331
					return true;
1332
				}
1333
			}
1334
			return false;
1335
		}
1336
		return true;
1337

    
1338
	}
1339

    
1340

    
1341
	/**
1342
	 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
1343
	 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
1344
	 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
1345
	 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
1346
	 * same secundum reference is returned. If no such single taxon exists an
1347
	 * {@link IllegalStateException illegal state exception} is thrown.
1348
	 * @param taxonBase
1349
	 * @return
1350
	 */
1351
	protected Taxon getAcceptedTaxon(TaxonBase<?> taxonBase) {
1352
		if (taxonBase == null){
1353
			return null;
1354
		}else if(taxonBase.isInstanceOf(Taxon.class)){
1355
			return CdmBase.deproxy(taxonBase, Taxon.class);
1356
		}else if(taxonBase.isInstanceOf(Synonym.class)){
1357
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
1358
			Taxon acceptedTaxon = synonym.getAcceptedTaxon();
1359
			return acceptedTaxon;
1360
		}else{
1361
			throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase.getClass().getName());
1362
		}
1363
	}
1364

    
1365
	protected Media getImageMedia(String uriString, boolean readMediaData) throws MalformedURLException {
1366
	    return getImageMedia(uriString, null, readMediaData);
1367
	}
1368

    
1369
	/**
1370
	 * Creates
1371
	 * @param uriString
1372
	 * @param readDataFromUrl
1373
	 * @see #READ_MEDIA_DATA
1374
	 * @return
1375
	 * @throws MalformedURLException
1376
	 */
1377
	protected Media getImageMedia(String uriString, String uriStrThumb, boolean readMediaData) throws MalformedURLException {
1378
	    return getImageMedia(uriString, null, uriStrThumb, readMediaData);
1379
	}
1380

    
1381
	protected Media getImageMedia(String uriString, String mediumUriString, String uriStrThumb, boolean readMediaData) throws MalformedURLException {
1382

    
1383
	    if( uriString == null){
1384
			return null;
1385
		} else {
1386

    
1387
			try {
1388
			    Media media = Media.NewInstance();
1389

    
1390
			    MediaRepresentation representation = makeMediaRepresentation(uriString, readMediaData);
1391
		        media.addRepresentation(representation);
1392

    
1393

    
1394
                //thumb
1395
				if (uriStrThumb != null){
1396
				    MediaRepresentation thumbRepresentation = makeMediaRepresentation(uriStrThumb, readMediaData);
1397
	                media.addRepresentation(thumbRepresentation);
1398
				}
1399

    
1400
				if (isNotBlank(mediumUriString) ){
1401
                    MediaRepresentation mediumRepresentation = makeMediaRepresentation(mediumUriString, readMediaData);
1402
                    media.addRepresentation(mediumRepresentation);
1403
				}
1404
                return media;
1405

    
1406
			} catch (URISyntaxException e1) {
1407
				String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " +  uriString;
1408
				logger.warn(message);
1409
				fireWarningEvent(message, "unknown location", 4, 0);
1410
				return null;
1411
			}
1412
		}
1413
	}
1414

    
1415
    private MediaRepresentation makeMediaRepresentation(String uriString, boolean readMediaData) throws URISyntaxException {
1416
        uriString = uriString.replace(" ", "%20");  //replace whitespace
1417
        CdmImageInfo cdmImageInfo = null;
1418
        URI uri = new URI(uriString);
1419

    
1420
        try {
1421
        	if (readMediaData){
1422
        		logger.info("Read media data from: " + uri);
1423
        		cdmImageInfo = getMediaInfoFactory().cdmImageInfo(uri, false);
1424
        	}
1425
        } catch (Exception e) {
1426
        	String message = "An error occurred when trying to read image meta data for " + uri.toString() + ": " +  e.getMessage();
1427
        	logger.warn(message);
1428
        	fireWarningEvent(message, "unknown location", 2, 0);
1429
        }
1430
        ImageFile imageFile = ImageFile.NewInstance(uri, null, cdmImageInfo);
1431

    
1432
        MediaRepresentation representation = MediaRepresentation.NewInstance();
1433

    
1434
        if(cdmImageInfo != null){
1435
        	representation.setMimeType(cdmImageInfo.getMimeType());
1436
        	representation.setSuffix(cdmImageInfo.getSuffix());
1437
        }
1438
        representation.addRepresentationPart(imageFile);
1439
        return representation;
1440
    }
1441

    
1442

    
1443
	/**
1444
	 * Retrieves an Integer value from a result set. If the value is NULL null is returned.
1445
	 * ResultSet.getInt() returns 0 therefore we need a special handling for this case.
1446
	 * @param rs
1447
	 * @param columnName
1448
	 * @return
1449
	 * @throws SQLException
1450
	 */
1451
	protected Integer nullSafeInt(ResultSet rs, String columnName) throws SQLException {
1452
		Object intObject = rs.getObject(columnName);
1453
		if (intObject == null){
1454
			return null;
1455
		}else{
1456
			return Integer.valueOf(intObject.toString());
1457
		}
1458
	}
1459

    
1460
	protected Boolean nullSafeBoolean(ResultSet rs, String columnName) throws SQLException {
1461
		Object bitObject = rs.getObject(columnName);
1462
		if (bitObject == null){
1463
			return null;
1464
		}else{
1465
			return Boolean.valueOf(bitObject.toString());
1466
		}
1467
	}
1468

    
1469
	protected Double nullSafeDouble(ResultSet rs, String columnName) throws SQLException {
1470
		Object doubleObject = rs.getObject(columnName);
1471
		if (doubleObject == null){
1472
			return null;
1473
		}else{
1474
			return Double.valueOf(doubleObject.toString());
1475
		}
1476
	}
1477

    
1478
	protected Float nullSafeFloat(ResultSet rs, String columnName) throws SQLException {
1479
		Object doubleObject = rs.getObject(columnName);
1480
		if (doubleObject == null){
1481
			return null;
1482
		}else{
1483
			return Float.valueOf(doubleObject.toString());
1484
		}
1485
	}
1486

    
1487

    
1488
    /**
1489
     * Converts a given string into an integer. If this is not possible
1490
     * an error is logged in the import result with record location and attribute name.
1491
     *
1492
     * @param state
1493
     * @param strToConvert
1494
     * @param recordLocation
1495
     * @param attributeName
1496
     * @return the converted integer
1497
     */
1498
    protected Integer intFromString(STATE state,
1499
            String strToConvert,
1500
            String recordLocation,
1501
            String attributeName) {
1502

    
1503
        if (strToConvert == null){
1504
            return null;
1505
        }
1506
        try {
1507
            Integer result = Integer.valueOf(strToConvert);
1508
            return result;
1509
        } catch (NumberFormatException e) {
1510
            String message = "Text '%s' could not be transformed into integer number for attribute %s";
1511
            message = String.format(message, strToConvert, attributeName);
1512
            state.getResult().addError(message, e, null, recordLocation);
1513
        }
1514
        return null;
1515
    }
1516

    
1517
    /**
1518
     * Converts a given string into a {@link Double}. If this is not possible
1519
     * an error is logged in the import result with record location and attribute name.
1520
     *
1521
     * @param state
1522
     * @param strToConvert
1523
     * @param recordLocation
1524
     * @param attributeName
1525
     * @return the converted integer
1526
     */
1527
    protected Double doubleFromString(STATE state,
1528
            String strToConvert,
1529
            String recordLocation,
1530
            String attributeName) {
1531

    
1532
        if (strToConvert == null){
1533
            return null;
1534
        }
1535
        try {
1536
            Double result = Double.valueOf(strToConvert);
1537
            return result;
1538
        } catch (NumberFormatException e) {
1539
            String message = "Text '%s' could not be transformed into number of type Double for attribute %s";
1540
            message = String.format(message, strToConvert, attributeName);
1541
            state.getResult().addError(message, e, null, recordLocation);
1542
        }
1543
        return null;
1544
    }
1545

    
1546

    
1547
	/**
1548
	 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1549
	 * @param str
1550
	 * @return
1551
	 */
1552
	protected String NB(String str) {
1553
		if (isBlank(str)){
1554
			return null;
1555
		}else{
1556
			return str;
1557
		}
1558
	}
1559

    
1560
	@Override
1561
    public byte[] getByteArray() {
1562
        // TODO Auto-generated method stub
1563
        return null;
1564
    }
1565

    
1566
	public static TeamOrPersonBase<?> parseAuthorString(String authorName){
1567
        TeamOrPersonBase<?> author = null;
1568
        String[] teamMembers = authorName.split(authorSeparator);
1569
        String lastMember;
1570
        String[] lastMembers;
1571
        Person teamMember;
1572
        if (teamMembers.length>1){
1573
            lastMember = teamMembers[teamMembers.length -1];
1574
            lastMembers = lastMember.split(lastAuthorSeparator);
1575
            teamMembers[teamMembers.length -1] = "";
1576
            author = Team.NewInstance();
1577
            for(String member:teamMembers){
1578
                if (!member.equals("")){
1579
                    teamMember = Person.NewInstance();
1580
                    teamMember.setTitleCache(member, true);
1581
                   ((Team)author).addTeamMember(teamMember);
1582
                }
1583
            }
1584
            if (lastMembers != null){
1585
                for(String member:lastMembers){
1586
                   teamMember = Person.NewInstance();
1587
                   teamMember.setTitleCache(member, true);
1588
                   ((Team)author).addTeamMember(teamMember);
1589
                }
1590
            }
1591

    
1592
        } else {
1593
            teamMembers = authorName.split(lastAuthorSeparator);
1594
            if (teamMembers.length>1){
1595
                author = Team.NewInstance();
1596
                for(String member:teamMembers){
1597
                  teamMember = Person.NewInstance();
1598
                  teamMember.setTitleCache(member, true);
1599
                  ((Team)author).addTeamMember(teamMember);
1600

    
1601
                }
1602
            }else{
1603
                if (isNotBlank(authorName)){
1604
                    author = Person.NewInstance();
1605
                    author.setTitleCache(authorName, true);
1606
                }else{
1607
                    return null;
1608
                }
1609

    
1610
            }
1611
        }
1612
        author.getTitleCache();
1613
        return author;
1614
    }
1615

    
1616
    /**
1617
     * Saves name relations. Needed if a name was parsed and has hybrid parents
1618
     * which will not be saved via cascade.
1619
     */
1620
    protected void saveNameRelations(TaxonName name) {
1621
        for (HybridRelationship rel: name.getHybridChildRelations()){
1622
            getNameService().saveOrUpdate(rel.getParentName());
1623
        }
1624
        for (NameRelationship rel: name.getNameRelations()){
1625
            getNameService().saveOrUpdate(rel.getFromName());
1626
            getNameService().saveOrUpdate(rel.getToName());
1627
        }
1628
    }
1629

    
1630
    public ImportDeduplicationHelper createDeduplicationHelper(STATE state){
1631
        return ImportDeduplicationHelper.NewInstance(this, state);
1632
    }
1633
}
(7-7/65)