Project

General

Profile

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

    
117

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

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

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

    
133

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

    
156

    
157
	}
158

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

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

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

    
177

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

    
188

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

    
207

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

    
218

    
219

    
220

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

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

    
268

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

    
289
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev){
290
		return getMarkerType(state, uuid, label, description, labelAbbrev, null, null);
291
	}
292

    
293
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MarkerType> voc){
294
	    return this.getMarkerType(state, uuid, label, description, labelAbbrev, voc, null);
295
	}
296

    
297

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

    
323
	protected AnnotationType getAnnotationType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){
324
		if (uuid == null){
325
			uuid = UUID.randomUUID();
326
		}
327
		AnnotationType annotationType = state.getAnnotationType(uuid);
328
		if (annotationType == null){
329
			annotationType = (AnnotationType)getTermService().find(uuid);
330
			if (annotationType == null){
331
				annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
332
				annotationType.setUuid(uuid);
333
				if (voc == null){
334
					boolean isOrdered = false;
335
					voc = getVocabulary(TermType.AnnotationType, uuidUserDefinedAnnotationTypeVocabulary, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered, annotationType);
336
				}
337

    
338
				voc.addTerm(annotationType);
339
				getTermService().save(annotationType);
340
			}
341
			state.putAnnotationType(annotationType);
342
		}
343
		return annotationType;
344
	}
345

    
346

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

    
368
	}
369

    
370

    
371

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

    
397
	}
398

    
399
	/**
400
	 * Returns a named area for a given uuid by first . If the named area does not
401
	 * @param state
402
	 * @param uuid
403
	 * @param label
404
	 * @param text
405
	 * @param labelAbbrev
406
	 * @param areaType
407
	 * @param level
408
	 * @return
409
	 */
410
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level){
411
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, null, null);
412
	}
413

    
414
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
415
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode, null);
416
	}
417

    
418

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

    
433
			if (vocabularyPreference == null){
434
				vocabularyPreference =  new ArrayList<TermVocabulary<NamedArea>>();
435
			}
436
			if (vocabularyPreference.isEmpty()){  //add TDWG vocabulary if preferences are empty
437
				vocabularyPreference.add(Country.GERMANY().getVocabulary());
438
				vocabularyPreference.add(TdwgAreaProvider.getAreaByTdwgAbbreviation("GER").getVocabulary());
439
			}
440

    
441

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

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

    
470

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

    
507

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

    
526
		return result;
527
	}
528

    
529

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

    
542

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

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

    
588

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

    
626
	/**
627
	 * Returns a feature if it exists, null otherwise.
628
	 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
629
	 * @param state
630
	 * @param uuid
631
	 * @return
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
	 * @param state
642
	 * @param uuid
643
	 * @param label
644
	 * @param text
645
	 * @param labelAbbrev
646
	 * @return
647
	 */
648
	protected Feature getFeature(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<Feature> voc){
649
		if (uuid == null){
650
			return null;
651
		}
652
		Feature feature = state.getFeature(uuid);
653
		if (feature == null){
654
			feature = (Feature)getTermService().find(uuid);
655
			if (feature == null && ! hasNoLabel(label, description, labelAbbrev)){
656
				feature = Feature.NewInstance(description, label, labelAbbrev);
657
				feature.setUuid(uuid);
658
				feature.setSupportsTextData(true);
659
//				UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
660
				if (voc == null){
661
					boolean isOrdered = false;
662
					voc = getVocabulary(TermType.Feature, uuidUserDefinedFeatureVocabulary, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered, feature);
663
				}
664
				voc.addTerm(feature);
665
				getTermService().save(feature);
666
			}
667
			state.putFeature(feature);
668
		}
669
		return feature;
670
	}
671

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

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

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

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

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

    
826
	private boolean hasNoLabel(String label, String text, String labelAbbrev) {
827
		return label == null && text == null && labelAbbrev == null;
828
	}
829

    
830
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm){
831
	    return getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, null);
832
	}
833

    
834

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

    
868
	/**
869
	 * Returns a language for a given uuid by first ...
870
	 * @param state
871
	 * @param uuid
872
	 * @param label
873
	 * @param text
874
	 * @param labelAbbrev
875
	 * @return
876
	 */
877
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
878
		return getLanguage(state, uuid, label, text, labelAbbrev, null);
879
	}
880

    
881
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
882
		if (uuid == null){
883
			return null;
884
		}
885
		Language language = state.getLanguage(uuid);
886
		if (language == null){
887
			language = (Language)getTermService().find(uuid);
888
			if (language == null){
889
				language = Language.NewInstance(text, label, labelAbbrev);
890

    
891
				language.setUuid(uuid);
892
				if (voc == null){
893
					UUID uuidLanguageVoc = UUID.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
894
					boolean isOrdered = false;
895
					voc = getVocabulary(TermType.Language, uuidLanguageVoc, "User defined languages", "User defined languages", "User defined languages", null, isOrdered, language);
896
				}
897
				//set vocabulary ; FIXME use another user-defined vocabulary
898

    
899
				voc.addTerm(language);
900
				getTermService().save(language);
901
			}
902
			state.putLanguage(language);
903
		}
904
		return language;
905
	}
906

    
907

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

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

    
964
	/**
965
	 * @see #addOriginalSource(CdmBase, Object, String, Reference)
966
	 * @param rs
967
	 * @param cdmBase
968
	 * @param dbIdAttribute
969
	 * @param namespace
970
	 * @param citation
971
	 * @throws SQLException
972
	 */
973
	public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
974
		Object id = rs.getObject(dbIdAttribute);
975
		addOriginalSource(cdmBase, id, namespace, citation);
976
	}
977

    
978

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

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

    
1010
		if (StringUtils.isBlank(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
1011
			childName.setSpecificEpithet(parentName.getSpecificEpithet());
1012
		}
1013
		if (childName.isAutonym() && childName.getCombinationAuthorship() == null && childName.getBasionymAuthorship() == null ){
1014
			childName.setCombinationAuthorship(parentName.getCombinationAuthorship());
1015
			childName.setBasionymAuthorship(parentName.getBasionymAuthorship());
1016
		}
1017
	}
1018

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

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

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

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

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

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

    
1179

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

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

    
1222

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

    
1270

    
1271

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

    
1289
	}
1290

    
1291

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

    
1316
	protected Media getImageMedia(String uriString, boolean readMediaData) throws MalformedURLException {
1317
	    return getImageMedia(uriString, null, readMediaData);
1318
	}
1319

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

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

    
1349
				MediaRepresentation representation = MediaRepresentation.NewInstance();
1350

    
1351
				if(imageInfo != null){
1352
					representation.setMimeType(imageInfo.getMimeType());
1353
					representation.setSuffix(imageInfo.getSuffix());
1354
				}
1355
				representation.addRepresentationPart(imageFile);
1356
				Media media = Media.NewInstance();
1357
                media.addRepresentation(representation);
1358

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

    
1374
	                ImageFile imageFileFhumb = ImageFile.NewInstance(uriThumb, null, imageInfoThumb);
1375
				    MediaRepresentation reprThumb = MediaRepresentation.NewInstance();
1376
				    if(imageInfoThumb != null){
1377
				        reprThumb.setMimeType(imageInfoThumb.getMimeType());
1378
				        reprThumb.setSuffix(imageInfoThumb.getSuffix());
1379
	                }
1380
				    reprThumb.addRepresentationPart(imageFileFhumb);
1381
				    media.addRepresentation(reprThumb);
1382
				}
1383

    
1384
				return media;
1385
			} catch (URISyntaxException e1) {
1386
				String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " +  uriString;
1387
				logger.warn(message);
1388
				fireWarningEvent(message, "unknown location", 4, 0);
1389
				return null;
1390
			}
1391
		}
1392
	}
1393

    
1394

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

    
1412
	protected Boolean nullSafeBoolean(ResultSet rs, String columnName) throws SQLException {
1413
		Object bitObject = rs.getObject(columnName);
1414
		if (bitObject == null){
1415
			return null;
1416
		}else{
1417
			return Boolean.valueOf(bitObject.toString());
1418
		}
1419
	}
1420

    
1421
	protected Double nullSafeDouble(ResultSet rs, String columnName) throws SQLException {
1422
		Object doubleObject = rs.getObject(columnName);
1423
		if (doubleObject == null){
1424
			return null;
1425
		}else{
1426
			return Double.valueOf(doubleObject.toString());
1427
		}
1428
	}
1429

    
1430
	protected Float nullSafeFloat(ResultSet rs, String columnName) throws SQLException {
1431
		Object doubleObject = rs.getObject(columnName);
1432
		if (doubleObject == null){
1433
			return null;
1434
		}else{
1435
			return Float.valueOf(doubleObject.toString());
1436
		}
1437
	}
1438

    
1439

    
1440
	/**
1441
	 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1442
	 * @param str
1443
	 * @return
1444
	 */
1445
	protected String NB(String str) {
1446
		if (StringUtils.isBlank(str)){
1447
			return null;
1448
		}else{
1449
			return str;
1450
		}
1451
	}
1452

    
1453
	@Override
1454
    public byte[] getByteArray() {
1455
        // TODO Auto-generated method stub
1456
        return null;
1457
    }
1458

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

    
1485
        } else {
1486
            teamMembers = authorName.split(lastAuthorSeparator);
1487
            if (teamMembers.length>1){
1488
                author = Team.NewInstance();
1489
                for(String member:teamMembers){
1490
                  teamMember = Person.NewInstance();
1491
                  teamMember.setTitleCache(member, true);
1492
                  ((Team)author).addTeamMember(teamMember);
1493

    
1494
                }
1495
            }else{
1496
                author = Person.NewInstance();
1497
                author.setTitleCache(authorName, true);
1498
            }
1499
        }
1500
        author.getTitleCache();
1501
        return author;
1502
    }
1503

    
1504

    
1505
}
(11-11/71)