Project

General

Profile

« Previous | Next » 

Revision ad690b5a

Added by Andreas Müller about 8 years ago

Fix javassist error for specimen cache strategy loading #5545

View differences:

cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/facade/DerivedUnitFacade.java
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.api.facade;
11

  
12
import java.beans.PropertyChangeEvent;
13
import java.beans.PropertyChangeListener;
14
import java.text.ParseException;
15
import java.util.ArrayList;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21

  
22
import javax.persistence.Transient;
23

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

  
27
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
28
import eu.etaxonomy.cdm.common.CdmUtils;
29
import eu.etaxonomy.cdm.common.UTF8;
30
import eu.etaxonomy.cdm.model.agent.AgentBase;
31
import eu.etaxonomy.cdm.model.agent.Person;
32
import eu.etaxonomy.cdm.model.common.Annotation;
33
import eu.etaxonomy.cdm.model.common.CdmBase;
34
import eu.etaxonomy.cdm.model.common.DefinedTerm;
35
import eu.etaxonomy.cdm.model.common.IOriginalSource;
36
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
37
import eu.etaxonomy.cdm.model.common.Identifier;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.common.LanguageString;
40
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
41
import eu.etaxonomy.cdm.model.common.TimePeriod;
42
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
43
import eu.etaxonomy.cdm.model.description.Feature;
44
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
45
import eu.etaxonomy.cdm.model.description.TextData;
46
import eu.etaxonomy.cdm.model.location.NamedArea;
47
import eu.etaxonomy.cdm.model.location.Point;
48
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
49
import eu.etaxonomy.cdm.model.media.Media;
50
import eu.etaxonomy.cdm.model.media.Rights;
51
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
52
import eu.etaxonomy.cdm.model.occurrence.Collection;
53
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
54
import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
55
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
56
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
57
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
58
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
59
import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
60
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
61
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
62
import eu.etaxonomy.cdm.model.reference.Reference;
63

  
64
/**
65
 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
66
 * a specimen based view. It does not support all functionality available in the
67
 * occurrence package.<BR>
68
 * The most significant restriction is that a specimen may derive only from one
69
 * direct derivation event and there must be only one field unit
70
 * (gathering event) it derives from.<BR>
71
 *
72
 * @author a.mueller
73
 * @date 14.05.2010
74
 */
75
public class DerivedUnitFacade {
76
	private static final String METER = "m";
77

  
78
	@SuppressWarnings("unused")
79
	private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);
80

  
81
	private static final String notSupportMessage = "A specimen facade not supported exception has occurred at a place where this should not have happened. The developer should implement not support check properly during class initialization ";
82

  
83
	private static final boolean CREATE = true;
84
	private static final boolean CREATE_NOT = false;
85

  
86
	private final DerivedUnitFacadeConfigurator config;
87

  
88
	private final Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();
89

  
90
	// Either fieldUnit or derivedUnit must not be null.
91
	private FieldUnit fieldUnit;
92
	private final DerivedUnit derivedUnit;
93

  
94
	// media - the text data holding the media
95
	private TextData derivedUnitMediaTextData;
96
	private TextData fieldObjectMediaTextData;
97

  
98
	private TextData ecology;
99
	private TextData plantDescription;
100

  
101
	/**
102
	 * Creates a derived unit facade for a new derived unit of type
103
	 * <code>type</code>.
104
	 *
105
	 * @param type
106
	 * @return
107
	 */
108
	public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type) {
109
		return new DerivedUnitFacade(type, null, null);
110
	}
111

  
112
	/**
113
	 * Creates a derived unit facade for a new derived unit of type
114
	 * <code>type</code>.
115
	 *
116
	 * @param type
117
	 * @return
118
	 */
119
	public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit) {
120
		return new DerivedUnitFacade(type, fieldUnit, null);
121
	}
122

  
123
	/**
124
	 * Creates a derived unit facade for a new derived unit of type
125
	 * <code>type</code>.
126
	 *
127
	 * @param type
128
	 * @param fieldUnit the field unit to use
129
	 * @param config the facade configurator to use
130
	 * //TODO are there any ambiguities to solve with defining a field unit or a configurator
131
	 * @return
132
	 */
133
	public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
134
		return new DerivedUnitFacade(type, fieldUnit, config);
135
	}
136

  
137

  
138
	/**
139
	 * Creates a derived unit facade for a given derived unit using the default
140
	 * configuration.
141
	 *
142
	 * @param derivedUnit
143
	 * @return
144
	 * @throws DerivedUnitFacadeNotSupportedException
145
	 */
146
	public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit)
147
			throws DerivedUnitFacadeNotSupportedException {
148
		return new DerivedUnitFacade(derivedUnit, null);
149
	}
150

  
151
	public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,
152
			DerivedUnitFacadeConfigurator config)
153
			throws DerivedUnitFacadeNotSupportedException {
154
		return new DerivedUnitFacade(derivedUnit, config);
155
	}
156

  
157
	// ****************** CONSTRUCTOR ******************************************
158

  
159
	private DerivedUnitFacade(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
160
		if (config == null){
161
			config = DerivedUnitFacadeConfigurator.NewInstance();
162
		}
163
		this.config = config;
164
		// derivedUnit
165
		derivedUnit = getNewDerivedUnitInstance(type);
166
		//TODO parameter checking should be solved in a more generic way if we start using other entity facades
167
		if(derivedUnit==null && fieldUnit==null && type.isFieldUnit()){
168
		    this.fieldUnit = getFieldUnit(CREATE);
169
		}
170
		setFieldUnit(fieldUnit);
171
		if (derivedUnit != null){
172
			setCacheStrategy();
173
		}else{
174
			setFieldUnitCacheStrategy();
175
		}
176
	}
177

  
178
	private DerivedUnit getNewDerivedUnitInstance(SpecimenOrObservationType type) {
179
		if (type.isFieldUnit()){
180
			return null;
181
		}else if(type.isAnyDerivedUnit()){
182
			return DerivedUnit.NewInstance(type);
183
		} else {
184
			String message = "Unknown specimen or observation type %s";
185
			message = String.format(message, type.getMessage());
186
			throw new IllegalStateException(message);
187
		}
188
	}
189

  
190
	private DerivedUnitFacade(DerivedUnit derivedUnit, DerivedUnitFacadeConfigurator config)
191
			throws DerivedUnitFacadeNotSupportedException {
192

  
193
	    if(derivedUnit==null){
194
	        throw new IllegalArgumentException("DerivedUnit must not be null");
195
	    }
196

  
197
		if (config == null) {
198
			config = DerivedUnitFacadeConfigurator.NewInstance();
199
		}
200
		this.config = config;
201

  
202
		// derived unit
203
		this.derivedUnit = derivedUnit;
204

  
205
		// derivation event
206
		if (this.derivedUnit.getDerivedFrom() != null) {
207
			DerivationEvent derivationEvent = getDerivationEvent(CREATE);
208
			// fieldUnit
209
			Set<FieldUnit> fieldOriginals = getFieldUnitOriginals(derivationEvent, null);
210
			if (fieldOriginals.size() > 1) {
211
				throw new DerivedUnitFacadeNotSupportedException(
212
						"Specimen must not have more than 1 derivation event");
213
			} else if (fieldOriginals.size() == 0) {
214
				// fieldUnit = FieldUnit.NewInstance();
215
			} else if (fieldOriginals.size() == 1) {
216
				fieldUnit = fieldOriginals.iterator().next();
217
				// ###fieldUnit =
218
				// getInitializedFieldUnit(fieldUnit);
219
				if (config.isFirePropertyChangeEvents()){
220
					addNewEventPropagationListener(fieldUnit);
221
				}
222
			} else {
223
				throw new IllegalStateException("Illegal state");
224
			}
225
		}
226

  
227
		this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);
228

  
229
		fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);
230

  
231

  
232
//direct media have been removed from specimenorobservationbase #3597
233
//		// handle derivedUnit.getMedia()
234
//		if (derivedUnit.getMedia().size() > 0) {
235
//			// TODO better changed model here to allow only one place for images
236
//			if (this.config.isMoveDerivedUnitMediaToGallery()) {
237
//				Set<Media> mediaSet = derivedUnit.getMedia();
238
//				for (Media media : mediaSet) {
239
//					this.addDerivedUnitMedia(media);
240
//				}
241
//				mediaSet.removeAll(getDerivedUnitMedia());
242
//			} else {
243
//				throw new DerivedUnitFacadeNotSupportedException(
244
//						"Specimen may not have direct media. Only (one) image gallery is allowed");
245
//			}
246
//		}
247
//
248
//		// handle fieldUnit.getMedia()
249
//		if (fieldUnit != null && fieldUnit.getMedia() != null
250
//				&& fieldUnit.getMedia().size() > 0) {
251
//			// TODO better changed model here to allow only one place for images
252
//			if (this.config.isMoveFieldObjectMediaToGallery()) {
253
//				Set<Media> mediaSet = fieldUnit.getMedia();
254
//				for (Media media : mediaSet) {
255
//					this.addFieldObjectMedia(media);
256
//				}
257
//				mediaSet.removeAll(getFieldObjectMedia());
258
//			} else {
259
//				throw new DerivedUnitFacadeNotSupportedException(
260
//						"Field object may not have direct media. Only (one) image gallery is allowed");
261
//			}
262
//		}
263

  
264
		// test if descriptions are supported
265
		ecology = initializeFieldObjectTextDataWithSupportTest(
266
				Feature.ECOLOGY(), false, false);
267
		plantDescription = initializeFieldObjectTextDataWithSupportTest(
268
				Feature.DESCRIPTION(), false, false);
269

  
270
		setCacheStrategy();
271

  
272
	}
273

  
274
	private DerivedUnit getInitializedDerivedUnit(
275
			DerivedUnit derivedUnit) {
276
		IOccurrenceService occurrenceService = this.config
277
				.getOccurrenceService();
278
		if (occurrenceService == null) {
279
			return derivedUnit;
280
		}
281
		List<String> propertyPaths = this.config.getPropertyPaths();
282
		if (propertyPaths == null) {
283
			return derivedUnit;
284
		}
285
		propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
286
		DerivedUnit result = (DerivedUnit) occurrenceService.load(
287
				derivedUnit.getUuid(), propertyPaths);
288
		return result;
289
	}
290

  
291
	/**
292
	 * Initializes the derived unit according to the configuartions property
293
	 * path. If the property path is <code>null</code> or no occurrence service
294
	 * is given the returned object is the same as the input parameter.
295
	 *
296
	 * @param fieldUnit
297
	 * @return
298
	 */
299
	private FieldUnit getInitializedFieldUnit(FieldUnit fieldUnit) {
300
		IOccurrenceService occurrenceService = this.config
301
				.getOccurrenceService();
302
		if (occurrenceService == null) {
303
			return fieldUnit;
304
		}
305
		List<String> propertyPaths = this.config.getPropertyPaths();
306
		if (propertyPaths == null) {
307
			return fieldUnit;
308
		}
309
		propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
310
		FieldUnit result = (FieldUnit) occurrenceService.load(
311
				fieldUnit.getUuid(), propertyPaths);
312
		return result;
313
	}
314

  
315
	/**
316
	 * Transforms the property paths in a way that the facade is handled just
317
	 * like an ordinary CdmBase object.<BR>
318
	 * E.g. a property path "collectinAreas" will be translated into
319
	 * gatheringEvent.collectingAreas
320
	 *
321
	 * @param propertyPaths
322
	 * @return
323
	 */
324
	private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
325
		List<String> result = new ArrayList<String>();
326
		for (String facadePath : propertyPaths) {
327
			// collecting areas (named area)
328
			if (facadePath.startsWith("collectingAreas")) {
329
				facadePath = "gatheringEvent." + facadePath;
330
				result.add(facadePath);
331
			}
332
			// collector (agentBase)
333
			else if (facadePath.startsWith("collector")) {
334
				facadePath = facadePath.replace("collector",
335
						"gatheringEvent.actor");
336
				result.add(facadePath);
337
			}
338
			// exactLocation (agentBase)
339
			else if (facadePath.startsWith("exactLocation")) {
340
				facadePath = "gatheringEvent." + facadePath;
341
				result.add(facadePath);
342
			}
343
			// gatheringPeriod (TimePeriod)
344
			else if (facadePath.startsWith("gatheringPeriod")) {
345
				facadePath = facadePath.replace("gatheringPeriod",
346
						"gatheringEvent.timeperiod");
347
				result.add(facadePath);
348
			}
349
			// (locality/ localityLanguage , LanguageString)
350
			else if (facadePath.startsWith("locality")) {
351
				facadePath = "gatheringEvent." + facadePath;
352
				result.add(facadePath);
353
			}
354

  
355
			// *********** FIELD OBJECT ************
356
			// fieldObjectDefinitions (Map<language, languageString)
357
			else if (facadePath.startsWith("fieldObjectDefinitions")) {
358
				// TODO or definition ???
359
				facadePath = facadePath.replace("fieldObjectDefinitions",
360
						"description");
361
				result.add(facadePath);
362
			}
363
			// fieldObjectMedia (Media)
364
			else if (facadePath.startsWith("fieldObjectMedia")) {
365
				// TODO ???
366
				facadePath = facadePath.replace("fieldObjectMedia",
367
						"descriptions.elements.media");
368
				result.add(facadePath);
369
			}
370

  
371
			// Gathering Event will always be added
372
			result.add("gatheringEvent");
373

  
374
		}
375

  
376
		/*
377
		 * Gathering Event ==================== - gatheringEvent
378
		 * (GatheringEvent)
379
		 *
380
		 * Field Object ================= - ecology/ ecologyAll (String) ??? -
381
		 * plant description (like ecology)
382
		 *
383
		 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
384
		 * initialized via fieldObjectMedia
385
		 */
386

  
387
		return result;
388
	}
389

  
390
	/**
391
	 * Transforms the property paths in a way that the facade is handled just
392
	 * like an ordinary CdmBase object.<BR>
393
	 * E.g. a property path "collectinAreas" will be translated into
394
	 * gatheringEvent.collectingAreas
395
	 *
396
	 * Not needed (?) as the facade works with REST service property paths
397
	 * without using this method.
398
	 *
399
	 * @param propertyPaths
400
	 * @return
401
	 */
402
	private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
403
		List<String> result = new ArrayList<String>();
404
		for (String facadePath : propertyPaths) {
405
			// determinations (DeterminationEvent)
406
			if (facadePath.startsWith("determinations")) {
407
				facadePath = "" + facadePath; // no change
408
				result.add(facadePath);
409
			}
410
			// storedUnder (TaxonNameBase)
411
			else if (facadePath.startsWith("storedUnder")) {
412
				facadePath = "" + facadePath; // no change
413
				result.add(facadePath);
414
			}
415
			// sources (IdentifiableSource)
416
			else if (facadePath.startsWith("sources")) {
417
				facadePath = "" + facadePath; // no change
418
				result.add(facadePath);
419
			}
420
			// collection (Collection)
421
			else if (facadePath.startsWith("collection")) {
422
				facadePath = "" + facadePath; // no change
423
				result.add(facadePath);
424
			}
425
			// (locality/ localityLanguage , LanguageString)
426
			else if (facadePath.startsWith("locality")) {
427
				facadePath = "gatheringEvent." + facadePath;
428
				result.add(facadePath);
429
			}
430

  
431
			// *********** FIELD OBJECT ************
432
			// derivedUnitDefinitions (Map<language, languageString)
433
			else if (facadePath.startsWith("derivedUnitDefinitions")) {
434
				// TODO or definition ???
435
				facadePath = facadePath.replace("derivedUnitDefinitions",
436
						"description");
437
				result.add(facadePath);
438
			}
439

  
440
			// derivedUnitMedia (Media)
441
			else if (facadePath.startsWith("derivedUnitMedia")) {
442
				// TODO ???
443
				facadePath = facadePath.replace("derivedUnitMedia",
444
						"descriptions.elements.media");
445
				result.add(facadePath);
446
			}
447

  
448
		}
449

  
450
		/*
451
		 * //TODO Derived Unit =====================
452
		 *
453
		 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
454
		 * initialized via derivedUnitMedia
455
		 *
456
		 * - derivationEvent (DerivationEvent) - will always be initialized -
457
		 * duplicates (??? Specimen???) ???
458
		 */
459

  
460
		return result;
461
	}
462

  
463
	/**
464
	 *
465
	 */
466
	private void setCacheStrategy() {
467
		if (derivedUnit == null) {
468
			throw new NullPointerException(
469
					"Facade's derviedUnit must not be null to set cache strategy");
470
		}else{
471
			derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
472
			setFieldUnitCacheStrategy();
473
		}
474
	}
475

  
476
	private void setFieldUnitCacheStrategy() {
477
		if (this.hasFieldObject()){
478
			DerivedUnitFacadeFieldUnitCacheStrategy strategy = new DerivedUnitFacadeFieldUnitCacheStrategy();
479
			this.fieldUnit.setCacheStrategy(strategy);
480
		}
481
	}
482

  
483
	/**
484
	 * @param feature
485
	 * @param createIfNotExists
486
	 * @param isImageGallery
487
	 * @return
488
	 * @throws DerivedUnitFacadeNotSupportedException
489
	 */
490
	private TextData initializeFieldObjectTextDataWithSupportTest(
491
			Feature feature, boolean createIfNotExists, boolean isImageGallery)
492
			throws DerivedUnitFacadeNotSupportedException {
493
		// field object
494
		FieldUnit fieldObject = getFieldUnit(createIfNotExists);
495
		if (fieldObject == null) {
496
			return null;
497
		}
498
		return inititializeTextDataWithSupportTest(feature, fieldObject,
499
				createIfNotExists, isImageGallery);
500
	}
501

  
502
	/**
503
	 * @param feature
504
	 * @param specimen
505
	 * @param createIfNotExists
506
	 * @param isImageGallery
507
	 * @return
508
	 * @throws DerivedUnitFacadeNotSupportedException
509
	 */
510
	private TextData inititializeTextDataWithSupportTest(Feature feature,
511
			SpecimenOrObservationBase specimen, boolean createIfNotExists,
512
			boolean isImageGallery)
513
			throws DerivedUnitFacadeNotSupportedException {
514
		if (feature == null) {
515
			return null;
516
		}
517
		TextData textData = null;
518
		if (createIfNotExists) {
519
			textData = TextData.NewInstance(feature);
520
		}
521

  
522
		Set<SpecimenDescription> descriptions;
523
		if (isImageGallery) {
524
			descriptions = specimen.getSpecimenDescriptionImageGallery();
525
		} else {
526
			descriptions = specimen.getSpecimenDescriptions(false);
527
		}
528
		// no description exists yet for this specimen
529
		if (descriptions.size() == 0) {
530
			if (createIfNotExists) {
531
				SpecimenDescription newSpecimenDescription = SpecimenDescription
532
						.NewInstance(specimen);
533
				newSpecimenDescription.addElement(textData);
534
				newSpecimenDescription.setImageGallery(isImageGallery);
535
				return textData;
536
			} else {
537
				return null;
538
			}
539
		}
540
		// description already exists
541
		Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
542
		for (SpecimenDescription description : descriptions) {
543
			// collect all existing text data
544
			for (DescriptionElementBase element : description.getElements()) {
545
				if (element.isInstanceOf(TextData.class)
546
						&& (feature.equals(element.getFeature()) || isImageGallery)) {
547
					existingTextData.add(element);
548
				}
549
			}
550
		}
551
		// use existing text data if exactly one exists
552
		if (existingTextData.size() > 1) {
553
			throw new DerivedUnitFacadeNotSupportedException(
554
					"Specimen facade does not support more than one description text data of type "
555
							+ feature.getLabel());
556

  
557
		} else if (existingTextData.size() == 1) {
558
			return CdmBase.deproxy(existingTextData.iterator().next(),
559
					TextData.class);
560
		} else {
561
			if (createIfNotExists) {
562
				SpecimenDescription description = descriptions.iterator()
563
						.next();
564
				description.addElement(textData);
565
			}
566
			return textData;
567
		}
568
	}
569

  
570
	/**
571
	 * Tests if a given image gallery is supported by the derived unit facade.
572
	 * It returns the only text data attached to the given image gallery. If the
573
	 * given image gallery does not have text data attached, it is created and
574
	 * attached.
575
	 *
576
	 * @param imageGallery
577
	 * @return
578
	 * @throws DerivedUnitFacadeNotSupportedException
579
	 */
580
	private TextData testImageGallery(SpecimenDescription imageGallery)
581
			throws DerivedUnitFacadeNotSupportedException {
582
		if (imageGallery.isImageGallery() == false) {
583
			throw new DerivedUnitFacadeNotSupportedException(
584
					"Image gallery needs to have image gallery flag set");
585
		}
586
		if (imageGallery.getElements().size() > 1) {
587
			throw new DerivedUnitFacadeNotSupportedException(
588
					"Image gallery must not have more then one description element");
589
		}
590
		TextData textData;
591
		if (imageGallery.getElements().size() == 0) {
592
			textData = TextData.NewInstance(Feature.IMAGE());
593
			imageGallery.addElement(textData);
594
		} else {
595
			if (!imageGallery.getElements().iterator().next()
596
					.isInstanceOf(TextData.class)) {
597
				throw new DerivedUnitFacadeNotSupportedException(
598
						"Image gallery must only have TextData as element");
599
			} else {
600
				textData = CdmBase.deproxy(imageGallery.getElements()
601
						.iterator().next(), TextData.class);
602
			}
603
		}
604
		return textData;
605
	}
606

  
607
	// ************************** METHODS
608
	// *****************************************
609

  
610
	private TextData getDerivedUnitImageGalleryTextData(
611
			boolean createIfNotExists)
612
			throws DerivedUnitFacadeNotSupportedException {
613
		if (this.derivedUnitMediaTextData == null && createIfNotExists) {
614
			this.derivedUnitMediaTextData = getImageGalleryTextData(
615
					derivedUnit, "Specimen");
616
		}
617
		return this.derivedUnitMediaTextData;
618
	}
619

  
620
	private TextData getObservationImageGalleryTextData(
621
			boolean createIfNotExists)
622
			throws DerivedUnitFacadeNotSupportedException {
623
		if (this.fieldObjectMediaTextData == null && createIfNotExists) {
624
			this.fieldObjectMediaTextData = getImageGalleryTextData(fieldUnit, "Field unit");
625
		}
626
		return this.fieldObjectMediaTextData;
627
	}
628

  
629
	/**
630
	 * @param derivationEvent
631
	 * @return
632
	 * @throws DerivedUnitFacadeNotSupportedException
633
	 */
634
	private Set<FieldUnit> getFieldUnitOriginals(
635
			DerivationEvent derivationEvent,
636
			Set<SpecimenOrObservationBase> recursionAvoidSet)
637
			throws DerivedUnitFacadeNotSupportedException {
638
		if (recursionAvoidSet == null) {
639
			recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
640
		}
641
		Set<FieldUnit> result = new HashSet<FieldUnit>();
642
		Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
643
		for (SpecimenOrObservationBase original : originals) {
644
			if (original.isInstanceOf(FieldUnit.class)) {
645
				result.add(CdmBase.deproxy(original, FieldUnit.class));
646
			} else if (original.isInstanceOf(DerivedUnit.class)) {
647
				// if specimen has already been tested exclude it from further
648
				// recursion
649
				if (recursionAvoidSet.contains(original)) {
650
					continue;
651
				}
652
				DerivedUnit derivedUnit = CdmBase.deproxy(original,	DerivedUnit.class);
653
				DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();
654
				// Set<DerivationEvent> derivationEvents =
655
				// original.getDerivationEvents();
656
				// for (DerivationEvent originalDerivation : derivationEvents){
657
				if(originalDerivation!=null){
658
				    Set<FieldUnit> fieldUnits = getFieldUnitOriginals(
659
				            originalDerivation, recursionAvoidSet);
660
				    result.addAll(fieldUnits);
661
				}
662
				// }
663
			} else {
664
				throw new DerivedUnitFacadeNotSupportedException(
665
						"Unhandled specimen or observation base type: "
666
								+ original.getClass().getName());
667
			}
668

  
669
		}
670
		return result;
671
	}
672

  
673
	// *********** MEDIA METHODS ******************************
674

  
675
	// /**
676
	// * Returns the media list for a specimen. Throws an exception if the
677
	// existing specimen descriptions
678
	// * are not supported by this facade.
679
	// * @param specimen the specimen the media belongs to
680
	// * @param specimenExceptionText text describing the specimen for exception
681
	// messages
682
	// * @return
683
	// * @throws DerivedUnitFacadeNotSupportedException
684
	// */
685
	// private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
686
	// specimen, String specimenExceptionText) throws
687
	// DerivedUnitFacadeNotSupportedException{
688
	// List<Media> result;
689
	// SpecimenDescription imageGallery =
690
	// getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
691
	// TextData textData = getImageTextDataWithSupportTest(imageGallery,
692
	// specimenExceptionText);
693
	// result = textData.getMedia();
694
	// return result;
695
	// }
696

  
697
	/**
698
	 * Returns the media list for a specimen. Throws an exception if the
699
	 * existing specimen descriptions are not supported by this facade.
700
	 *
701
	 * @param specimen
702
	 *            the specimen the media belongs to
703
	 * @param specimenExceptionText
704
	 *            text describing the specimen for exception messages
705
	 * @return
706
	 * @throws DerivedUnitFacadeNotSupportedException
707
	 */
708
	private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)
709
			throws DerivedUnitFacadeNotSupportedException {
710
		TextData result;
711
		SpecimenDescription imageGallery = getImageGalleryWithSupportTest(
712
				specimen, specimenExceptionText, true);
713
		result = getImageTextDataWithSupportTest(imageGallery,
714
				specimenExceptionText);
715
		return result;
716
	}
717

  
718
	/**
719
	 * Returns the image gallery of the according specimen. Throws an exception
720
	 * if the attached image gallerie(s) are not supported by this facade. If no
721
	 * image gallery exists a new one is created if
722
	 * <code>createNewIfNotExists</code> is true and if specimen is not
723
	 * <code>null</code>.
724
	 *
725
	 * @param specimen
726
	 * @param specimenText
727
	 * @param createNewIfNotExists
728
	 * @return
729
	 * @throws DerivedUnitFacadeNotSupportedException
730
	 */
731
	private SpecimenDescription getImageGalleryWithSupportTest(
732
			SpecimenOrObservationBase<?> specimen, String specimenText,
733
			boolean createNewIfNotExists)
734
			throws DerivedUnitFacadeNotSupportedException {
735
		if (specimen == null) {
736
			return null;
737
		}
738
		SpecimenDescription imageGallery;
739
		if (hasMultipleImageGalleries(specimen)) {
740
			throw new DerivedUnitFacadeNotSupportedException(specimenText
741
					+ " must not have more than 1 image gallery");
742
		} else {
743
			imageGallery = getImageGallery(specimen, createNewIfNotExists);
744
			getImageTextDataWithSupportTest(imageGallery, specimenText);
745
		}
746
		return imageGallery;
747
	}
748

  
749
	/**
750
	 * Returns the media holding text data element of the image gallery. Throws
751
	 * an exception if multiple such text data already exist. Creates a new text
752
	 * data if none exists and adds it to the image gallery. If image gallery is
753
	 * <code>null</code> nothing happens.
754
	 *
755
	 * @param imageGallery
756
	 * @param textData
757
	 * @return
758
	 * @throws DerivedUnitFacadeNotSupportedException
759
	 */
760
	private TextData getImageTextDataWithSupportTest(
761
			SpecimenDescription imageGallery, String specimenText)
762
			throws DerivedUnitFacadeNotSupportedException {
763
		if (imageGallery == null) {
764
			return null;
765
		}
766
		TextData textData = null;
767
		for (DescriptionElementBase element : imageGallery.getElements()) {
768
			if (element.isInstanceOf(TextData.class)
769
					&& element.getFeature().equals(Feature.IMAGE())) {
770
				if (textData != null) {
771
					throw new DerivedUnitFacadeNotSupportedException(
772
							specimenText
773
									+ " must not have more than 1 image text data element in image gallery");
774
				}
775
				textData = CdmBase.deproxy(element, TextData.class);
776
			}
777
		}
778
		if (textData == null) {
779
			textData = TextData.NewInstance(Feature.IMAGE());
780
			imageGallery.addElement(textData);
781
		}
782
		return textData;
783
	}
784

  
785
	/**
786
	 * Checks, if a specimen belongs to more than one description that is an
787
	 * image gallery
788
	 *
789
	 * @param derivedUnit
790
	 * @return
791
	 */
792
	private boolean hasMultipleImageGalleries(
793
			SpecimenOrObservationBase<?> derivedUnit) {
794
		int count = 0;
795
		Set<SpecimenDescription> descriptions = derivedUnit
796
				.getSpecimenDescriptions();
797
		for (SpecimenDescription description : descriptions) {
798
			if (description.isImageGallery()) {
799
				count++;
800
			}
801
		}
802
		return (count > 1);
803
	}
804

  
805
	/**
806
	 * Returns the image gallery for a specimen. If there are multiple specimen
807
	 * descriptions marked as image galleries an arbitrary one is chosen. If no
808
	 * image gallery exists, a new one is created if
809
	 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
810
	 * If specimen is <code>null</code> a null pointer exception is thrown.
811
	 *
812
	 * @param createNewIfNotExists
813
	 * @return
814
	 */
815
	private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
816
		SpecimenDescription result = null;
817
		Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();
818
		for (SpecimenDescription description : descriptions) {
819
			if (description.isImageGallery()) {
820
				result = description;
821
				break;
822
			}
823
		}
824
		if (result == null && createIfNotExists) {
825
			result = SpecimenDescription.NewInstance(specimen);
826
			result.setImageGallery(true);
827
		}
828
		return result;
829
	}
830

  
831
	/**
832
	 * Adds a media to the specimens image gallery. If media is
833
	 * <code>null</code> nothing happens.
834
	 *
835
	 * @param media
836
	 * @param specimen
837
	 * @return true if media is not null (as specified by
838
	 *         {@link java.util.Collection#add(Object) Collection.add(E e)}
839
	 * @throws DerivedUnitFacadeNotSupportedException
840
	 */
841
	private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
842
		if (media != null) {
843
			List<Media> mediaList = getMediaList(specimen, true);
844
			if (! mediaList.contains(media)){
845
				return mediaList.add(media);
846
			}else{
847
				return true;
848
			}
849
		} else {
850
			return false;
851
		}
852
	}
853

  
854
	/**
855
	 * Removes a media from the specimens image gallery.
856
	 *
857
	 * @param media
858
	 * @param specimen
859
	 * @return true if an element was removed as a result of this call (as
860
	 *         specified by {@link java.util.Collection#remove(Object)
861
	 *         Collection.remove(E e)}
862
	 * @throws DerivedUnitFacadeNotSupportedException
863
	 */
864
	private boolean removeMedia(Media media,
865
			SpecimenOrObservationBase<?> specimen)
866
			throws DerivedUnitFacadeNotSupportedException {
867
		List<Media> mediaList = getMediaList(specimen, true);
868
		return mediaList == null ? null : mediaList.remove(media);
869
	}
870

  
871
	private List<Media> getMediaList(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists)
872
			throws DerivedUnitFacadeNotSupportedException {
873
		TextData textData = getMediaTextData(specimen, createIfNotExists);
874
		return textData == null ? null : textData.getMedia();
875
	}
876

  
877
	/**
878
	 * Returns the one media list of a specimen which is part of the only image
879
	 * gallery that this specimen is part of.<BR>
880
	 * If these conditions are not hold an exception is thrwon.
881
	 *
882
	 * @param specimen
883
	 * @return
884
	 * @throws DerivedUnitFacadeNotSupportedException
885
	 */
886
	// private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
887
	// throws DerivedUnitFacadeNotSupportedException {
888
	// if (specimen == null){
889
	// return null;
890
	// }
891
	// if (specimen == this.derivedUnit){
892
	// return getDerivedUnitImageGalleryMedia();
893
	// }else if (specimen == this.fieldUnit){
894
	// return getObservationImageGalleryTextData();
895
	// }else{
896
	// return getImageGalleryMedia(specimen, "Undefined specimen ");
897
	// }
898
	// }
899

  
900
	/**
901
	 * Returns the one media list of a specimen which is part of the only image
902
	 * gallery that this specimen is part of.<BR>
903
	 * If these conditions are not hold an exception is thrown.
904
	 *
905
	 * @param specimen
906
	 * @return
907
	 * @throws DerivedUnitFacadeNotSupportedException
908
	 */
909
	private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,
910
			boolean createIfNotExists)
911
			throws DerivedUnitFacadeNotSupportedException {
912
		if (specimen == null) {
913
			return null;
914
		}
915
		if (specimen == this.derivedUnit) {
916
			return getDerivedUnitImageGalleryTextData(createIfNotExists);
917
		} else if (specimen == this.fieldUnit) {
918
			return getObservationImageGalleryTextData(createIfNotExists);
919
		} else {
920
			return getImageGalleryTextData(specimen, "Undefined specimen ");
921
		}
922
	}
923

  
924
	// ****************** GETTER / SETTER / ADDER / REMOVER
925
	// ***********************/
926

  
927
	// ****************** Gathering Event *********************************/
928

  
929
	// country
930
	@Transient
931
	public NamedArea getCountry() {
932
		return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()
933
				: null);
934
	}
935

  
936
	public void setCountry(NamedArea country) {
937
		getGatheringEvent(true).setCountry(country);
938
	}
939

  
940
	// Collecting area
941
	public void addCollectingArea(NamedArea area) {
942
		getGatheringEvent(true).addCollectingArea(area);
943
	}
944

  
945
	public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
946
		for (NamedArea area : areas) {
947
			getGatheringEvent(true).addCollectingArea(area);
948
		}
949
	}
950

  
951
	@Transient
952
	public Set<NamedArea> getCollectingAreas() {
953
		return (hasGatheringEvent() ? getGatheringEvent(true)
954
				.getCollectingAreas() : null);
955
	}
956

  
957
	public void removeCollectingArea(NamedArea area) {
958
		if (hasGatheringEvent()) {
959
			getGatheringEvent(true).removeCollectingArea(area);
960
		}
961
	}
962

  
963
	static final String ALTITUDE_POSTFIX = " m";
964

  
965
	/**
966
	 * Returns the correctly formatted <code>absolute elevation</code> information.
967
	 * If absoluteElevationText is set, this will be returned,
968
	 * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax
969
	 * if existing, separated by " - "
970
	 * @return
971
	 */
972
	@Transient
973
	public String absoluteElevationToString() {
974
		if (! hasGatheringEvent()){
975
			return null;
976
		}else{
977
			GatheringEvent ev = getGatheringEvent(true);
978
			if (StringUtils.isNotBlank(ev.getAbsoluteElevationText())){
979
				return ev.getAbsoluteElevationText();
980
			}else{
981
				String text = ev.getAbsoluteElevationText();
982
				Integer min = getAbsoluteElevation();
983
				Integer max = getAbsoluteElevationMaximum();
984
				return distanceString(min, max, text, METER);
985
			}
986
		}
987
	}
988

  
989

  
990
	/**
991
	 * meter above/below sea level of the surface
992
	 *
993
	 * @see #getAbsoluteElevationError()
994
	 * @see #getAbsoluteElevationRange()
995
	 **/
996
	@Transient
997
	public Integer getAbsoluteElevation() {
998
		return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);
999
	}
1000

  
1001
	public void setAbsoluteElevation(Integer absoluteElevation) {
1002
		getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
1003
	}
1004

  
1005
	public void setAbsoluteElevationMax(Integer absoluteElevationMax) {
1006
		getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);
1007
	}
1008

  
1009
	public void setAbsoluteElevationText(String absoluteElevationText) {
1010
		getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText);
1011
	}
1012

  
1013
	/**
1014
	 * @see #getAbsoluteElevation()
1015
	 * @see #getAbsoluteElevationError()
1016
	 * @see #setAbsoluteElevationRange(Integer, Integer)
1017
	 * @see #getAbsoluteElevationMinimum()
1018
	 */
1019
	@Transient
1020
	public Integer getAbsoluteElevationMaximum() {
1021
		if (!hasGatheringEvent()) {
1022
			return null;
1023
		}else{
1024
			return getGatheringEvent(true).getAbsoluteElevationMax();
1025
		}
1026
	}
1027

  
1028
	/**
1029
	 * @see #getAbsoluteElevation()
1030
	 * @see #getAbsoluteElevationError()
1031
	 * @see #setAbsoluteElevationRange(Integer, Integer)
1032
	 * @see #getAbsoluteElevationMinimum()
1033
	 */
1034
	@Transient
1035
	public String getAbsoluteElevationText() {
1036
		if (!hasGatheringEvent()) {
1037
			return null;
1038
		}else{
1039
			return getGatheringEvent(true).getAbsoluteElevationText();
1040
		}
1041
	}
1042

  
1043
	/**
1044
	 * Convenience method to set absolute elevation minimum and maximum.
1045
	 *
1046
	 * @see #setAbsoluteElevation(Integer)
1047
	 * @see #setAbsoluteElevationMax(Integer)
1048
	 * @param minimumElevation minimum of the range
1049
	 * @param maximumElevation maximum of the range
1050
	 */
1051
	public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {
1052
		getGatheringEvent(true).setAbsoluteElevation(minimumElevation);
1053
		getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);
1054
	}
1055

  
1056
	// collector
1057
	@Transient
1058
	public AgentBase getCollector() {
1059
		return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()
1060
				: null);
1061
	}
1062

  
1063
	public void setCollector(AgentBase collector) {
1064
		getGatheringEvent(true).setCollector(collector);
1065
	}
1066

  
1067
	// collecting method
1068
	@Transient
1069
	public String getCollectingMethod() {
1070
		return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);
1071
	}
1072

  
1073
	public void setCollectingMethod(String collectingMethod) {
1074
		getGatheringEvent(true).setCollectingMethod(collectingMethod);
1075
	}
1076

  
1077
	// distance to ground
1078

  
1079
	/**
1080
	 * Returns the correctly formatted <code>distance to ground</code> information.
1081
	 * If distanceToGroundText is not blank, it will be returned,
1082
	 * otherwise distanceToGround will be returned, followed by distanceToGroundMax
1083
	 * if existing, separated by " - "
1084
	 * @return
1085
	 */
1086
	@Transient
1087
	public String distanceToGroundToString() {
1088
		if (! hasGatheringEvent()){
1089
			return null;
1090
		}else{
1091
			GatheringEvent ev = getGatheringEvent(true);
1092
			String text = ev.getDistanceToGroundText();
1093
			Double min = getDistanceToGround();
1094
			Double max = getDistanceToGroundMax();
1095
			return distanceString(min, max, text, METER);
1096
		}
1097
	}
1098

  
1099
	@Transient
1100
	public Double getDistanceToGround() {
1101
		return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);
1102
	}
1103

  
1104
	public void setDistanceToGround(Double distanceToGround) {
1105
		getGatheringEvent(true).setDistanceToGround(distanceToGround);
1106
	}
1107

  
1108
	/**
1109
	 * @see #getDistanceToGround()
1110
	 * @see #getDistanceToGroundRange(Integer, Integer)
1111
	 */
1112
	@Transient
1113
	public Double getDistanceToGroundMax() {
1114
		if (!hasGatheringEvent()) {
1115
			return null;
1116
		}else{
1117
			return getGatheringEvent(true).getDistanceToGroundMax();
1118
		}
1119
	}
1120

  
1121
	public void setDistanceToGroundMax(Double distanceToGroundMax) {
1122
		getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax);
1123
	}
1124

  
1125
	/**
1126
	 * @see #getDistanceToGround()
1127
	 * @see #setDistanceToGroundRange(Integer, Integer)
1128
	 */
1129
	@Transient
1130
	public String getDistanceToGroundText() {
1131
		if (!hasGatheringEvent()) {
1132
			return null;
1133
		}else{
1134
			return getGatheringEvent(true).getDistanceToGroundText();
1135
		}
1136
	}
1137
	public void setDistanceToGroundText(String distanceToGroundText) {
1138
		getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText);
1139
	}
1140

  
1141
	/**
1142
	 * Convenience method to set distance to ground minimum and maximum.
1143
	 *
1144
	 * @see #getDistanceToGround()
1145
	 * @see #getDistanceToGroundMax()
1146
	 * @param minimumDistance minimum of the range
1147
	 * @param maximumDistance maximum of the range
1148
	 */
1149
	public void setDistanceToGroundRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
1150
		getGatheringEvent(true).setDistanceToGround(minimumDistance);
1151
		getGatheringEvent(true).setDistanceToGroundMax(maximumDistance);
1152
	}
1153

  
1154

  
1155
	/**
1156
	 * Returns the correctly formatted <code>distance to water surface</code> information.
1157
	 * If distanceToWaterSurfaceText is not blank, it will be returned,
1158
	 * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax
1159
	 * if existing, separated by " - "
1160
	 * @return
1161
	 */
1162
	@Transient
1163
	public String distanceToWaterSurfaceToString() {
1164
		if (! hasGatheringEvent()){
1165
			return null;
1166
		}else{
1167
			GatheringEvent ev = getGatheringEvent(true);
1168
			String text = ev.getDistanceToWaterSurfaceText();
1169
			Double min = getDistanceToWaterSurface();
1170
			Double max = getDistanceToWaterSurfaceMax();
1171
			return distanceString(min, max, text, METER);
1172
		}
1173
	}
1174

  
1175
	// distance to water surface
1176
	@Transient
1177
	public Double getDistanceToWaterSurface() {
1178
		return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);
1179
	}
1180

  
1181
	public void setDistanceToWaterSurface(Double distanceToWaterSurface) {
1182
		getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);
1183
	}
1184

  
1185
	/**
1186
	 * @see #getDistanceToWaterSurface()
1187
	 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1188
	 */
1189
	@Transient
1190
	public Double getDistanceToWaterSurfaceMax() {
1191
		if (!hasGatheringEvent()) {
1192
			return null;
1193
		}else{
1194
			return getGatheringEvent(true).getDistanceToWaterSurfaceMax();
1195
		}
1196
	}
1197

  
1198
	public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax) {
1199
		getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax);
1200
	}
1201

  
1202
	/**
1203
	 * @see #getDistanceToWaterSurface()
1204
	 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1205
	 */
1206
	@Transient
1207
	public String getDistanceToWaterSurfaceText() {
1208
		if (!hasGatheringEvent()) {
1209
			return null;
1210
		}else{
1211
			return getGatheringEvent(true).getDistanceToWaterSurfaceText();
1212
		}
1213
	}
1214
	public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText) {
1215
		getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText);
1216
	}
1217

  
1218
	/**
1219
	 * Convenience method to set distance to ground minimum and maximum.
1220
	 *
1221
	 * @see #getDistanceToWaterSurface()
1222
	 * @see #getDistanceToWaterSurfaceMax()
1223
	 * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface
1224
	 * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface
1225
	 */
1226
	public void setDistanceToWaterSurfaceRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
1227
		getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance);
1228
		getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance);
1229
	}
1230

  
1231

  
1232
	// exact location
1233
	@Transient
1234
	public Point getExactLocation() {
1235
		return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null);
1236
	}
1237

  
1238
	/**
1239
	 * Returns a sexagesimal representation of the exact location (e.g.
1240
	 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1241
	 * string is returned.
1242
	 *
1243
	 * @param includeEmptySeconds
1244
	 * @param includeReferenceSystem
1245
	 * @return
1246
	 */
1247
	public String getExactLocationText(boolean includeEmptySeconds,
1248
			boolean includeReferenceSystem) {
1249
		return (this.getExactLocation() == null ? "" : this.getExactLocation()
1250
				.toSexagesimalString(includeEmptySeconds,
1251
						includeReferenceSystem));
1252
	}
1253

  
1254
	public void setExactLocation(Point exactLocation) {
1255
		getGatheringEvent(true).setExactLocation(exactLocation);
1256
	}
1257

  
1258
	public void setExactLocationByParsing(String longitudeToParse,
1259
			String latitudeToParse, ReferenceSystem referenceSystem,
1260
			Integer errorRadius) throws ParseException {
1261
		Point point = Point.NewInstance(null, null, referenceSystem,
1262
				errorRadius);
1263
		point.setLongitudeByParsing(longitudeToParse);
1264
		point.setLatitudeByParsing(latitudeToParse);
1265
		setExactLocation(point);
1266
	}
1267

  
1268
	// gathering event description
1269
	@Transient
1270
	public String getGatheringEventDescription() {
1271
		return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()
1272
				: null);
1273
	}
1274

  
1275
	public void setGatheringEventDescription(String description) {
1276
		getGatheringEvent(true).setDescription(description);
1277
	}
1278

  
1279
	// gatering period
1280
	@Transient
1281
	public TimePeriod getGatheringPeriod() {
1282
		return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()
1283
				: null);
1284
	}
1285

  
1286
	public void setGatheringPeriod(TimePeriod timeperiod) {
1287
		getGatheringEvent(true).setTimeperiod(timeperiod);
1288
	}
1289

  
1290
	// locality
1291
	@Transient
1292
	public LanguageString getLocality() {
1293
		return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()
1294
				: null);
1295
	}
1296

  
1297
	/**
1298
	 * convienience method for {@link #getLocality()}.
1299
	 * {@link LanguageString#getText() getText()}
1300
	 *
1301
	 * @return
1302
	 */
1303
	@Transient
1304
	public String getLocalityText() {
1305
		LanguageString locality = getLocality();
1306
		if (locality != null) {
1307
			return locality.getText();
1308
		}
1309
		return null;
1310
	}
1311

  
1312
	/**
1313
	 * convienience method for {@link #getLocality()}.
1314
	 * {@link LanguageString#getLanguage() getLanguage()}
1315
	 *
1316
	 * @return
1317
	 */
1318
	@Transient
1319
	public Language getLocalityLanguage() {
1320
		LanguageString locality = getLocality();
1321
		if (locality != null) {
1322
			return locality.getLanguage();
1323
		}
1324
		return null;
1325
	}
1326

  
1327
	/**
1328
	 * Sets the locality string in the default language
1329
	 *
1330
	 * @param locality
1331
	 */
1332
	public void setLocality(String locality) {
1333
		Language language = Language.DEFAULT();
1334
		setLocality(locality, language);
1335
	}
1336

  
1337
	public void setLocality(String locality, Language language) {
1338
		LanguageString langString = LanguageString.NewInstance(locality, language);
1339
		setLocality(langString);
1340
	}
1341

  
1342
	public void setLocality(LanguageString locality) {
1343
		getGatheringEvent(true).setLocality(locality);
1344
	}
1345

  
1346
	/**
1347
	 * The gathering event will be used for the field object instead of the old
1348
	 * gathering event.<BR>
1349
	 * <B>This method will override all gathering values (see below).</B>
1350
	 *
1351
	 * @see #getAbsoluteElevation()
1352
	 * @see #getAbsoluteElevationError()
1353
	 * @see #getDistanceToGround()
1354
	 * @see #getDistanceToWaterSurface()
1355
	 * @see #getExactLocation()
1356
	 * @see #getGatheringEventDescription()
1357
	 * @see #getGatheringPeriod()
1358
	 * @see #getCollectingAreas()
1359
	 * @see #getCollectingMethod()
1360
	 * @see #getLocality()
1361
	 * @see #getCollector()
1362
	 * @param gatheringEvent
1363
	 */
1364
	public void setGatheringEvent(GatheringEvent gatheringEvent) {
1365
		getFieldUnit(true).setGatheringEvent(gatheringEvent);
1366
	}
1367

  
1368
	public boolean hasGatheringEvent() {
1369
		return (getGatheringEvent(false) != null);
1370
	}
1371

  
1372
	public GatheringEvent innerGatheringEvent() {
1373
		return getGatheringEvent(false);
1374
	}
1375

  
1376
	public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
1377
		if (!hasFieldUnit() && !createIfNotExists) {
1378
			return null;
1379
		}
1380
		if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {
1381
			GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
1382
			getFieldUnit(true).setGatheringEvent(gatheringEvent);
1383
		}
1384
		return getFieldUnit(true).getGatheringEvent();
1385
	}
1386

  
1387
	// ****************** Field Object ************************************/
1388

  
1389
	/**
1390
	 * Returns true if a field unit exists (even if all attributes are
1391
	 * empty or <code>null<code>.
1392
	 *
1393
	 * @return
1394
	 */
1395
	public boolean hasFieldObject() {
1396
		return this.fieldUnit != null;
1397
	}
1398

  
1399
	// ecology
1400
	@Transient
1401
	public String getEcology() {
1402
		return getEcology(Language.DEFAULT());
1403
	}
1404

  
1405
	public String getEcology(Language language) {
1406
		LanguageString languageString = getEcologyAll().get(language);
1407
		return (languageString == null ? null : languageString.getText());
1408
	}
1409

  
1410
	// public String getEcologyPreferred(List<Language> languages){
1411
	// LanguageString languageString =
1412
	// getEcologyAll().getPreferredLanguageString(languages);
1413
	// return languageString.getText();
1414
	// }
1415
	/**
1416
	 * Returns a copy of the multilanguage text holding the ecology data.
1417
	 *
1418
	 * @see {@link TextData#getMultilanguageText()}
1419
	 * @return
1420
	 */
1421
	@Transient
1422
	public Map<Language, LanguageString> getEcologyAll() {
1423
		if (ecology == null) {
1424
			try {
1425
				ecology = initializeFieldObjectTextDataWithSupportTest(
1426
						Feature.ECOLOGY(), false, false);
1427
			} catch (DerivedUnitFacadeNotSupportedException e) {
1428
				throw new IllegalStateException(notSupportMessage, e);
1429
			}
1430
			if (ecology == null) {
1431
				return new HashMap<Language, LanguageString>();
1432
			}
1433
		}
1434
		return ecology.getMultilanguageText();
1435
	}
1436

  
1437
	public void setEcology(String ecology) {
1438
		setEcology(ecology, null);
1439
	}
1440

  
1441
	public void setEcology(String ecologyText, Language language) {
1442
		if (language == null) {
1443
			language = Language.DEFAULT();
1444
		}
1445
		boolean isEmpty = StringUtils.isBlank(ecologyText);
1446
		if (ecology == null) {
1447
			try {
1448
				ecology = initializeFieldObjectTextDataWithSupportTest(
1449
						Feature.ECOLOGY(), !isEmpty, false);
1450
			} catch (DerivedUnitFacadeNotSupportedException e) {
1451
				throw new IllegalStateException(notSupportMessage, e);
1452
			}
1453
		}
1454
		if (ecology != null){
1455
			if (ecologyText == null) {
1456
				ecology.removeText(language);
1457
			} else {
1458
				ecology.putText(language, ecologyText);
1459
			}
1460
		}
1461
	}
1462

  
1463
	public void removeEcology(Language language) {
1464
		setEcology(null, language);
1465
	}
1466

  
1467
	/**
1468
	 * Removes ecology for the default language
1469
	 */
1470
	public void removeEcology() {
1471
		setEcology(null, null);
1472
	}
1473

  
1474
	public void removeEcologyAll() {
1475

  
1476
	}
1477

  
1478
	// plant description
1479
	@Transient
1480
	public String getPlantDescription() {
1481
		return getPlantDescription(null);
1482
	}
1483

  
1484
	public String getPlantDescription(Language language) {
1485
		if (language == null) {
1486
			language = Language.DEFAULT();
1487
		}
1488
		LanguageString languageString = getPlantDescriptionAll().get(language);
1489
		return (languageString == null ? null : languageString.getText());
1490
	}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff