Project

General

Profile

Download (56.9 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.URI;
14
import java.net.URISyntaxException;
15
import java.sql.ResultSet;
16
import java.sql.SQLException;
17
import java.util.ArrayList;
18
import java.util.Arrays;
19
import java.util.List;
20
import java.util.Set;
21
import java.util.UUID;
22

    
23
import org.apache.commons.lang.StringUtils;
24
import org.apache.log4j.Logger;
25

    
26
import eu.etaxonomy.cdm.api.service.pager.Pager;
27
import eu.etaxonomy.cdm.common.media.ImageInfo;
28
import eu.etaxonomy.cdm.io.common.mapping.IInputTransformer;
29
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
30
import eu.etaxonomy.cdm.io.markup.MarkupTransformer;
31
import eu.etaxonomy.cdm.model.agent.Person;
32
import eu.etaxonomy.cdm.model.agent.Team;
33
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
34
import eu.etaxonomy.cdm.model.common.AnnotationType;
35
import eu.etaxonomy.cdm.model.common.CdmBase;
36
import eu.etaxonomy.cdm.model.common.DefinedTerm;
37
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
38
import eu.etaxonomy.cdm.model.common.ExtensionType;
39
import eu.etaxonomy.cdm.model.common.ICdmBase;
40
import eu.etaxonomy.cdm.model.common.IOriginalSource;
41
import eu.etaxonomy.cdm.model.common.ISourceable;
42
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
43
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
44
import eu.etaxonomy.cdm.model.common.Language;
45
import eu.etaxonomy.cdm.model.common.Marker;
46
import eu.etaxonomy.cdm.model.common.MarkerType;
47
import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
48
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
49
import eu.etaxonomy.cdm.model.common.TermType;
50
import eu.etaxonomy.cdm.model.common.TermVocabulary;
51
import eu.etaxonomy.cdm.model.description.DescriptionBase;
52
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
53
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
54
import eu.etaxonomy.cdm.model.description.Feature;
55
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
56
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
57
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
58
import eu.etaxonomy.cdm.model.description.State;
59
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
60
import eu.etaxonomy.cdm.model.description.TaxonDescription;
61
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
62
import eu.etaxonomy.cdm.model.description.TextData;
63
import eu.etaxonomy.cdm.model.location.Country;
64
import eu.etaxonomy.cdm.model.location.NamedArea;
65
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
66
import eu.etaxonomy.cdm.model.location.NamedAreaType;
67
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
68
import eu.etaxonomy.cdm.model.media.ImageFile;
69
import eu.etaxonomy.cdm.model.media.Media;
70
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
71
import eu.etaxonomy.cdm.model.name.INonViralName;
72
import eu.etaxonomy.cdm.model.name.Rank;
73
import eu.etaxonomy.cdm.model.name.RankClass;
74
import eu.etaxonomy.cdm.model.name.TaxonName;
75
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
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

    
83
/**
84
 * @author a.mueller
85
 * @created 01.07.2008
86
 */
87
public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE extends ImportStateBase>
88
            extends CdmIoBase<STATE, ImportResult>
89
            implements ICdmImport<CONFIG, STATE>{
90

    
91
    private static final long serialVersionUID = 8730012744209195616L;
92
    private static final Logger logger = Logger.getLogger(CdmImportBase.class);
93

    
94
	protected static final boolean CREATE = true;
95
	protected static final boolean IMAGE_GALLERY = true;
96
	protected static final boolean READ_MEDIA_DATA = true;
97

    
98
	public static final UUID uuidUserDefinedNamedAreaLevelVocabulary = UUID.fromString("255144da-8d95-457e-a327-9752a8f85e5a");
99
	public static final UUID uuidUserDefinedNamedAreaVocabulary = UUID.fromString("b2238399-a3af-4f6d-b7eb-ff5d0899bf1b");
100
	public static final UUID uuidUserDefinedExtensionTypeVocabulary = UUID.fromString("e28c1394-1be8-4847-8b81-ab44eb6d5bc8");
101
	public static final UUID uuidUserDefinedIdentifierTypeVocabulary = UUID.fromString("194b173b-e2c8-49f1-bbfa-d5d51556cf68");
102
	public static final UUID uuidUserDefinedReferenceSystemVocabulary = UUID.fromString("467591a3-10b4-4bf1-9239-f06ece33e90a");
103
	public static final UUID uuidUserDefinedFeatureVocabulary = UUID.fromString("fe5fccb3-a2f2-4b97-b199-6e2743cf1627");
104
	public static final UUID uuidUserDefinedMeasurementUnitVocabulary = UUID.fromString("d5e72bb7-f312-4080-bb86-c695d04a6e66");
105
	public static final UUID uuidUserDefinedStatisticalMeasureVocabulary = UUID.fromString("62a89836-c730-4b4f-a904-3d859dbfc400");
106
	public static final UUID uuidUserDefinedStateVocabulary = UUID.fromString("f7cddb49-8392-4db1-8640-65b48a0e6d13");
107
	public static final UUID uuidUserDefinedTaxonRelationshipTypeVocabulary = UUID.fromString("31a324dc-408d-4877-891f-098db21744c6");
108
	public static final UUID uuidUserDefinedAnnotationTypeVocabulary = UUID.fromString("cd9ecdd2-9cae-4890-9032-ad83293ae883");
109
	public static final UUID uuidUserDefinedMarkerTypeVocabulary = UUID.fromString("5f02a261-fd7d-4fce-bbe4-21472de8cd51");
110
	public static final UUID uuidUserDefinedRankVocabulary = UUID.fromString("4dc57931-38e2-46c3-974d-413b087646ba");
111
	public static final UUID uuidUserDefinedPresenceAbsenceVocabulary = UUID.fromString("6b8a2581-1471-4ea6-b8ad-b2d931cfbc23");
112

    
113
	public static final UUID uuidUserDefinedModifierVocabulary = UUID.fromString("2a8b3838-3a95-49ea-9ab2-3049614b5884");
114
	public static final UUID uuidUserDefinedKindOfUnitVocabulary = UUID.fromString("e7c5deb2-f485-4a66-9104-0c5398efd481");
115

    
116
	public static final UUID uuidUserDefinedLanguageVocabulary = UUID.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
117

    
118

    
119
	private static final String UuidOnly = "UUIDOnly";
120
	private static final String UuidLabel = "UUID or label";
121
	private static final String UuidLabelAbbrev = "UUID, label or abbreviation";
122
	private static final String UuidAbbrev = "UUID or abbreviation";
123

    
124
	private final static String authorSeparator = ", ";
125
    private final static String lastAuthorSeparator = " & ";
126

    
127
	public enum TermMatchMode{
128
		UUID_ONLY(0, UuidOnly)
129
		,UUID_LABEL(1, UuidLabel)
130
		,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev)
131
		,UUID_ABBREVLABEL(3, UuidAbbrev)
132
		;
133

    
134

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

    
157

    
158
	}
159

    
160
    @Override
161
    protected ImportResult getNoDataResult(STATE state) {
162
        return ImportResult.NewNoDataInstance();
163
    }
164

    
165
    @Override
166
    protected ImportResult getDefaultResult(STATE state) {
167
        return ImportResult.NewInstance();
168
    }
169

    
170
	protected Classification makeTree(STATE state, Reference reference){
171
		String treeName = "Classification (Import)";
172
		if (reference != null && StringUtils.isNotBlank(reference.getTitleCache())){
173
			treeName = reference.getTitleCache();
174
		}
175
		Classification tree = Classification.NewInstance(treeName);
176
		tree.setReference(reference);
177

    
178

    
179
		// use defined uuid for first tree
180
		CONFIG config = (CONFIG)state.getConfig();
181
		if (state.countTrees() < 1 ){
182
			tree.setUuid(config.getClassificationUuid());
183
		}
184
		getClassificationService().save(tree);
185
		state.putTree(reference, tree);
186
		return tree;
187
	}
188

    
189

    
190
	/**
191
	 * Alternative memory saving method variant of
192
	 * {@link #makeTree(STATE state, Reference ref)} which stores only the
193
	 * UUID instead of the full tree in the <code>ImportStateBase</code> by
194
	 * using <code>state.putTreeUuid(ref, tree);</code>
195
	 *
196
	 * @param state
197
	 * @param ref
198
	 * @return
199
	 */
200
	protected Classification makeTreeMemSave(STATE state, Reference ref){
201
		String treeName = "Classification (Import)";
202
		if (ref != null && StringUtils.isNotBlank(ref.getTitleCache())){
203
			treeName = ref.getTitleCache();
204
		}
205
		Classification tree = Classification.NewInstance(treeName);
206
		tree.setReference(ref);
207

    
208

    
209
		// use defined uuid for first tree
210
		CONFIG config = (CONFIG)state.getConfig();
211
		if (state.countTrees() < 1 ){
212
			tree.setUuid(config.getClassificationUuid());
213
		}
214
		getClassificationService().save(tree);
215
		state.putTreeUuid(ref, tree);
216
		return tree;
217
	}
218

    
219

    
220

    
221

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

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

    
269

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

    
290
	/**
291
	 * @see #getMarkerType(ImportStateBase, UUID, String, String, String, TermVocabulary, Language)
292
	 */
293
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev){
294
		return getMarkerType(state, uuid, label, description, labelAbbrev, null, null);
295
	}
296

    
297
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MarkerType> voc){
298
	    return this.getMarkerType(state, uuid, label, description, labelAbbrev, voc, null);
299
	}
300

    
301

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

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

    
345
				voc.addTerm(annotationType);
346
				getTermService().save(annotationType);
347
			}
348
			state.putAnnotationType(annotationType);
349
		}
350
		return annotationType;
351
	}
352

    
353

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

    
375
	}
376

    
377

    
378

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

    
404
	}
405

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

    
421
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
422
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode, null);
423
	}
424

    
425

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

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

    
448

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

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

    
477

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

    
514

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

    
533
		return result;
534
	}
535

    
536

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

    
549

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

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

    
595

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

    
633
	/**
634
	 * Returns a feature if it exists, null otherwise.
635
	 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
636
	 * @param state
637
	 * @param uuid
638
	 * @return
639
	 */
640
	protected Feature getFeature(STATE state, UUID uuid){
641
		return getFeature(state, uuid, null, null, null, null);
642
	}
643

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

    
678
	protected DefinedTerm getKindOfUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
679
		if (uuid == null){
680
		    uuid = UUID.randomUUID();
681
		}
682
		DefinedTerm unit = state.getKindOfUnit(uuid);
683
		if (unit == null){
684
			unit = (DefinedTerm)getTermService().find(uuid);
685
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
686
				unit = DefinedTerm.NewKindOfUnitInstance(description, label, labelAbbrev);
687
				unit.setUuid(uuid);
688
				if (voc == null){
689
					boolean isOrdered = false;
690
					voc = getVocabulary(TermType.KindOfUnit, uuidUserDefinedKindOfUnitVocabulary, "User defined vocabulary for kind-of-units", "User Defined Measurement kind-of-units", null, null, isOrdered, unit);
691
				}
692
				voc.addTerm(unit);
693
				getTermService().save(unit);
694
			}
695
			state.putKindOfUnit(unit);
696
		}
697
		return unit;
698
	}
699

    
700
	/**
701
	 * Returns a {@link MeasurementUnit} for a given uuid by first checking if the uuid has already been used in this import, if not
702
	 * checking if the {@link MeasurementUnit} exists in the database, if not creating it anew (with vocabulary etc.).
703
	 * If label, text and labelAbbrev are all <code>null</code> no {@link MeasurementUnit} is created.
704
	 * @param state
705
	 * @param uuid
706
	 * @param label
707
	 * @param text
708
	 * @param labelAbbrev
709
	 * @return
710
	 */
711
	protected MeasurementUnit getMeasurementUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MeasurementUnit> voc){
712
		if (uuid == null){
713
			return null;
714
		}
715
		MeasurementUnit unit = state.getMeasurementUnit(uuid);
716
		if (unit == null){
717
			unit = (MeasurementUnit)getTermService().find(uuid);
718
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
719
				unit = MeasurementUnit.NewInstance(description, label, labelAbbrev);
720
				unit.setUuid(uuid);
721
				if (voc == null){
722
					boolean isOrdered = false;
723
					voc = getVocabulary(TermType.MeasurementUnit, uuidUserDefinedMeasurementUnitVocabulary, "User defined vocabulary for measurement units", "User Defined Measurement Units", null, null, isOrdered, unit);
724
				}
725
				voc.addTerm(unit);
726
				getTermService().save(unit);
727
			}
728
			state.putMeasurementUnit(unit);
729
		}
730
		return unit;
731
	}
732

    
733
	/**
734
	 * Returns a {@link StatisticalMeasure} for a given uuid by first checking if the uuid has already been used in this import, if not
735
	 * checking if the {@link StatisticalMeasure} exists in the database, if not creating it anew (with vocabulary etc.).
736
	 * If label, text and labelAbbrev are all <code>null</code> no {@link StatisticalMeasure} is created.
737
	 * @param state
738
	 * @param uuid
739
	 * @param label
740
	 * @param text
741
	 * @param labelAbbrev
742
	 * @return
743
	 */
744
	protected StatisticalMeasure getStatisticalMeasure(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<StatisticalMeasure> voc){
745
		if (uuid == null){
746
			return null;
747
		}
748
		StatisticalMeasure statisticalMeasure = state.getStatisticalMeasure(uuid);
749
		if (statisticalMeasure == null){
750
			statisticalMeasure = (StatisticalMeasure)getTermService().find(uuid);
751
			if (statisticalMeasure == null && ! hasNoLabel(label, description, labelAbbrev)){
752
				statisticalMeasure = StatisticalMeasure.NewInstance(description, label, labelAbbrev);
753
				statisticalMeasure.setUuid(uuid);
754
				if (voc == null){
755
					boolean isOrdered = false;
756
					voc = getVocabulary(TermType.StatisticalMeasure, uuidUserDefinedStatisticalMeasureVocabulary, "User defined vocabulary for statistical measures", "User Defined Statistical Measures", null, null, isOrdered, statisticalMeasure);
757
				}
758
				voc.addTerm(statisticalMeasure);
759
				getTermService().save(statisticalMeasure);
760
			}
761
			state.putStatisticalMeasure(statisticalMeasure);
762
		}
763
		return statisticalMeasure;
764
	}
765

    
766
	/**
767
	 * Returns a {@link Modifier} for a given uuid by first checking if the uuid has already been used in this import, if not
768
	 * checking if the {@link Modifier} exists in the database, if not creating it anew (with vocabulary etc.).
769
	 * If label, text and labelAbbrev are all <code>null</code> no {@link Modifier} is created.
770
	 * @param state
771
	 * @param uuid
772
	 * @param label
773
	 * @param text
774
	 * @param labelAbbrev
775
	 * @return
776
	 */
777
	protected DefinedTerm getModifier(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
778
		if (uuid == null){
779
			return null;
780
		}
781
		DefinedTerm modifier = state.getModifier(uuid);
782
		if (modifier == null){
783
			modifier = (DefinedTerm)getTermService().find(uuid);
784
			if (modifier == null && ! hasNoLabel(label, description, labelAbbrev)){
785
				modifier = DefinedTerm.NewModifierInstance(description, label, labelAbbrev);
786
				modifier.setUuid(uuid);
787
				if (voc == null){
788
					boolean isOrdered = false;
789
					voc = getVocabulary(TermType.Modifier, uuidUserDefinedModifierVocabulary, "User defined vocabulary for modifier", "User Defined Modifier", null, null, isOrdered, modifier);
790
				}
791
				voc.addTerm(modifier);
792
				getTermService().save(modifier);
793
			}
794
			state.putModifier(modifier);
795
		}
796
		return modifier;
797
	}
798

    
799
	/**
800
	 * Returns a taxon relationship type for a given uuid by first checking if the uuid has already been used in this import, if not
801
	 * checking if the taxon relationship type exists in the database, if not creating it anew (with vocabulary etc.).
802
	 * If label, text and labelAbbrev are all <code>null</code> no taxon relationship type is created.
803
	 * @param state
804
	 * @param uuid
805
	 * @param label
806
	 * @param text
807
	 * @param labelAbbrev
808
	 * @return
809
	 */
810
	protected TaxonRelationshipType getTaxonRelationshipType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<TaxonRelationshipType> voc){
811
		if (uuid == null){
812
			return null;
813
		}
814
		TaxonRelationshipType relType = state.getTaxonRelationshipType(uuid);
815
		if (relType == null){
816
			relType = (TaxonRelationshipType)getTermService().find(uuid);
817
			if (relType == null && ! hasNoLabel(label, text, labelAbbrev)){
818
				relType = TaxonRelationshipType.NewInstance(text, label, labelAbbrev, false, false);
819
				relType.setUuid(uuid);
820
				if (voc == null){
821
					boolean isOrdered = true;
822
					voc = getVocabulary(TermType.TaxonRelationshipType, uuidUserDefinedTaxonRelationshipTypeVocabulary, "User defined vocabulary for taxon relationship types", "User Defined Taxon Relationship Types", null, null, isOrdered, relType);
823
				}
824
				voc.addTerm(relType);
825
				getTermService().save(relType);
826
			}
827
			state.putTaxonRelationshipType(relType);
828
		}
829
		return relType;
830
	}
831

    
832
	private boolean hasNoLabel(String label, String text, String labelAbbrev) {
833
		return label == null && text == null && labelAbbrev == null;
834
	}
835

    
836
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm){
837
	    return getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, null);
838
	}
839

    
840

    
841
	/**
842
	 * Returns a presence term for a given uuid by first ...
843
	 * @param state
844
	 * @param uuid
845
	 * @param label
846
	 * @param text
847
	 * @param labelAbbrev
848
	 * @return
849
	 */
850
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm, TermVocabulary<PresenceAbsenceTerm> voc){
851
		if (uuid == null){
852
			return null;
853
		}
854
		PresenceAbsenceTerm presenceTerm = state.getPresenceAbsenceTerm(uuid);
855
		if (presenceTerm == null){
856
			presenceTerm = (PresenceAbsenceTerm)getTermService().find(uuid);
857
			if (presenceTerm == null){
858
				presenceTerm = PresenceAbsenceTerm.NewPresenceInstance(text, label, labelAbbrev);
859
				presenceTerm.setUuid(uuid);
860
				presenceTerm.setAbsenceTerm(isAbsenceTerm);
861
				//set vocabulary ; FIXME use another user-defined vocabulary
862
				if (voc == null){
863
                    boolean isOrdered = true;
864
                    voc = getVocabulary(TermType.PresenceAbsenceTerm, uuidUserDefinedPresenceAbsenceVocabulary, "User defined vocabulary for distribution status", "User Defined Distribution Status", null, null, isOrdered, presenceTerm);
865
                }
866
				voc.addTerm(presenceTerm);
867
				getTermService().save(presenceTerm);
868
			}
869
			state.putPresenceAbsenceTerm(presenceTerm);
870
		}
871
		return presenceTerm;
872
	}
873

    
874
	/**
875
	 * Returns a language for a given uuid by first ...
876
	 * @param state
877
	 * @param uuid
878
	 * @param label
879
	 * @param text
880
	 * @param labelAbbrev
881
	 * @return
882
	 */
883
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
884
		return getLanguage(state, uuid, label, text, labelAbbrev, null);
885
	}
886

    
887
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
888
		if (uuid == null){
889
			return null;
890
		}
891
		Language language = state.getLanguage(uuid);
892
		if (language == null){
893
			language = (Language)getTermService().find(uuid);
894
			if (language == null){
895
				language = Language.NewInstance(text, label, labelAbbrev);
896

    
897
				language.setUuid(uuid);
898
				if (voc == null){
899
					UUID uuidLanguageVoc = uuidUserDefinedLanguageVocabulary;
900
					boolean isOrdered = false;
901
					voc = getVocabulary(TermType.Language, uuidLanguageVoc, "User defined languages", "User defined languages", "User defined languages", null, isOrdered, language);
902
				}
903
				//set vocabulary ; FIXME use another user-defined vocabulary
904

    
905
				voc.addTerm(language);
906
				getTermService().save(language);
907
			}
908
			state.putLanguage(language);
909
		}
910
		return language;
911
	}
912

    
913

    
914
	/**
915
	 * @param uuid
916
	 * @return
917
	 *
918
	 */
919
	protected <T extends DefinedTermBase> TermVocabulary<T> getVocabulary(TermType termType, UUID uuid, String description, String label, String abbrev, URI termSourceUri, boolean isOrdered, T type) {
920
		List<String> propPath = Arrays.asList(new String[]{"terms"});
921
		TermVocabulary<T> voc = getVocabularyService().load(uuid, propPath);
922
		if (voc == null){
923
			if (isOrdered){
924
				voc = OrderedTermVocabulary.NewInstance(termType, description, label, abbrev, termSourceUri);
925
			}else{
926
				voc = TermVocabulary.NewInstance(termType, description, label, abbrev, termSourceUri);
927
			}
928
			voc.setUuid(uuid);
929
			getVocabularyService().save(voc);
930
		}
931
		return voc;
932
	}
933

    
934
	/**
935
	 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
936
	 * If cdmBase is not sourceable nothing happens.
937
	 * TODO Move to DbImportBase once this exists.
938
	 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
939
	 * @param rs
940
	 * @param cdmBase
941
	 * @param dbIdAttribute
942
	 * @param namespace
943
	 * @param citation
944
	 * @throws SQLException
945
	 */
946
	public void addOriginalSource(ICdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation)  {
947
		if (cdmBase instanceof ISourceable ){
948
			IOriginalSource source;
949
			ISourceable sourceable = (ISourceable<?>)cdmBase;
950
			Object id = idAttributeValue;
951
			String strId = String.valueOf(id);
952
			String microCitation = null;
953
			OriginalSourceType type = OriginalSourceType.Import;
954
			if (cdmBase instanceof IdentifiableEntity){
955
				source = IdentifiableSource.NewInstance(type, strId, namespace, citation, microCitation);
956
			}else if (cdmBase instanceof DescriptionElementBase){
957
				source = DescriptionElementSource.NewInstance(type, strId, namespace, citation, microCitation);
958
			}else{
959
				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.");
960
				return;
961
			}
962
			sourceable.addSource(source);
963
		}else if (cdmBase != null){
964
			logger.warn("Sourced object does not implement ISourceable: " + cdmBase.getClass() + "," + cdmBase.getUuid());
965
		}else{
966
			logger.warn("Sourced object is null");
967
		}
968
	}
969

    
970
	/**
971
	 * @see #addOriginalSource(CdmBase, Object, String, Reference)
972
	 * @param rs
973
	 * @param cdmBase
974
	 * @param dbIdAttribute
975
	 * @param namespace
976
	 * @param citation
977
	 * @throws SQLException
978
	 */
979
	public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
980
		Object id = rs.getObject(dbIdAttribute);
981
		addOriginalSource(cdmBase, id, namespace, citation);
982
	}
983

    
984

    
985
	/**
986
	 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
987
	 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
988
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
989
	 * the parent.
990
	 * @param parentTaxon
991
	 * @param childTaxon
992
	 */
993
	protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
994
		if (parentTaxon == null){
995
			logger.warn("Parent taxon is null. Missing name parts can not be taken from parent");
996
			return;
997
		}
998
		INonViralName parentName = parentTaxon.getName();
999
		INonViralName childName = childTaxon.getName();
1000
		fillMissingEpithets(parentName, childName);
1001
	}
1002

    
1003
	/**
1004
	 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
1005
	 * or <i>species</i> respectively the according epithets are taken from the parent name.
1006
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
1007
	 * the parent.
1008
	 * @param parentTaxon
1009
	 * @param childTaxon
1010
	 */
1011
	protected void fillMissingEpithets(INonViralName parentName, INonViralName childName) {
1012
		if (StringUtils.isBlank(childName.getGenusOrUninomial()) && childName.getRank().isLower(Rank.GENUS()) ){
1013
			childName.setGenusOrUninomial(parentName.getGenusOrUninomial());
1014
		}
1015

    
1016
		if (StringUtils.isBlank(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
1017
			childName.setSpecificEpithet(parentName.getSpecificEpithet());
1018
		}
1019
		if (childName.isAutonym() && childName.getCombinationAuthorship() == null && childName.getBasionymAuthorship() == null ){
1020
			childName.setCombinationAuthorship(parentName.getCombinationAuthorship());
1021
			childName.setBasionymAuthorship(parentName.getBasionymAuthorship());
1022
		}
1023
	}
1024

    
1025
	/**
1026
	 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
1027
	 * an arbitrary one is chosen.
1028
	 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
1029
	 * is <code>true</code>.
1030
	 * @param createNewIfNotExists
1031
	 * @param isImageGallery if true only taxon description being image galleries are considered.
1032
	 * If false only taxon description being no image galleries are considered.
1033
	 * @return
1034
	 */
1035
	public TaxonNameDescription getTaxonNameDescription(TaxonName name, boolean isImageGallery, boolean createNewIfNotExists) {
1036
		Reference ref = null;
1037
		return getTaxonNameDescription(name, ref, isImageGallery, createNewIfNotExists);
1038
	}
1039

    
1040
	/**
1041
	 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
1042
	 * Only matches a description if the given reference is a source of the description.<BR>
1043
	 * If a new description is created the given reference will be added as a source.
1044
	 *
1045
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1046
	 */
1047
	public TaxonNameDescription getTaxonNameDescription(TaxonName name, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1048
		TaxonNameDescription result = null;
1049
		Set<TaxonNameDescription> descriptions= name.getDescriptions();
1050
		for (TaxonNameDescription description : descriptions){
1051
			if (description.isImageGallery() == isImageGallery){
1052
				if (hasCorrespondingSource(ref, description)){
1053
					result = description;
1054
					break;
1055
				}
1056
			}
1057
		}
1058
		if (result == null && createNewIfNotExists){
1059
			result = TaxonNameDescription.NewInstance(name);
1060
			result.setImageGallery(isImageGallery);
1061
			if (ref != null){
1062
				result.addImportSource(null, null, ref, null);
1063
			}
1064
		}
1065
		return result;
1066
	}
1067

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

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

    
1113
	/**
1114
	 * Returns the default taxon description. If no default taxon description exists,
1115
	 * a new one is created, the default flag is set to true and, if a source is passed,
1116
	 * it is added to the new description. Otherwise source has no influence.
1117
	 * @param taxon
1118
	 * @param isImageGallery
1119
	 * @param createNewIfNotExists
1120
	 * @param source
1121
	 * @return the default description
1122
	 * @see #getTaxonDescription(Taxon, Reference, boolean, boolean)
1123
	 */
1124
	public TaxonDescription getDefaultTaxonDescription(Taxon taxon, boolean isImageGallery,
1125
            boolean createNewIfNotExists, Reference source) {
1126
        TaxonDescription result = null;
1127
        Set<TaxonDescription> descriptions= taxon.getDescriptions();
1128
        for (TaxonDescription description : descriptions){
1129
            if (description.isImageGallery() == isImageGallery){
1130
                if (description.isDefault()){
1131
                    result = description;
1132
                    break;
1133
                }
1134
            }
1135
        }
1136
        if (result == null && createNewIfNotExists){
1137
            result = TaxonDescription.NewInstance(taxon);
1138
            result.setImageGallery(isImageGallery);
1139
            result.setDefault(true);
1140
            if (source != null){
1141
                result.addImportSource(null, null, source, null);
1142
            }
1143
        }
1144
        return result;
1145
    }
1146

    
1147
    /**
1148
     * Returns the taxon description with marked as <code>true</code> with the given marker type.
1149
     * If createNewIfNotExists a new description is created if it does not yet exist.
1150
     * For the new description the source and the title are set if not <code>null</code>.
1151
     * @param taxon
1152
     * @param markerType
1153
     * @param isImageGallery
1154
     * @param createNewIfNotExists
1155
     * @param source
1156
     * @param title
1157
     * @return the existing or new taxon description
1158
     */
1159
   public TaxonDescription getMarkedTaxonDescription(Taxon taxon, MarkerType markerType, boolean isImageGallery,
1160
            boolean createNewIfNotExists, Reference source, String title) {
1161
        TaxonDescription result = null;
1162
        Set<TaxonDescription> descriptions= taxon.getDescriptions();
1163
        for (TaxonDescription description : descriptions){
1164
            if (description.isImageGallery() == isImageGallery){
1165
                if (description.hasMarker(markerType, true)){
1166
                    result = description;
1167
                    break;
1168
                }
1169
            }
1170
        }
1171
        if (result == null && createNewIfNotExists){
1172
            result = TaxonDescription.NewInstance(taxon);
1173
            result.setImageGallery(isImageGallery);
1174
            result.addMarker(Marker.NewInstance(markerType, true));
1175
            if (source != null){
1176
                result.addImportSource(null, null, source, null);
1177
            }
1178
            if (isNotBlank(title)){
1179
                result.setTitleCache(title, true);
1180
            }
1181
        }
1182
        return result;
1183
    }
1184

    
1185

    
1186
	/**
1187
	 * Returns the {@link SpecimenDescription specimen description} for a {@link SpecimenOrObservationBase specimen or observation}.
1188
	 * If there are multiple specimen descriptions an arbitrary one is chosen.
1189
	 * If no specimen description exists, a new one is created if <code>createNewIfNotExists</code> is <code>true</code>.
1190
	 * @param createNewIfNotExists
1191
	 * @param isImageGallery if true only specimen description being image galleries are considered.
1192
	 * If false only specimen description being no image galleries are considered.
1193
	 * @return
1194
	 */
1195
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, boolean isImageGallery, boolean createNewIfNotExists) {
1196
		Reference ref = null;
1197
		return getSpecimenDescription(specimen, ref, isImageGallery, createNewIfNotExists);
1198
	}
1199

    
1200
	/**
1201
	 * Like {@link #getSpecimenDescription(SpecimenOrObservationBase, boolean, boolean)}
1202
	 * Only matches a description if the given reference is a source of the description.<BR>
1203
	 * If a new description is created the given reference will be added as a source.
1204
	 *
1205
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1206
	 */
1207
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1208
		SpecimenDescription result = null;
1209
		Set<SpecimenDescription> descriptions= specimen.getDescriptions();
1210
		for (SpecimenDescription description : descriptions){
1211
			if (description.isImageGallery() == isImageGallery){
1212
				if (hasCorrespondingSource(ref, description)){
1213
					result = description;
1214
					break;
1215
				}
1216
			}
1217
		}
1218
		if (result == null && createNewIfNotExists){
1219
			result = SpecimenDescription.NewInstance(specimen);
1220
			result.setImageGallery(isImageGallery);
1221
			if (ref != null){
1222
				result.addImportSource(null, null, ref, null);
1223
			}
1224
		}
1225
		return result;
1226
	}
1227

    
1228

    
1229
	/**
1230
	 * Returns the textdata that holds general information about a feature for a taxon description.
1231
	 * This is mainly necessary for descriptions that have more than one description element for
1232
	 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
1233
	 * for hierarchical features where no description element exists for a higher hierarchy level.
1234
	 * Example: the description feature has subfeatures. But some information like authorship, figures,
1235
	 * sources need to be added to the description itself.
1236
	 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
1237
	 * there will be a boolean marker in the TextData class itself.
1238
	 *
1239
	 * @param state
1240
	 * @param feature
1241
	 * @param taxon
1242
	 * @param ref
1243
	 * @param createIfNotExists
1244
	 * @return
1245
	 */
1246
	protected TextData getFeaturePlaceholder(STATE state, DescriptionBase<?> description, Feature feature, boolean createIfNotExists) {
1247
		UUID featurePlaceholderUuid = MarkupTransformer.uuidMarkerFeaturePlaceholder;
1248
		for (DescriptionElementBase element : description.getElements()){
1249
			if (element.isInstanceOf(TextData.class)){
1250
				TextData textData = CdmBase.deproxy(element, TextData.class);
1251
				if (textData.getFeature() == null || ! textData.getFeature().equals(feature)){
1252
					continue;
1253
				}
1254
				for (Marker marker : textData.getMarkers()){
1255
					MarkerType markerType = marker.getMarkerType();
1256
					if (markerType != null &&
1257
							markerType.getUuid().equals(featurePlaceholderUuid) &&
1258
							marker.getValue() == true){
1259
						return textData;
1260
					}
1261
				}
1262
			}
1263
		}
1264
		if (createIfNotExists){
1265
			TextData newPlaceholder = TextData.NewInstance(feature);
1266
			MarkerType placeholderMarkerType = getMarkerType(state, featurePlaceholderUuid, "Feature Placeholder", "Feature Placeholder", null);
1267
			Marker marker = Marker.NewInstance(placeholderMarkerType, true);
1268
			newPlaceholder.addMarker(marker);
1269
			description.addElement(newPlaceholder);
1270
			return newPlaceholder;
1271
		}else{
1272
			return null;
1273
		}
1274
	}
1275

    
1276

    
1277

    
1278
	/**
1279
	 * Returns true, if this description has a source with a citation equal to the given reference.
1280
	 * Returns true if the given reference is null.
1281
	 * @param ref
1282
	 * @param description
1283
	 */
1284
	private boolean hasCorrespondingSource(Reference ref, DescriptionBase<?> description) {
1285
		if (ref != null){
1286
			for (IdentifiableSource source : description.getSources()){
1287
				if (ref.equals(source.getCitation())){
1288
					return true;
1289
				}
1290
			}
1291
			return false;
1292
		}
1293
		return true;
1294

    
1295
	}
1296

    
1297

    
1298
	/**
1299
	 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
1300
	 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
1301
	 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
1302
	 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
1303
	 * same secundum reference is returned. If no such single taxon exists an
1304
	 * {@link IllegalStateException illegal state exception} is thrown.
1305
	 * @param taxonBase
1306
	 * @return
1307
	 */
1308
	protected Taxon getAcceptedTaxon(TaxonBase<?> taxonBase) {
1309
		if (taxonBase == null){
1310
			return null;
1311
		}else if(taxonBase.isInstanceOf(Taxon.class)){
1312
			return CdmBase.deproxy(taxonBase, Taxon.class);
1313
		}else if(taxonBase.isInstanceOf(Synonym.class)){
1314
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
1315
			Taxon acceptedTaxon = synonym.getAcceptedTaxon();
1316
			return acceptedTaxon;
1317
		}else{
1318
			throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase.getClass().getName());
1319
		}
1320
	}
1321

    
1322
	protected Media getImageMedia(String uriString, boolean readMediaData) throws MalformedURLException {
1323
	    return getImageMedia(uriString, null, readMediaData);
1324
	}
1325

    
1326
	/**
1327
	 * Creates
1328
	 * @param uriString
1329
	 * @param readDataFromUrl
1330
	 * @see #READ_MEDIA_DATA
1331
	 * @return
1332
	 * @throws MalformedURLException
1333
	 */
1334
	protected Media getImageMedia(String uriString, String uriStrThumb, boolean readMediaData) throws MalformedURLException {
1335
		if( uriString == null){
1336
			return null;
1337
		} else {
1338
			uriString = uriString.replace(" ", "%20");  //replace whitespace
1339
			try {
1340
			    ImageInfo imageInfo = null;
1341
				URI uri = new URI(uriString);
1342

    
1343
                try {
1344
					if (readMediaData){
1345
						logger.info("Read media data from: " + uri);
1346
						imageInfo = ImageInfo.NewInstance(uri, 0);
1347
					}
1348
				} catch (Exception e) {
1349
					String message = "An error occurred when trying to read image meta data for " + uri.toString() + ": " +  e.getMessage();
1350
					logger.warn(message);
1351
					fireWarningEvent(message, "unknown location", 2, 0);
1352
				}
1353
				ImageFile imageFile = ImageFile.NewInstance(uri, null, imageInfo);
1354

    
1355
				MediaRepresentation representation = MediaRepresentation.NewInstance();
1356

    
1357
				if(imageInfo != null){
1358
					representation.setMimeType(imageInfo.getMimeType());
1359
					representation.setSuffix(imageInfo.getSuffix());
1360
				}
1361
				representation.addRepresentationPart(imageFile);
1362
				Media media = Media.NewInstance();
1363
                media.addRepresentation(representation);
1364

    
1365
				if (uriStrThumb != null){
1366
				    ImageInfo imageInfoThumb = null;
1367
	                uriStrThumb = uriStrThumb.replace(" ", "%20");  //replace whitespace
1368
	                URI uriThumb = new URI(uriStrThumb);
1369
	                try {
1370
	                    if (readMediaData){
1371
	                        logger.info("Read media data from: " + uriThumb);
1372
	                        imageInfoThumb = ImageInfo.NewInstance(uriThumb, 0);
1373
	                    }
1374
	                } catch (Exception e) {
1375
	                    String message = "An error occurred when trying to read image meta data for " + uriThumb.toString() + ": " +  e.getMessage();
1376
	                    logger.warn(message);
1377
	                    fireWarningEvent(message, "unknown location", 2, 0);
1378
	                }
1379

    
1380
	                ImageFile imageFileFhumb = ImageFile.NewInstance(uriThumb, null, imageInfoThumb);
1381
				    MediaRepresentation reprThumb = MediaRepresentation.NewInstance();
1382
				    if(imageInfoThumb != null){
1383
				        reprThumb.setMimeType(imageInfoThumb.getMimeType());
1384
				        reprThumb.setSuffix(imageInfoThumb.getSuffix());
1385
	                }
1386
				    reprThumb.addRepresentationPart(imageFileFhumb);
1387
				    media.addRepresentation(reprThumb);
1388
				}
1389

    
1390
				return media;
1391
			} catch (URISyntaxException e1) {
1392
				String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " +  uriString;
1393
				logger.warn(message);
1394
				fireWarningEvent(message, "unknown location", 4, 0);
1395
				return null;
1396
			}
1397
		}
1398
	}
1399

    
1400

    
1401
	/**
1402
	 * Retrieves an Integer value from a result set. If the value is NULL null is returned.
1403
	 * ResultSet.getInt() returns 0 therefore we need a special handling for this case.
1404
	 * @param rs
1405
	 * @param columnName
1406
	 * @return
1407
	 * @throws SQLException
1408
	 */
1409
	protected Integer nullSafeInt(ResultSet rs, String columnName) throws SQLException {
1410
		Object intObject = rs.getObject(columnName);
1411
		if (intObject == null){
1412
			return null;
1413
		}else{
1414
			return Integer.valueOf(intObject.toString());
1415
		}
1416
	}
1417

    
1418
	protected Boolean nullSafeBoolean(ResultSet rs, String columnName) throws SQLException {
1419
		Object bitObject = rs.getObject(columnName);
1420
		if (bitObject == null){
1421
			return null;
1422
		}else{
1423
			return Boolean.valueOf(bitObject.toString());
1424
		}
1425
	}
1426

    
1427
	protected Double nullSafeDouble(ResultSet rs, String columnName) throws SQLException {
1428
		Object doubleObject = rs.getObject(columnName);
1429
		if (doubleObject == null){
1430
			return null;
1431
		}else{
1432
			return Double.valueOf(doubleObject.toString());
1433
		}
1434
	}
1435

    
1436
	protected Float nullSafeFloat(ResultSet rs, String columnName) throws SQLException {
1437
		Object doubleObject = rs.getObject(columnName);
1438
		if (doubleObject == null){
1439
			return null;
1440
		}else{
1441
			return Float.valueOf(doubleObject.toString());
1442
		}
1443
	}
1444

    
1445

    
1446
	/**
1447
	 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1448
	 * @param str
1449
	 * @return
1450
	 */
1451
	protected String NB(String str) {
1452
		if (StringUtils.isBlank(str)){
1453
			return null;
1454
		}else{
1455
			return str;
1456
		}
1457
	}
1458

    
1459
	@Override
1460
    public byte[] getByteArray() {
1461
        // TODO Auto-generated method stub
1462
        return null;
1463
    }
1464

    
1465
	public static TeamOrPersonBase<?> parseAuthorString(String authorName){
1466
        TeamOrPersonBase<?> author = null;
1467
        String[] teamMembers = authorName.split(authorSeparator);
1468
        String lastMember;
1469
        String[] lastMembers;
1470
        Person teamMember;
1471
        if (teamMembers.length>1){
1472
            lastMember = teamMembers[teamMembers.length -1];
1473
            lastMembers = lastMember.split(lastAuthorSeparator);
1474
            teamMembers[teamMembers.length -1] = "";
1475
            author = Team.NewInstance();
1476
            for(String member:teamMembers){
1477
                if (!member.equals("")){
1478
                    teamMember = Person.NewInstance();
1479
                    teamMember.setTitleCache(member, true);
1480
                   ((Team)author).addTeamMember(teamMember);
1481
                }
1482
            }
1483
            if (lastMembers != null){
1484
                for(String member:lastMembers){
1485
                   teamMember = Person.NewInstance();
1486
                   teamMember.setTitleCache(member, true);
1487
                   ((Team)author).addTeamMember(teamMember);
1488
                }
1489
            }
1490

    
1491
        } else {
1492
            teamMembers = authorName.split(lastAuthorSeparator);
1493
            if (teamMembers.length>1){
1494
                author = Team.NewInstance();
1495
                for(String member:teamMembers){
1496
                  teamMember = Person.NewInstance();
1497
                  teamMember.setTitleCache(member, true);
1498
                  ((Team)author).addTeamMember(teamMember);
1499

    
1500
                }
1501
            }else{
1502
                author = Person.NewInstance();
1503
                author.setTitleCache(authorName, true);
1504
            }
1505
        }
1506
        author.getTitleCache();
1507
        return author;
1508
    }
1509

    
1510

    
1511
}
(7-7/63)