Project

General

Profile

Download (52.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.agent.Person;
34
import eu.etaxonomy.cdm.model.agent.Team;
35
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
36
import eu.etaxonomy.cdm.model.common.AnnotationType;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.DefinedTerm;
39
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
40
import eu.etaxonomy.cdm.model.common.ExtensionType;
41
import eu.etaxonomy.cdm.model.common.IOriginalSource;
42
import eu.etaxonomy.cdm.model.common.ISourceable;
43
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
44
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
45
import eu.etaxonomy.cdm.model.common.Language;
46
import eu.etaxonomy.cdm.model.common.Marker;
47
import eu.etaxonomy.cdm.model.common.MarkerType;
48
import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
49
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
50
import eu.etaxonomy.cdm.model.common.TermType;
51
import eu.etaxonomy.cdm.model.common.TermVocabulary;
52
import eu.etaxonomy.cdm.model.description.DescriptionBase;
53
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
54
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
55
import eu.etaxonomy.cdm.model.description.Feature;
56
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
57
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
58
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
59
import eu.etaxonomy.cdm.model.description.State;
60
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
61
import eu.etaxonomy.cdm.model.description.TaxonDescription;
62
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
63
import eu.etaxonomy.cdm.model.description.TextData;
64
import eu.etaxonomy.cdm.model.location.Country;
65
import eu.etaxonomy.cdm.model.location.NamedArea;
66
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
67
import eu.etaxonomy.cdm.model.location.NamedAreaType;
68
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
69
import eu.etaxonomy.cdm.model.media.ImageFile;
70
import eu.etaxonomy.cdm.model.media.Media;
71
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
72
import eu.etaxonomy.cdm.model.name.NonViralName;
73
import eu.etaxonomy.cdm.model.name.Rank;
74
import eu.etaxonomy.cdm.model.name.RankClass;
75
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
76
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
77
import eu.etaxonomy.cdm.model.reference.Reference;
78
import eu.etaxonomy.cdm.model.taxon.Classification;
79
import eu.etaxonomy.cdm.model.taxon.Synonym;
80
import eu.etaxonomy.cdm.model.taxon.Taxon;
81
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
82
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
83

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

    
91
    private static Logger logger = Logger.getLogger(CdmImportBase.class);
92

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

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

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

    
115

    
116

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

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

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

    
132

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

    
155

    
156
	}
157

    
158
	protected Classification makeTree(STATE state, Reference reference){
159
		String treeName = "Classification (Import)";
160
		if (reference != null && StringUtils.isNotBlank(reference.getTitleCache())){
161
			treeName = reference.getTitleCache();
162
		}
163
		Classification tree = Classification.NewInstance(treeName);
164
		tree.setReference(reference);
165

    
166

    
167
		// use defined uuid for first tree
168
		CONFIG config = (CONFIG)state.getConfig();
169
		if (state.countTrees() < 1 ){
170
			tree.setUuid(config.getClassificationUuid());
171
		}
172
		getClassificationService().save(tree);
173
		state.putTree(reference, tree);
174
		return tree;
175
	}
176

    
177

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

    
196

    
197
		// use defined uuid for first tree
198
		CONFIG config = (CONFIG)state.getConfig();
199
		if (state.countTrees() < 1 ){
200
			tree.setUuid(config.getClassificationUuid());
201
		}
202
		getClassificationService().save(tree);
203
		state.putTreeUuid(ref, tree);
204
		return tree;
205
	}
206

    
207

    
208

    
209

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

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

    
257

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

    
278
	protected MarkerType getMarkerType(STATE state, UUID uuid, String label, String text, String labelAbbrev){
279
		return getMarkerType(state, uuid, label, text, labelAbbrev, null);
280
	}
281

    
282

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

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

    
320
				voc.addTerm(annotationType);
321
				getTermService().save(annotationType);
322
			}
323
			state.putAnnotationType(annotationType);
324
		}
325
		return annotationType;
326
	}
327

    
328

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

    
350
	}
351

    
352

    
353

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

    
379
	}
380

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

    
396
	protected NamedArea getNamedArea(STATE state, UUID uuid, String label, String text, String labelAbbrev, NamedAreaType areaType, NamedAreaLevel level, TermVocabulary voc, TermMatchMode matchMode){
397
		return getNamedArea(state, uuid, label, text, labelAbbrev, areaType, level, voc, matchMode, null);
398
	}
399

    
400

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

    
415
			if (vocabularyPreference == null){
416
				vocabularyPreference =  new ArrayList<TermVocabulary<NamedArea>>();
417
			}
418
			if (vocabularyPreference.isEmpty()){  //add TDWG vocabulary if preferences are empty
419
				vocabularyPreference.add(Country.GERMANY().getVocabulary());
420
				vocabularyPreference.add(TdwgAreaProvider.getAreaByTdwgAbbreviation("GER").getVocabulary());
421
			}
422

    
423

    
424
			//TODO matching still experimental
425
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_LABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL ))){
426
				//TODO test
427
				Pager<NamedArea> areaPager = (Pager)getTermService().findByTitle(clazz, label, null, null, null, null, null, null);
428
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
429
			}
430
			if (namedArea == null && (matchMode.equals(TermMatchMode.UUID_ABBREVLABEL) || matchMode.equals(TermMatchMode.UUID_LABEL_ABBREVLABEL))){
431
				Pager<NamedArea> areaPager = getTermService().findByRepresentationAbbreviation(labelAbbrev, clazz, null, null);
432
				namedArea = findBestMatchingArea(areaPager, uuid, label, text, labelAbbrev, vocabularyPreference);
433
			}
434

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

    
452

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

    
489

    
490
	private List<NamedArea> getLowestLevelAreas(List<NamedArea> preferredList) {
491
		List<NamedArea> result = new ArrayList<NamedArea>();
492
		for (NamedArea area : preferredList){
493
			if (result.isEmpty()){
494
				result.add(area);
495
			}else {
496
				int compare = compareAreaLevel(area, result.get(0));
497
				if (compare < 0){
498
					result = new ArrayList<NamedArea>();
499
					result.add(area);
500
				}else if (compare == 0){
501
					result.add(area);
502
				}else{
503
					//do nothing
504
				}
505

    
506
			}
507
		}
508

    
509
		return result;
510
	}
511

    
512

    
513
	private int compareAreaLevel(NamedArea area1, NamedArea area2) {
514
		NamedAreaLevel level1 = area1.getLevel();
515
		NamedAreaLevel level2 = area2.getLevel();
516
		if (level1 == null){
517
			return (level2 == null)? 0 : 1;
518
		}else if (level2 == null){
519
			return -1;
520
		}else{
521
			return level1.compareTo(level2);
522
		}
523
	}
524

    
525

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

    
561
	/**
562
	 * Returns a {@link State} if it exists. <code>null</code> otherwise.
563
	 * @param state
564
	 * @param uuid
565
	 * @return {@link State}
566
	 */
567
	protected State getStateTerm(STATE state, UUID uuid){
568
		return getStateTerm(state, uuid, null, null, null, null);
569
	}
570

    
571

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

    
609
	/**
610
	 * Returns a feature if it exists, null otherwise.
611
	 * @see #getFeature(ImportStateBase, UUID, String, String, String, TermVocabulary)
612
	 * @param state
613
	 * @param uuid
614
	 * @return
615
	 */
616
	protected Feature getFeature(STATE state, UUID uuid){
617
		return getFeature(state, uuid, null, null, null, null);
618
	}
619

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

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

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

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

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

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

    
809
	private boolean hasNoLabel(String label, String text, String labelAbbrev) {
810
		return label == null && text == null && labelAbbrev == null;
811
	}
812

    
813
	protected PresenceAbsenceTerm getPresenceTerm(STATE state, UUID uuid, String label, String text, String labelAbbrev, boolean isAbsenceTerm){
814
	    return getPresenceTerm(state, uuid, label, text, labelAbbrev, isAbsenceTerm, null);
815
	}
816

    
817

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

    
851
	/**
852
	 * Returns a language for a given uuid by first ...
853
	 * @param state
854
	 * @param uuid
855
	 * @param label
856
	 * @param text
857
	 * @param labelAbbrev
858
	 * @return
859
	 */
860
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev){
861
		return getLanguage(state, uuid, label, text, labelAbbrev, null);
862
	}
863

    
864
	protected Language getLanguage(STATE state, UUID uuid, String label, String text, String labelAbbrev, TermVocabulary voc){
865
		if (uuid == null){
866
			return null;
867
		}
868
		Language language = state.getLanguage(uuid);
869
		if (language == null){
870
			language = (Language)getTermService().find(uuid);
871
			if (language == null){
872
				language = Language.NewInstance(text, label, labelAbbrev);
873

    
874
				language.setUuid(uuid);
875
				if (voc == null){
876
					UUID uuidLanguageVoc = UUID.fromString("463a96f1-20ba-4a4c-9133-854c1682bd9b");
877
					boolean isOrdered = false;
878
					voc = getVocabulary(TermType.Language, uuidLanguageVoc, "User defined languages", "User defined languages", "User defined languages", null, isOrdered, language);
879
				}
880
				//set vocabulary ; FIXME use another user-defined vocabulary
881

    
882
				voc.addTerm(language);
883
				getTermService().save(language);
884
			}
885
			state.putLanguage(language);
886
		}
887
		return language;
888
	}
889

    
890

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

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

    
947
	/**
948
	 * @see #addOriginalSource(CdmBase, Object, String, Reference)
949
	 * @param rs
950
	 * @param cdmBase
951
	 * @param dbIdAttribute
952
	 * @param namespace
953
	 * @param citation
954
	 * @throws SQLException
955
	 */
956
	public void addOriginalSource(ResultSet rs, CdmBase cdmBase, String dbIdAttribute, String namespace, Reference citation) throws SQLException {
957
		Object id = rs.getObject(dbIdAttribute);
958
		addOriginalSource(cdmBase, id, namespace, citation);
959
	}
960

    
961

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

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

    
993
		if (StringUtils.isBlank(childName.getSpecificEpithet()) && childName.getRank().isLower(Rank.SPECIES()) ){
994
			childName.setSpecificEpithet(parentName.getSpecificEpithet());
995
		}
996
		if (childName.isAutonym() && childName.getCombinationAuthorship() == null && childName.getBasionymAuthorship() == null ){
997
			childName.setCombinationAuthorship(parentName.getCombinationAuthorship());
998
			childName.setBasionymAuthorship(parentName.getBasionymAuthorship());
999
		}
1000
	}
1001

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

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

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

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

    
1088

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

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

    
1131

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

    
1178

    
1179

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

    
1197
	}
1198

    
1199

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

    
1242

    
1243

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

    
1290

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

    
1308
	protected Boolean nullSafeBoolean(ResultSet rs, String columnName) throws SQLException {
1309
		Object bitObject = rs.getObject(columnName);
1310
		if (bitObject == null){
1311
			return null;
1312
		}else{
1313
			return Boolean.valueOf(bitObject.toString());
1314
		}
1315
	}
1316

    
1317
	protected Double nullSafeDouble(ResultSet rs, String columnName) throws SQLException {
1318
		Object doubleObject = rs.getObject(columnName);
1319
		if (doubleObject == null){
1320
			return null;
1321
		}else{
1322
			return Double.valueOf(doubleObject.toString());
1323
		}
1324
	}
1325

    
1326
	protected Float nullSafeFloat(ResultSet rs, String columnName) throws SQLException {
1327
		Object doubleObject = rs.getObject(columnName);
1328
		if (doubleObject == null){
1329
			return null;
1330
		}else{
1331
			return Float.valueOf(doubleObject.toString());
1332
		}
1333
	}
1334

    
1335

    
1336
	/**
1337
	 * Returns <code>null</code> for all blank strings. Identity function otherwise.
1338
	 * @param str
1339
	 * @return
1340
	 */
1341
	protected String NB(String str) {
1342
		if (StringUtils.isBlank(str)){
1343
			return null;
1344
		}else{
1345
			return str;
1346
		}
1347
	}
1348

    
1349
	@Override
1350
    public byte[] getByteArray() {
1351
        // TODO Auto-generated method stub
1352
        return null;
1353
    }
1354

    
1355
	public static TeamOrPersonBase<?> parseAuthorString(String authorName){
1356
        TeamOrPersonBase<?> author = null;
1357
        String[] teamMembers = authorName.split(authorSeparator);
1358
        String lastMember;
1359
        String[] lastMembers;
1360
        Person teamMember;
1361
        if (teamMembers.length>1){
1362
            lastMember = teamMembers[teamMembers.length -1];
1363
            lastMembers = lastMember.split(lastAuthorSeparator);
1364
            teamMembers[teamMembers.length -1] = "";
1365
            author = Team.NewInstance();
1366
            for(String member:teamMembers){
1367
                if (!member.equals("")){
1368
                    teamMember = Person.NewInstance();
1369
                    teamMember.setTitleCache(member, true);
1370
                   ((Team)author).addTeamMember(teamMember);
1371
                }
1372
            }
1373
            if (lastMembers != null){
1374
                for(String member:lastMembers){
1375
                   teamMember = Person.NewInstance();
1376
                   teamMember.setTitleCache(member, true);
1377
                   ((Team)author).addTeamMember(teamMember);
1378
                }
1379
            }
1380

    
1381
        } else {
1382
            teamMembers = authorName.split(lastAuthorSeparator);
1383
            if (teamMembers.length>1){
1384
                author = Team.NewInstance();
1385
                for(String member:teamMembers){
1386
                  teamMember = Person.NewInstance();
1387
                  teamMember.setTitleCache(member, true);
1388
                  ((Team)author).addTeamMember(teamMember);
1389

    
1390
                }
1391
            }else{
1392
                author = Person.NewInstance();
1393
                author.setTitleCache(authorName, true);
1394
            }
1395
        }
1396
        author.getTitleCache();
1397
        return author;
1398
    }
1399

    
1400

    
1401
}
(10-10/61)