Project

General

Profile

Download (50.4 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.HashSet;
20
import java.util.List;
21
import java.util.Set;
22
import java.util.UUID;
23

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

    
27
import eu.etaxonomy.cdm.api.service.pager.Pager;
28
import eu.etaxonomy.cdm.common.media.ImageInfo;
29
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
30
import eu.etaxonomy.cdm.io.common.mapping.IInputTransformer;
31
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
32
import eu.etaxonomy.cdm.io.markup.MarkupTransformer;
33
import eu.etaxonomy.cdm.model.common.AnnotationType;
34
import eu.etaxonomy.cdm.model.common.CdmBase;
35
import eu.etaxonomy.cdm.model.common.DefinedTerm;
36
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
37
import eu.etaxonomy.cdm.model.common.ExtensionType;
38
import eu.etaxonomy.cdm.model.common.IOriginalSource;
39
import eu.etaxonomy.cdm.model.common.ISourceable;
40
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
41
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
42
import eu.etaxonomy.cdm.model.common.Language;
43
import eu.etaxonomy.cdm.model.common.Marker;
44
import eu.etaxonomy.cdm.model.common.MarkerType;
45
import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
46
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
47
import eu.etaxonomy.cdm.model.common.TermType;
48
import eu.etaxonomy.cdm.model.common.TermVocabulary;
49
import eu.etaxonomy.cdm.model.description.DescriptionBase;
50
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
51
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
52
import eu.etaxonomy.cdm.model.description.Feature;
53
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
54
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
55
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
56
import eu.etaxonomy.cdm.model.description.State;
57
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
58
import eu.etaxonomy.cdm.model.description.TaxonDescription;
59
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
60
import eu.etaxonomy.cdm.model.description.TextData;
61
import eu.etaxonomy.cdm.model.location.Country;
62
import eu.etaxonomy.cdm.model.location.NamedArea;
63
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
64
import eu.etaxonomy.cdm.model.location.NamedAreaType;
65
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
66
import eu.etaxonomy.cdm.model.media.ImageFile;
67
import eu.etaxonomy.cdm.model.media.Media;
68
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
69
import eu.etaxonomy.cdm.model.name.NonViralName;
70
import eu.etaxonomy.cdm.model.name.Rank;
71
import eu.etaxonomy.cdm.model.name.RankClass;
72
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
73
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
74
import eu.etaxonomy.cdm.model.reference.Reference;
75
import eu.etaxonomy.cdm.model.taxon.Classification;
76
import eu.etaxonomy.cdm.model.taxon.Synonym;
77
import eu.etaxonomy.cdm.model.taxon.Taxon;
78
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
79
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
80

    
81
/**
82
 * @author a.mueller
83
 * @created 01.07.2008
84
 */
85
public abstract class CdmImportBase<CONFIG extends IImportConfigurator, STATE extends ImportStateBase> extends CdmIoBase<STATE> implements ICdmImport<CONFIG, STATE>{
86
    private static final long serialVersionUID = 8730012744209195616L;
87

    
88
    private static Logger logger = Logger.getLogger(CdmImportBase.class);
89

    
90
	protected static final boolean CREATE = true;
91
	protected static final boolean IMAGE_GALLERY = true;
92
	protected static final boolean READ_MEDIA_DATA = true;
93

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

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

    
112

    
113

    
114
	private static final String UuidOnly = "UUIDOnly";
115
	private static final String UuidLabel = "UUID or label";
116
	private static final String UuidLabelAbbrev = "UUID, label or abbreviation";
117
	private static final String UuidAbbrev = "UUID or abbreviation";
118

    
119
	public enum TermMatchMode{
120
		UUID_ONLY(0, UuidOnly)
121
		,UUID_LABEL(1, UuidLabel)
122
		,UUID_LABEL_ABBREVLABEL(2, UuidLabelAbbrev)
123
		,UUID_ABBREVLABEL(3, UuidAbbrev)
124
		;
125

    
126

    
127
		private final int id;
128
		private final String representation;
129
		private TermMatchMode(int id, String representation){
130
			this.id = id;
131
			this.representation = representation;
132
		}
133
		public int getId() {
134
			return id;
135
		}
136
		public String getRepresentation() {
137
			return representation;
138
		}
139
		public TermMatchMode valueOf(int id){
140
			switch (id){
141
				case 0: return UUID_ONLY;
142
				case 1: return UUID_LABEL;
143
				case 2: return UUID_LABEL_ABBREVLABEL;
144
				case 3: return UUID_ABBREVLABEL;
145
				default: return UUID_ONLY;
146
			}
147
 		}
148

    
149

    
150
	}
151

    
152
	protected Classification makeTree(STATE state, Reference reference){
153
		String treeName = "Classification (Import)";
154
		if (reference != null && StringUtils.isNotBlank(reference.getTitleCache())){
155
			treeName = reference.getTitleCache();
156
		}
157
		Classification tree = Classification.NewInstance(treeName);
158
		tree.setReference(reference);
159

    
160

    
161
		// use defined uuid for first tree
162
		CONFIG config = (CONFIG)state.getConfig();
163
		if (state.countTrees() < 1 ){
164
			tree.setUuid(config.getClassificationUuid());
165
		}
166
		getClassificationService().save(tree);
167
		state.putTree(reference, tree);
168
		return tree;
169
	}
170

    
171

    
172
	/**
173
	 * Alternative memory saving method variant of
174
	 * {@link #makeTree(STATE state, Reference ref)} which stores only the
175
	 * UUID instead of the full tree in the <code>ImportStateBase</code> by
176
	 * using <code>state.putTreeUuid(ref, tree);</code>
177
	 *
178
	 * @param state
179
	 * @param ref
180
	 * @return
181
	 */
182
	protected Classification makeTreeMemSave(STATE state, Reference ref){
183
		String treeName = "Classification (Import)";
184
		if (ref != null && StringUtils.isNotBlank(ref.getTitleCache())){
185
			treeName = ref.getTitleCache();
186
		}
187
		Classification tree = Classification.NewInstance(treeName);
188
		tree.setReference(ref);
189

    
190

    
191
		// use defined uuid for first tree
192
		CONFIG config = (CONFIG)state.getConfig();
193
		if (state.countTrees() < 1 ){
194
			tree.setUuid(config.getClassificationUuid());
195
		}
196
		getClassificationService().save(tree);
197
		state.putTreeUuid(ref, tree);
198
		return tree;
199
	}
200

    
201

    
202

    
203

    
204
	protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
205
		return getExtensionType(state, uuid, label, text, labelAbbrev, null);
206
	}
207
	protected ExtensionType getExtensionType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<ExtensionType> voc){
208
		if (uuid == null){
209
			uuid = UUID.randomUUID();
210
		}
211
		ExtensionType extensionType = state.getExtensionType(uuid);
212
		if (extensionType == null){
213
			extensionType = (ExtensionType)getTermService().find(uuid);
214
			if (extensionType == null){
215
				extensionType = ExtensionType.NewInstance(text, label, labelAbbrev);
216
				extensionType.setUuid(uuid);
217
				if (voc == null){
218
					boolean isOrdered = false;
219
					voc = getVocabulary(TermType.ExtensionType, uuidUserDefinedExtensionTypeVocabulary, "User defined vocabulary for extension types", "User Defined Extension Types", null, null, isOrdered, extensionType);
220
				}
221
				voc.addTerm(extensionType);
222
				getTermService().saveOrUpdate(extensionType);
223
			}
224
			state.putExtensionType(extensionType);
225
		}
226
		return extensionType;
227
	}
228

    
229
	protected DefinedTerm getIdentiferType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
230
		if (uuid == null){
231
			uuid = UUID.randomUUID();
232
		}
233
		DefinedTerm identifierType = state.getIdentifierType(uuid);
234
		if (identifierType == null){
235
			identifierType = (DefinedTerm)getTermService().find(uuid);
236
			if (identifierType == null){
237
				identifierType = DefinedTerm .NewIdentifierTypeInstance(text, label, labelAbbrev);
238
				identifierType.setUuid(uuid);
239
				if (voc == null){
240
					boolean isOrdered = false;
241
					voc = getVocabulary(TermType.IdentifierType, uuidUserDefinedIdentifierTypeVocabulary, "User defined vocabulary for identifier types", "User Defined Identifier Types", null, null, isOrdered, identifierType);
242
				}
243
				voc.addTerm(identifierType);
244
				getTermService().saveOrUpdate(identifierType);
245
			}
246
			state.putIdentifierType(identifierType);
247
		}
248
		return identifierType;
249
	}
250

    
251

    
252
	protected MarkerType getMarkerType(STATE state, String keyString) {
253
		IInputTransformer transformer = state.getTransformer();
254
		MarkerType markerType = null;
255
		try {
256
			markerType = transformer.getMarkerTypeByKey(keyString);
257
		} catch (UndefinedTransformerMethodException e) {
258
			logger.info("getMarkerTypeByKey not yet implemented for this import");
259
		}
260
		if (markerType == null ){
261
			UUID uuid;
262
			try {
263
				uuid = transformer.getMarkerTypeUuid(keyString);
264
				return getMarkerType(state, uuid, keyString, keyString, keyString);
265
			} catch (UndefinedTransformerMethodException e) {
266
				logger.warn("getMarkerTypeUuid not yet implemented for this import");
267
			}
268
		}
269
		return null;
270
	}
271

    
272
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
273
		return getMarkerType(state, uuid, label, text, labelAbbrev, null);
274
	}
275

    
276

    
277
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MarkerType> voc){
278
		if (uuid == null){
279
			uuid = UUID.randomUUID();
280
		}
281
		MarkerType markerType = state.getMarkerType(uuid);
282
		if (markerType == null){
283
			markerType = (MarkerType)getTermService().find(uuid);
284
			if (markerType == null){
285
				markerType = MarkerType.NewInstance(description, label, labelAbbrev);
286
				markerType.setUuid(uuid);
287
				if (voc == null){
288
					boolean isOrdered = false;
289
					voc = getVocabulary(TermType.MarkerType, uuidUserDefinedMarkerTypeVocabulary, "User defined vocabulary for marker types", "User Defined Marker Types", null, null, isOrdered, markerType);
290
				}
291
				voc.addTerm(markerType);
292
				getTermService().save(markerType);
293
			}
294
			state.putMarkerType(markerType);
295
		}
296
		return markerType;
297
	}
298

    
299
	protected AnnotationType getAnnotationType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<AnnotationType> voc){
300
		if (uuid == null){
301
			uuid = UUID.randomUUID();
302
		}
303
		AnnotationType annotationType = state.getAnnotationType(uuid);
304
		if (annotationType == null){
305
			annotationType = (AnnotationType)getTermService().find(uuid);
306
			if (annotationType == null){
307
				annotationType = AnnotationType.NewInstance(label, text, labelAbbrev);
308
				annotationType.setUuid(uuid);
309
				if (voc == null){
310
					boolean isOrdered = false;
311
					voc = getVocabulary(TermType.AnnotationType, uuidUserDefinedAnnotationTypeVocabulary, "User defined vocabulary for annotation types", "User Defined Annotation Types", null, null, isOrdered, annotationType);
312
				}
313

    
314
				voc.addTerm(annotationType);
315
				getTermService().save(annotationType);
316
			}
317
			state.putAnnotationType(annotationType);
318
		}
319
		return annotationType;
320
	}
321

    
322

    
323
	protected ReferenceSystem getReferenceSystem(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
324
		if (uuid == null){
325
			uuid = UUID.randomUUID();
326
		}
327
		ReferenceSystem refSystem = state.getReferenceSystem(uuid);
328
		if (refSystem == null){
329
			refSystem = (ReferenceSystem)getTermService().find(uuid);
330
			if (refSystem == null){
331
				refSystem = ReferenceSystem.NewInstance(text, label, labelAbbrev);
332
				if (voc == null){
333
					boolean isOrdered = false;
334
					voc = getVocabulary(TermType.ReferenceSystem, uuidUserDefinedReferenceSystemVocabulary, "User defined vocabulary for reference systems", "User Defined Reference System", null, null, isOrdered, refSystem);
335
				}
336
				voc.addTerm(refSystem);
337
				refSystem.setUuid(uuid);
338
				getTermService().save(refSystem);
339
			}
340
			state.putReferenceSystem(refSystem);
341
		}
342
		return refSystem;
343

    
344
	}
345

    
346

    
347

    
348
	protected Rank getRank(STATE state, UUID uuid, String label, String text, String labelAbbrev,OrderedTermVocabulary<Rank> voc, Rank lowerRank, RankClass rankClass){
349
		if (uuid == null){
350
			uuid = UUID.randomUUID();
351
		}
352
		Rank rank = state.getRank(uuid);
353
		if (rank == null){
354
			rank = (Rank)getTermService().find(uuid);
355
			if (rank == null){
356
				rank = Rank.NewInstance(rankClass, text, label, labelAbbrev);
357
				if (voc == null){
358
					boolean isOrdered = true;
359
					voc = (OrderedTermVocabulary)getVocabulary(TermType.Rank, uuidUserDefinedRankVocabulary, "User defined vocabulary for ranks", "User Defined Reference System", null, null, isOrdered, rank);
360
				}
361
				if (lowerRank == null){
362
					voc.addTerm(rank);
363
				}else{
364
					voc.addTermAbove(rank, lowerRank);
365
				}
366
				rank.setUuid(uuid);
367
				getTermService().save(rank);
368
			}
369
			state.putRank(rank);
370
		}
371
		return rank;
372

    
373
	}
374

    
375
	/**
376
	 * Returns a named area for a given uuid by first . If the named area does not
377
	 * @param state
378
	 * @param uuid
379
	 * @param label
380
	 * @param text
381
	 * @param labelAbbrev
382
	 * @param areaType
383
	 * @param level
384
	 * @return
385
	 */
386
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level){
387
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, null, null);
388
	}
389

    
390
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
391
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode, null);
392
	}
393

    
394

    
395
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode,
396
			List<TermVocabulary<NamedArea>> vocabularyPreference){
397
		Class<NamedArea> clazz = NamedArea.class;
398
		if (uuid == null){
399
			uuid = UUID.randomUUID();
400
		}
401
		if (matchMode == null){
402
			matchMode = TermMatchMode.UUID_ONLY;
403
		}
404
		NamedArea namedArea = state.getNamedArea(uuid);
405
		if (namedArea == null){
406
			DefinedTermBase<?> term = getTermService().find(uuid);
407
			namedArea = CdmBase.deproxy(term,NamedArea.class);
408

    
409
			if (vocabularyPreference == null){
410
				vocabularyPreference =  new ArrayList<TermVocabulary<NamedArea>>();
411
			}
412
			if (vocabularyPreference.isEmpty()){  //add TDWG vocabulary if preferences are empty
413
				vocabularyPreference.add(Country.GERMANY().getVocabulary());
414
				vocabularyPreference.add(TdwgAreaProvider.getAreaByTdwgAbbreviation("GER").getVocabulary());
415
			}
416

    
417

    
418
			//TODO matching still experimental
419
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_LABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL ))){
420
				//TODO test
421
				Pager<NamedArea> areaPager = (Pager)getTermService().findByTitle(clazz, label, null, null, null, null, null, null);
422
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
423
			}
424
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_ABBREVLABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL))){
425
				Pager<NamedArea> areaPager = getTermService().findByRepresentationAbbreviation(labelAbbrev, clazz, null, null);
426
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
427
			}
428

    
429
			if (namedArea == null){
430
				namedArea = NamedArea.NewInstance(text, label, labelAbbrev);
431
				if (voc == null){
432
					boolean isOrdered = true;
433
					voc = getVocabulary(TermType.NamedArea, uuidUserDefinedNamedAreaVocabulary, "User defined vocabulary for named areas", "User Defined Named Areas", null, null, isOrdered, namedArea);
434
				}
435
				voc.addTerm(namedArea);
436
				namedArea.setType(areaType);
437
				namedArea.setLevel(level);
438
				namedArea.setUuid(uuid);
439
				getTermService().saveOrUpdate(namedArea);
440
			}
441
			state.putNamedArea(namedArea);
442
		}
443
		return namedArea;
444
	}
445

    
446

    
447
	private NamedArea findBestMatchingArea(Pager<NamedArea> areaPager, UUID uuid, String label, String text, String abbrev, List<TermVocabulary<NamedArea>> vocabularyPreference) {
448
		// TODO preliminary implementation
449
		List<NamedArea> list = areaPager.getRecords();
450
		if (list.size() == 0){
451
			return null;
452
		}else if (list.size() == 1){
453
			return list.get(0);
454
		}else if (list.size() > 1){
455
			List<NamedArea> preferredList = new ArrayList<NamedArea>();
456
			for (TermVocabulary<NamedArea> voc: vocabularyPreference){
457
				for (NamedArea area : list){
458
					if (voc.equals(area.getVocabulary())){
459
						preferredList.add(area);
460
					}
461
				}
462
				if (preferredList.size() > 0){
463
					break;
464
				}
465
			}
466
			if (preferredList.size() > 1 ){
467
				preferredList = getLowestLevelAreas(preferredList);
468
			}else if (preferredList.size() == 0 ){
469
				preferredList = list;
470
			}
471
			if (preferredList.size() == 1 ){
472
				return preferredList.get(0);
473
			}else if (preferredList.size() > 1 ){
474
				String message = "There is more than 1 matching area for %s, %s, %s. As a preliminary implementation I take the first";
475
				message = String.format(message, label, abbrev, text);
476
				logger.warn(message);
477
				return list.get(0);
478
			}
479
		}
480
		return null;
481
	}
482

    
483

    
484
	private List<NamedArea> getLowestLevelAreas(List<NamedArea> preferredList) {
485
		List<NamedArea> result = new ArrayList<NamedArea>();
486
		for (NamedArea area : preferredList){
487
			if (result.isEmpty()){
488
				result.add(area);
489
			}else {
490
				int compare = compareAreaLevel(area, result.get(0));
491
				if (compare < 0){
492
					result = new ArrayList<NamedArea>();
493
					result.add(area);
494
				}else if (compare == 0){
495
					result.add(area);
496
				}else{
497
					//do nothing
498
				}
499

    
500
			}
501
		}
502

    
503
		return result;
504
	}
505

    
506

    
507
	private int compareAreaLevel(NamedArea area1, NamedArea area2) {
508
		NamedAreaLevel level1 = area1.getLevel();
509
		NamedAreaLevel level2 = area2.getLevel();
510
		if (level1 == null){
511
			return (level2 == null)? 0 : 1;
512
		}else if (level2 == null){
513
			return -1;
514
		}else{
515
			return level1.compareTo(level2);
516
		}
517
	}
518

    
519

    
520
	protected NamedAreaLevel getNamedAreaLevel(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<NamedAreaLevel> voc){
521
		if (uuid == null){
522
			uuid = UUID.randomUUID();
523
		}
524
		NamedAreaLevel namedAreaLevel = state.getNamedAreaLevel(uuid);
525
		if (namedAreaLevel == null){
526
			//TODO propPath just for testing
527
			List<String> propPath = Arrays.asList("vocabulary");
528
			DefinedTermBase<NamedAreaLevel> term = getTermService().load(uuid, propPath);
529
			namedAreaLevel = CdmBase.deproxy(term, NamedAreaLevel.class);
530
			if (namedAreaLevel == null){
531
				namedAreaLevel = NamedAreaLevel.NewInstance(text, label, labelAbbrev);
532
				if (voc == null){
533
					boolean isOrdered = true;
534
					voc = getVocabulary(TermType.NamedAreaLevel, uuidUserDefinedNamedAreaLevelVocabulary, "User defined vocabulary for named area levels", "User Defined Named Area Levels", null, null, isOrdered, namedAreaLevel);
535
				}
536
				//FIXME only for debugging
537
				Set<NamedAreaLevel> terms = voc.getTerms();
538
				for (NamedAreaLevel level : terms){
539
					TermVocabulary<NamedAreaLevel> levelVoc = level.getVocabulary();
540
					if (levelVoc == null){
541
						logger.error("ONLY FOR DEBUG: Level voc is null");
542
					}else{
543
						logger.info("ONLY FOR DEBUG: Level voc is not null");
544
					}
545
				}
546
				voc.addTerm(namedAreaLevel);
547
				namedAreaLevel.setUuid(uuid);
548
				getTermService().save(namedAreaLevel);
549
			}
550
			state.putNamedAreaLevel(namedAreaLevel);
551
		}
552
		return namedAreaLevel;
553
	}
554

    
555
	/**
556
	 * Returns a {@link State} if it exists. <code>null</code> otherwise.
557
	 * @param state
558
	 * @param uuid
559
	 * @return {@link State}
560
	 */
561
	protected State getStateTerm(STATE state, UUID uuid){
562
		return getStateTerm(state, uuid, null, null, null, null);
563
	}
564

    
565

    
566
	/**
567
	 * Returns a {@link State} for a given uuid by first checking if the uuid has already been used in this import, if not
568
	 * checking if the state exists in the database, if not creating it anew (with vocabulary etc.).
569
	 * If label, text and labelAbbrev are all <code>null</code> no state is created.
570
	 * @param importState
571
	 * @param uuid
572
	 * @param label
573
	 * @param text
574
	 * @param labelAbbrev
575
	 * @param voc
576
	 * @return
577
	 */
578
	protected State getStateTerm(STATE importState, UUID uuid, String label, String text, String labelAbbrev, OrderedTermVocabulary<State> voc) {
579
		if (uuid == null){
580
			return null;
581
		}
582
		State stateTerm = importState.getStateTerm(uuid);
583
		if (stateTerm == null){
584
			stateTerm = CdmBase.deproxy(getTermService().find(uuid), State.class);
585
			if (stateTerm == null && ! hasNoLabel(label, text, labelAbbrev)){
586
				stateTerm = State.NewInstance(text, label, labelAbbrev);
587
				stateTerm.setUuid(uuid);
588
				if (voc == null){
589
					boolean isOrdered = true;
590
					TermVocabulary<State> orderedVoc = getVocabulary(TermType.State, uuidUserDefinedStateVocabulary, "User defined vocabulary for states used by Categorical Data", "User Defined States", null, null, isOrdered, stateTerm);
591
					voc = CdmBase.deproxy(orderedVoc, OrderedTermVocabulary.class);
592
				}
593
				voc.addTerm(stateTerm);
594
				getTermService().save(stateTerm);
595
			}else{
596
				logger.warn("No label provided for new state with uuid " + uuid);
597
			}
598
			importState.putStateTerm(stateTerm);
599
		}
600
		return stateTerm;
601
	}
602

    
603
	/**
604
	 * Returns a feature if it exists, null otherwise.
605
	 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
606
	 * @param state
607
	 * @param uuid
608
	 * @return
609
	 */
610
	protected Feature getFeature(STATE state, UUID uuid){
611
		return getFeature(state, uuid, null, null, null, null);
612
	}
613

    
614
	/**
615
	 * Returns a feature for a given uuid by first checking if the uuid has already been used in this import, if not
616
	 * checking if the feature exists in the database, if not creating it anew (with vocabulary etc.).
617
	 * If label, text and labelAbbrev are all <code>null</code> no feature is created.
618
	 * @param state
619
	 * @param uuid
620
	 * @param label
621
	 * @param text
622
	 * @param labelAbbrev
623
	 * @return
624
	 */
625
	protected Feature getFeature(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<Feature> voc){
626
		if (uuid == null){
627
			return null;
628
		}
629
		Feature feature = state.getFeature(uuid);
630
		if (feature == null){
631
			feature = (Feature)getTermService().find(uuid);
632
			if (feature == null && ! hasNoLabel(label, description, labelAbbrev)){
633
				feature = Feature.NewInstance(description, label, labelAbbrev);
634
				feature.setUuid(uuid);
635
				feature.setSupportsTextData(true);
636
//				UUID uuidFeatureVoc = UUID.fromString("b187d555-f06f-4d65-9e53-da7c93f8eaa8");
637
				if (voc == null){
638
					boolean isOrdered = false;
639
					voc = getVocabulary(TermType.Feature, uuidUserDefinedFeatureVocabulary, "User defined vocabulary for features", "User Defined Features", null, null, isOrdered, feature);
640
				}
641
				voc.addTerm(feature);
642
				getTermService().save(feature);
643
			}
644
			state.putFeature(feature);
645
		}
646
		return feature;
647
	}
648

    
649
	protected DefinedTerm getKindOfUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<DefinedTerm> voc){
650
		if (uuid == null){
651
			return null;
652
		}
653
		DefinedTerm unit = state.getKindOfUnit(uuid);
654
		if (unit == null){
655
			unit = (DefinedTerm)getTermService().find(uuid);
656
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
657
				unit = DefinedTerm.NewKindOfUnitInstance(description, label, labelAbbrev);
658
				unit.setUuid(uuid);
659
				if (voc == null){
660
					boolean isOrdered = false;
661
					voc = getVocabulary(TermType.KindOfUnit, uuidUserDefinedKindOfUnitVocabulary, "User defined vocabulary for kind-of-units", "User Defined Measurement kind-of-units", null, null, isOrdered, unit);
662
				}
663
				voc.addTerm(unit);
664
				getTermService().save(unit);
665
			}
666
			state.putKindOfUnit(unit);
667
		}
668
		return unit;
669
	}
670

    
671
	/**
672
	 * Returns a {@link MeasurementUnit} for a given uuid by first checking if the uuid has already been used in this import, if not
673
	 * checking if the {@link MeasurementUnit} exists in the database, if not creating it anew (with vocabulary etc.).
674
	 * If label, text and labelAbbrev are all <code>null</code> no {@link MeasurementUnit} is created.
675
	 * @param state
676
	 * @param uuid
677
	 * @param label
678
	 * @param text
679
	 * @param labelAbbrev
680
	 * @return
681
	 */
682
	protected MeasurementUnit getMeasurementUnit(STATE state, UUID uuid, String label, String description, String labelAbbrev, TermVocabulary<MeasurementUnit> voc){
683
		if (uuid == null){
684
			return null;
685
		}
686
		MeasurementUnit unit = state.getMeasurementUnit(uuid);
687
		if (unit == null){
688
			unit = (MeasurementUnit)getTermService().find(uuid);
689
			if (unit == null && ! hasNoLabel(label, description, labelAbbrev)){
690
				unit = MeasurementUnit.NewInstance(description, label, labelAbbrev);
691
				unit.setUuid(uuid);
692
				if (voc == null){
693
					boolean isOrdered = false;
694
					voc = getVocabulary(TermType.MeasurementUnit, uuidUserDefinedMeasurementUnitVocabulary, "User defined vocabulary for measurement units", "User Defined Measurement Units", null, null, isOrdered, unit);
695
				}
696
				voc.addTerm(unit);
697
				getTermService().save(unit);
698
			}
699
			state.putMeasurementUnit(unit);
700
		}
701
		return unit;
702
	}
703

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

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

    
770
	/**
771
	 * Returns a taxon relationship type for a given uuid by first checking if the uuid has already been used in this import, if not
772
	 * checking if the taxon relationship type exists in the database, if not creating it anew (with vocabulary etc.).
773
	 * If label, text and labelAbbrev are all <code>null</code> no taxon relationship type is created.
774
	 * @param state
775
	 * @param uuid
776
	 * @param label
777
	 * @param text
778
	 * @param labelAbbrev
779
	 * @return
780
	 */
781
	protected TaxonRelationshipType getTaxonRelationshipType(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary<TaxonRelationshipType> voc){
782
		if (uuid == null){
783
			return null;
784
		}
785
		TaxonRelationshipType relType = state.getTaxonRelationshipType(uuid);
786
		if (relType == null){
787
			relType = (TaxonRelationshipType)getTermService().find(uuid);
788
			if (relType == null && ! hasNoLabel(label, text, labelAbbrev)){
789
				relType = TaxonRelationshipType.NewInstance(text, label, labelAbbrev, false, false);
790
				relType.setUuid(uuid);
791
				if (voc == null){
792
					boolean isOrdered = true;
793
					voc = getVocabulary(TermType.TaxonRelationshipType, uuidUserDefinedTaxonRelationshipTypeVocabulary, "User defined vocabulary for taxon relationship types", "User Defined Taxon Relationship Types", null, null, isOrdered, relType);
794
				}
795
				voc.addTerm(relType);
796
				getTermService().save(relType);
797
			}
798
			state.putTaxonRelationshipType(relType);
799
		}
800
		return relType;
801
	}
802

    
803
	private boolean hasNoLabel(String label, String text, String labelAbbrev) {
804
		return label == null && text == null && labelAbbrev == null;
805
	}
806

    
807
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm){
808
	    return getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, null);
809
	}
810

    
811

    
812
	/**
813
	 * Returns a presence term for a given uuid by first ...
814
	 * @param state
815
	 * @param uuid
816
	 * @param label
817
	 * @param text
818
	 * @param labelAbbrev
819
	 * @return
820
	 */
821
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm, TermVocabulary<PresenceAbsenceTerm> voc){
822
		if (uuid == null){
823
			return null;
824
		}
825
		PresenceAbsenceTerm presenceTerm = state.getPresenceAbsenceTerm(uuid);
826
		if (presenceTerm == null){
827
			presenceTerm = (PresenceAbsenceTerm)getTermService().find(uuid);
828
			if (presenceTerm == null){
829
				presenceTerm = PresenceAbsenceTerm.NewPresenceInstance(text, label, labelAbbrev);
830
				presenceTerm.setUuid(uuid);
831
				presenceTerm.setAbsenceTerm(isAbsenceTerm);
832
				//set vocabulary ; FIXME use another user-defined vocabulary
833
				if (voc == null){
834
                    boolean isOrdered = true;
835
                    voc = getVocabulary(TermType.PresenceAbsenceTerm, uuidUserDefinedPresenceAbsenceVocabulary, "User defined vocabulary for distribution status", "User Defined Distribution Status", null, null, isOrdered, presenceTerm);
836
                }
837
				voc.addTerm(presenceTerm);
838
				getTermService().save(presenceTerm);
839
			}
840
			state.putPresenceAbsenceTerm(presenceTerm);
841
		}
842
		return presenceTerm;
843
	}
844

    
845
	/**
846
	 * Returns a language for a given uuid by first ...
847
	 * @param state
848
	 * @param uuid
849
	 * @param label
850
	 * @param text
851
	 * @param labelAbbrev
852
	 * @return
853
	 */
854
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
855
		return getLanguage(state, uuid, label, text, labelAbbrev, null);
856
	}
857

    
858
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
859
		if (uuid == null){
860
			return null;
861
		}
862
		Language language = state.getLanguage(uuid);
863
		if (language == null){
864
			language = (Language)getTermService().find(uuid);
865
			if (language == null){
866
				language = Language.NewInstance(text, label, labelAbbrev);
867

    
868
				language.setUuid(uuid);
869
				if (voc == null){
870
					UUID uuidLanguageVoc = UUID.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
871
					boolean isOrdered = false;
872
					voc = getVocabulary(TermType.Language, uuidLanguageVoc, "User defined languages", "User defined languages", "User defined languages", null, isOrdered, language);
873
				}
874
				//set vocabulary ; FIXME use another user-defined vocabulary
875

    
876
				voc.addTerm(language);
877
				getTermService().save(language);
878
			}
879
			state.putLanguage(language);
880
		}
881
		return language;
882
	}
883

    
884

    
885
	/**
886
	 * @param uuid
887
	 * @return
888
	 *
889
	 */
890
	protected <T extends DefinedTermBase> TermVocabulary<T> getVocabulary(TermType termType, UUID uuid, String description, String label, String abbrev, URI termSourceUri, boolean isOrdered, T type) {
891
		List<String> propPath = Arrays.asList(new String[]{"terms"});
892
		TermVocabulary<T> voc = getVocabularyService().load(uuid, propPath);
893
		if (voc == null){
894
			if (isOrdered){
895
				voc = OrderedTermVocabulary.NewInstance(termType, description, label, abbrev, termSourceUri);
896
			}else{
897
				voc = TermVocabulary.NewInstance(termType, description, label, abbrev, termSourceUri);
898
			}
899
			voc.setUuid(uuid);
900
			getVocabularyService().save(voc);
901
		}
902
		return voc;
903
	}
904

    
905
	/**
906
	 * Adds an orginal source to a sourceable objects (implemented for Identifiable entity and description element.
907
	 * If cdmBase is not sourceable nothing happens.
908
	 * TODO Move to DbImportBase once this exists.
909
	 * TODO also implemented in DbImportObjectCreationMapper (reduce redundance)
910
	 * @param rs
911
	 * @param cdmBase
912
	 * @param dbIdAttribute
913
	 * @param namespace
914
	 * @param citation
915
	 * @throws SQLException
916
	 */
917
	public void addOriginalSource(CdmBase cdmBase, Object idAttributeValue, String namespace, Reference citation)  {
918
		if (cdmBase instanceof ISourceable ){
919
			IOriginalSource source;
920
			ISourceable sourceable = (ISourceable<?>)cdmBase;
921
			Object id = idAttributeValue;
922
			String strId = String.valueOf(id);
923
			String microCitation = null;
924
			OriginalSourceType type = OriginalSourceType.Import;
925
			if (cdmBase instanceof IdentifiableEntity){
926
				source = IdentifiableSource.NewInstance(type, strId, namespace, citation, microCitation);
927
			}else if (cdmBase instanceof DescriptionElementBase){
928
				source = DescriptionElementSource.NewInstance(type, strId, namespace, citation, microCitation);
929
			}else{
930
				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.");
931
				return;
932
			}
933
			sourceable.addSource(source);
934
		}else if (cdmBase != null){
935
			logger.warn("Sourced object does not implement ISourceable: " + cdmBase.getClass() + "," + cdmBase.getUuid());
936
		}else{
937
			logger.warn("Sourced object is null");
938
		}
939
	}
940

    
941
	/**
942
	 * @see #addOriginalSource(CdmBase, Object, String, Reference)
943
	 * @param rs
944
	 * @param cdmBase
945
	 * @param dbIdAttribute
946
	 * @param namespace
947
	 * @param citation
948
	 * @throws SQLException
949
	 */
950
	public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
951
		Object id = rs.getObject(dbIdAttribute);
952
		addOriginalSource(cdmBase, id, namespace, citation);
953
	}
954

    
955

    
956
	/**
957
	 * If the child taxon is missing genus or species epithet information and the rank is below <i>genus</i>
958
	 * or <i>species</i> respectively the according epithets are taken from the parent taxon.
959
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
960
	 * the parent.
961
	 * @param parentTaxon
962
	 * @param childTaxon
963
	 */
964
	protected void fillMissingEpithetsForTaxa(Taxon parentTaxon, Taxon childTaxon) {
965
		if (parentTaxon == null){
966
			logger.warn("Parent taxon is null. Missing name parts can not be taken from parent");
967
			return;
968
		}
969
		NonViralName<?> parentName = HibernateProxyHelper.deproxy(parentTaxon.getName(), NonViralName.class);
970
		NonViralName<?> childName = HibernateProxyHelper.deproxy(childTaxon.getName(), NonViralName.class);
971
		fillMissingEpithets(parentName, childName);
972
	}
973

    
974
	/**
975
	 * If the child name is missing genus or species epithet information and the rank is below <i>genus</i>
976
	 * or <i>species</i> respectively the according epithets are taken from the parent name.
977
	 * If the name is an autonym and has no combination author/basionym author the authors are taken from
978
	 * the parent.
979
	 * @param parentTaxon
980
	 * @param childTaxon
981
	 */
982
	protected void fillMissingEpithets(NonViralName parentName, NonViralName childName) {
983
		if (StringUtils.isBlank(childName.getGenusOrUninomial()) && childName.getRank().isLower(Rank.GENUS()) ){
984
			childName.setGenusOrUninomial(parentName.getGenusOrUninomial());
985
		}
986

    
987
		if (StringUtils.isBlank(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
988
			childName.setSpecificEpithet(parentName.getSpecificEpithet());
989
		}
990
		if (childName.isAutonym() && childName.getCombinationAuthorship() == null && childName.getBasionymAuthorship() == null ){
991
			childName.setCombinationAuthorship(parentName.getCombinationAuthorship());
992
			childName.setBasionymAuthorship(parentName.getBasionymAuthorship());
993
		}
994
	}
995

    
996
	/**
997
	 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
998
	 * an arbitrary one is chosen.
999
	 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
1000
	 * is <code>true</code>.
1001
	 * @param createNewIfNotExists
1002
	 * @param isImageGallery if true only taxon description being image galleries are considered.
1003
	 * If false only taxon description being no image galleries are considered.
1004
	 * @return
1005
	 */
1006
	public TaxonNameDescription getTaxonNameDescription(TaxonNameBase name, boolean isImageGallery, boolean createNewIfNotExists) {
1007
		Reference ref = null;
1008
		return getTaxonNameDescription(name, ref, isImageGallery, createNewIfNotExists);
1009
	}
1010

    
1011
	/**
1012
	 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
1013
	 * Only matches a description if the given reference is a source of the description.<BR>
1014
	 * If a new description is created the given reference will be added as a source.
1015
	 *
1016
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1017
	 */
1018
	public TaxonNameDescription getTaxonNameDescription(TaxonNameBase<?,?> name, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1019
		TaxonNameDescription result = null;
1020
		Set<TaxonNameDescription> descriptions= name.getDescriptions();
1021
		for (TaxonNameDescription description : descriptions){
1022
			if (description.isImageGallery() == isImageGallery){
1023
				if (hasCorrespondingSource(ref, description)){
1024
					result = description;
1025
					break;
1026
				}
1027
			}
1028
		}
1029
		if (result == null && createNewIfNotExists){
1030
			result = TaxonNameDescription.NewInstance(name);
1031
			result.setImageGallery(isImageGallery);
1032
			if (ref != null){
1033
				result.addImportSource(null, null, ref, null);
1034
			}
1035
		}
1036
		return result;
1037
	}
1038

    
1039
	/**
1040
	 * Returns the taxon description for a taxon. If there are multiple taxon descriptions
1041
	 * an arbitrary one is chosen.
1042
	 * If no taxon description exists, a new one is created if <code>createNewIfNotExists</code>
1043
	 * is <code>true</code>.
1044
	 * @param createNewIfNotExists
1045
	 * @param isImageGallery if true only taxon description being image galleries are considered.
1046
	 * If false only taxon description being no image galleries are considered.
1047
	 * @return
1048
	 */
1049
	public TaxonDescription getTaxonDescription(Taxon taxon, boolean isImageGallery, boolean createNewIfNotExists) {
1050
		Reference ref = null;
1051
		return getTaxonDescription(taxon, ref, isImageGallery, createNewIfNotExists);
1052
	}
1053

    
1054
	/**
1055
	 * Like {@link #getTaxonDescription(Taxon, boolean, boolean)}
1056
	 * Only matches a description if the given reference is a source of the description.<BR>
1057
	 * If a new description is created the given reference will be added as a source.
1058
	 *
1059
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1060
	 */
1061
	public TaxonDescription getTaxonDescription(Taxon taxon, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1062
		TaxonDescription result = null;
1063
		Set<TaxonDescription> descriptions= taxon.getDescriptions();
1064
		for (TaxonDescription description : descriptions){
1065
			if (description.isImageGallery() == isImageGallery){
1066
				if (hasCorrespondingSource(ref, description)){
1067
					result = description;
1068
					break;
1069
				}
1070
			}
1071
		}
1072
		if (result == null && createNewIfNotExists){
1073
			result = TaxonDescription.NewInstance(taxon);
1074
			result.setImageGallery(isImageGallery);
1075
			if (ref != null){
1076
				result.addImportSource(null, null, ref, null);
1077
			}
1078
		}
1079
		return result;
1080
	}
1081

    
1082

    
1083
	/**
1084
	 * Returns the {@link SpecimenDescription specimen description} for a {@link SpecimenOrObservationBase specimen or observation}.
1085
	 * If there are multiple specimen descriptions an arbitrary one is chosen.
1086
	 * If no specimen description exists, a new one is created if <code>createNewIfNotExists</code> is <code>true</code>.
1087
	 * @param createNewIfNotExists
1088
	 * @param isImageGallery if true only specimen description being image galleries are considered.
1089
	 * If false only specimen description being no image galleries are considered.
1090
	 * @return
1091
	 */
1092
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, boolean isImageGallery, boolean createNewIfNotExists) {
1093
		Reference ref = null;
1094
		return getSpecimenDescription(specimen, ref, isImageGallery, createNewIfNotExists);
1095
	}
1096

    
1097
	/**
1098
	 * Like {@link #getSpecimenDescription(SpecimenOrObservationBase, boolean, boolean)}
1099
	 * Only matches a description if the given reference is a source of the description.<BR>
1100
	 * If a new description is created the given reference will be added as a source.
1101
	 *
1102
	 * @see #getTaxonDescription(Taxon, boolean, boolean)
1103
	 */
1104
	public SpecimenDescription getSpecimenDescription(SpecimenOrObservationBase specimen, Reference ref, boolean isImageGallery, boolean createNewIfNotExists) {
1105
		SpecimenDescription result = null;
1106
		Set<SpecimenDescription> descriptions= specimen.getDescriptions();
1107
		for (SpecimenDescription description : descriptions){
1108
			if (description.isImageGallery() == isImageGallery){
1109
				if (hasCorrespondingSource(ref, description)){
1110
					result = description;
1111
					break;
1112
				}
1113
			}
1114
		}
1115
		if (result == null && createNewIfNotExists){
1116
			result = SpecimenDescription.NewInstance(specimen);
1117
			result.setImageGallery(isImageGallery);
1118
			if (ref != null){
1119
				result.addImportSource(null, null, ref, null);
1120
			}
1121
		}
1122
		return result;
1123
	}
1124

    
1125

    
1126
	/**
1127
	 * Returns the textdata that holds general information about a feature for a taxon description.
1128
	 * This is mainly necessary for descriptions that have more than one description element for
1129
	 * a given feature such as 'distribution', 'description' or 'common name'. It may also hold
1130
	 * for hierarchical features where no description element exists for a higher hierarchy level.
1131
	 * Example: the description feature has subfeatures. But some information like authorship, figures,
1132
	 * sources need to be added to the description itself.
1133
	 * Currently a feature placeholder is marked by a marker of type 'feature placeholder'. Maybe in future
1134
	 * there will be a boolean marker in the TextData class itself.
1135
	 * @param state
1136
	 * @param feature
1137
	 * @param taxon
1138
	 * @param ref
1139
	 * @param createIfNotExists
1140
	 * @return
1141
	 */
1142
	protected TextData getFeaturePlaceholder(STATE state, DescriptionBase<?> description, Feature feature, boolean createIfNotExists) {
1143
		UUID featurePlaceholderUuid = MarkupTransformer.uuidFeaturePlaceholder;
1144
		for (DescriptionElementBase element : description.getElements()){
1145
			if (element.isInstanceOf(TextData.class)){
1146
				TextData textData = CdmBase.deproxy(element, TextData.class);
1147
				if (textData.getFeature() == null || ! textData.getFeature().equals(feature)){
1148
					continue;
1149
				}
1150
				for (Marker marker : textData.getMarkers()){
1151
					MarkerType markerType = marker.getMarkerType();
1152
					if (markerType != null &&
1153
							markerType.getUuid().equals(featurePlaceholderUuid) &&
1154
							marker.getValue() == true){
1155
						return textData;
1156
					}
1157
				}
1158
			}
1159
		}
1160
		if (createIfNotExists){
1161
			TextData newPlaceholder = TextData.NewInstance(feature);
1162
			MarkerType placeholderMarkerType = getMarkerType(state, featurePlaceholderUuid, "Feature Placeholder", "Feature Placeholder", null);
1163
			Marker marker = Marker.NewInstance(placeholderMarkerType, true);
1164
			newPlaceholder.addMarker(marker);
1165
			description.addElement(newPlaceholder);
1166
			return newPlaceholder;
1167
		}else{
1168
			return null;
1169
		}
1170
	}
1171

    
1172

    
1173

    
1174
	/**
1175
	 * Returns true, if this description has a source with a citation equal to the given reference.
1176
	 * Returns true if the given reference is null.
1177
	 * @param ref
1178
	 * @param description
1179
	 */
1180
	private boolean hasCorrespondingSource(Reference ref, DescriptionBase<?> description) {
1181
		if (ref != null){
1182
			for (IdentifiableSource source : description.getSources()){
1183
				if (ref.equals(source.getCitation())){
1184
					return true;
1185
				}
1186
			}
1187
			return false;
1188
		}
1189
		return true;
1190

    
1191
	}
1192

    
1193

    
1194
	/**
1195
	 * Returns the accepted taxon of a {@link TaxonBase taxon base}. <BR>
1196
	 * If taxonBase is of type taxon the same object is returned. If taxonBase is of type
1197
	 * synonym the accepted taxon is returned if one exists. If no accepted taxon exists
1198
	 * <code>null</code> is returned. If multiple accepted taxa exist the one taxon with the
1199
	 * same secundum reference is returned. If no such single taxon exists an
1200
	 * {@link IllegalStateException illegal state exception} is thrown.
1201
	 * @param taxonBase
1202
	 * @return
1203
	 */
1204
	protected Taxon getAcceptedTaxon(TaxonBase<?> taxonBase) {
1205
		if (taxonBase == null){
1206
			return null;
1207
		}else if(taxonBase.isInstanceOf(Taxon.class)){
1208
			return CdmBase.deproxy(taxonBase, Taxon.class);
1209
		}else if(taxonBase.isInstanceOf(Synonym.class)){
1210
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
1211
			Set<Taxon> acceptedTaxa = synonym.getAcceptedTaxa();
1212
			if (acceptedTaxa.size() == 0){
1213
				return null;
1214
			}else if (acceptedTaxa.size() == 1){
1215
				return acceptedTaxa.iterator().next();
1216
			}else{
1217
				Reference sec = synonym.getSec();
1218
				if (sec != null){
1219
					Set<Taxon> taxaWithSameSec = new HashSet<Taxon>();
1220
					for (Taxon taxon: acceptedTaxa){
1221
						if (sec.equals(taxon.getSec())){
1222
							taxaWithSameSec.add(taxon);
1223
						}
1224
					}
1225
					if (taxaWithSameSec.size() == 1){
1226
						return taxaWithSameSec.iterator().next();
1227
					}
1228
				}
1229
				throw new IllegalStateException("Can't define the one accepted taxon for a synonym out of multiple accept taxa");
1230
			}
1231
		}else{
1232
			throw new IllegalStateException("Unknown TaxonBase subclass: " + taxonBase.getClass().getName());
1233
		}
1234
	}
1235

    
1236

    
1237

    
1238
	/**
1239
	 * Creates
1240
	 * @param uriString
1241
	 * @param readDataFromUrl
1242
	 * @see #READ_MEDIA_DATA
1243
	 * @return
1244
	 * @throws MalformedURLException
1245
	 */
1246
	protected Media getImageMedia(String uriString, boolean readMediaData) throws MalformedURLException {
1247
		if( uriString == null){
1248
			return null;
1249
		} else {
1250
			ImageInfo imageInfo = null;
1251
			URI uri;
1252
			uriString = uriString.replace(" ", "%20");  //replace whitespace
1253
			try {
1254
				uri = new URI(uriString);
1255
				try {
1256
					if (readMediaData){
1257
						logger.info("Read media data from: " + uri);
1258
						imageInfo = ImageInfo.NewInstance(uri, 0);
1259
					}
1260
				} catch (Exception e) {
1261
					String message = "An error occurred when trying to read image meta data for " + uri.toString() + ": " +  e.getMessage();
1262
					logger.warn(message);
1263
					fireWarningEvent(message, "unknown location", 2, 0);
1264
				}
1265
				ImageFile imageFile = ImageFile.NewInstance(uri, null, imageInfo);
1266
				MediaRepresentation representation = MediaRepresentation.NewInstance();
1267
				if(imageInfo != null){
1268
					representation.setMimeType(imageInfo.getMimeType());
1269
					representation.setSuffix(imageInfo.getSuffix());
1270
				}
1271
				representation.addRepresentationPart(imageFile);
1272
				Media media = Media.NewInstance();
1273
				media.addRepresentation(representation);
1274
				return media;
1275
			} catch (URISyntaxException e1) {
1276
				String message = "An URISyntaxException occurred when trying to create uri from multimedia objcet string: " +  uriString;
1277
				logger.warn(message);
1278
				fireWarningEvent(message, "unknown location", 4, 0);
1279
				return null;
1280
			}
1281
		}
1282
	}
1283

    
1284

    
1285
	/**
1286
	 * Retrieves an Integer value from a result set. If the value is NULL null is returned.
1287
	 * ResultSet.getInt() returns 0 therefore we need a special handling for this case.
1288
	 * @param rs
1289
	 * @param columnName
1290
	 * @return
1291
	 * @throws SQLException
1292
	 */
1293
	protected Integer nullSafeInt(ResultSet rs, String columnName) throws SQLException {
1294
		Object intObject = rs.getObject(columnName);
1295
		if (intObject == null){
1296
			return null;
1297
		}else{
1298
			return Integer.valueOf(intObject.toString());
1299
		}
1300
	}
1301

    
1302
	protected Boolean nullSafeBoolean(ResultSet rs, String columnName) throws SQLException {
1303
		Object bitObject = rs.getObject(columnName);
1304
		if (bitObject == null){
1305
			return null;
1306
		}else{
1307
			return Boolean.valueOf(bitObject.toString());
1308
		}
1309
	}
1310

    
1311
	protected Double nullSafeDouble(ResultSet rs, String columnName) throws SQLException {
1312
		Object doubleObject = rs.getObject(columnName);
1313
		if (doubleObject == null){
1314
			return null;
1315
		}else{
1316
			return Double.valueOf(doubleObject.toString());
1317
		}
1318
	}
1319

    
1320
	protected Float nullSafeFloat(ResultSet rs, String columnName) throws SQLException {
1321
		Object doubleObject = rs.getObject(columnName);
1322
		if (doubleObject == null){
1323
			return null;
1324
		}else{
1325
			return Float.valueOf(doubleObject.toString());
1326
		}
1327
	}
1328

    
1329

    
1330
	/**
1331
	 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1332
	 * @param str
1333
	 * @return
1334
	 */
1335
	protected String NB(String str) {
1336
		if (StringUtils.isBlank(str)){
1337
			return null;
1338
		}else{
1339
			return str;
1340
		}
1341
	}
1342

    
1343
	@Override
1344
    public byte[] getByteArray() {
1345
        // TODO Auto-generated method stub
1346
        return null;
1347
    }
1348

    
1349
}
(10-10/59)