clearer getters for DerivedUnitFacade + PointBeanProcessor
[cdmlib.git] / 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.log4j.Logger;
25
26 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
27 import eu.etaxonomy.cdm.model.agent.AgentBase;
28 import eu.etaxonomy.cdm.model.common.Annotation;
29 import eu.etaxonomy.cdm.model.common.CdmBase;
30 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
31 import eu.etaxonomy.cdm.model.common.Language;
32 import eu.etaxonomy.cdm.model.common.LanguageString;
33 import eu.etaxonomy.cdm.model.common.TimePeriod;
34 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
35 import eu.etaxonomy.cdm.model.description.Feature;
36 import eu.etaxonomy.cdm.model.description.Sex;
37 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
38 import eu.etaxonomy.cdm.model.description.Stage;
39 import eu.etaxonomy.cdm.model.description.TextData;
40 import eu.etaxonomy.cdm.model.location.NamedArea;
41 import eu.etaxonomy.cdm.model.location.Point;
42 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
43 import eu.etaxonomy.cdm.model.media.Media;
44 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
45 import eu.etaxonomy.cdm.model.occurrence.Collection;
46 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
47 import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
48 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
49 import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
50 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
51 import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
52 import eu.etaxonomy.cdm.model.occurrence.Specimen;
53 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
54 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
55
56 /**
57 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
58 * a specimen based view. It does not support all functionality available in the
59 * occurrence package.<BR>
60 * The most significant restriction is that a specimen may derive only from
61 * one direct derivation event and there must be only one field observation (gathering event)
62 * it derives from.<BR>
63 *
64 * @author a.mueller
65 * @date 14.05.2010
66 */
67 public class DerivedUnitFacade {
68 private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);
69
70 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 ";
71
72 /**
73 * Enum that defines the class the "Specimen" belongs to.
74 * Some methods of the facade are not available for certain classes
75 * and will throw an Exception when invoking them.
76 */
77 public enum DerivedUnitType{
78 Specimen ("Specimen"),
79 Observation("Observation"),
80 LivingBeing("Living Being"),
81 Fossil("Fossil"),
82 DerivedUnit("Derived Unit");
83
84 String representation;
85 private DerivedUnitType(String representation){
86 this.representation = representation;
87 }
88
89 /**
90 * @return the representation
91 */
92 public String getRepresentation() {
93 return representation;
94 }
95
96 private DerivedUnitBase getNewDerivedUnitInstance(){
97 if (this == DerivedUnitType.Specimen){
98 return eu.etaxonomy.cdm.model.occurrence.Specimen.NewInstance();
99 }else if (this == DerivedUnitType.Observation){
100 return eu.etaxonomy.cdm.model.occurrence.Observation.NewInstance();
101 }else if (this == DerivedUnitType.LivingBeing){
102 return eu.etaxonomy.cdm.model.occurrence.LivingBeing.NewInstance();
103 }else if (this == DerivedUnitType.Fossil){
104 return eu.etaxonomy.cdm.model.occurrence.Fossil.NewInstance();
105 }else if (this == DerivedUnitType.DerivedUnit){
106 return eu.etaxonomy.cdm.model.occurrence.DerivedUnit.NewInstance();
107 }else{
108 throw new IllegalStateException("Unknown derived unit type " + this.getRepresentation());
109 }
110 }
111
112 }
113
114
115 private DerivedUnitFacadeConfigurator config;
116
117 //private GatheringEvent gatheringEvent;
118 private DerivedUnitType type; //needed?
119
120 private FieldObservation fieldObservation;
121
122 private DerivedUnitBase derivedUnit;
123
124 //media - the text data holding the media
125 private TextData derivedUnitMediaTextData;
126 private TextData fieldObjectMediaTextData;
127
128
129 private TextData ecology;
130 private TextData plantDescription;
131
132
133 /**
134 * Creates a derived unit facade for a new derived unit of type <code>type</code>.
135 * @param type
136 * @return
137 */
138 public static DerivedUnitFacade NewInstance(DerivedUnitType type){
139 return new DerivedUnitFacade(type);
140 }
141
142 /**
143 * Creates a derived unit facade for a given derived unit using the default configuration.
144 * @param derivedUnit
145 * @return
146 * @throws DerivedUnitFacadeNotSupportedException
147 */
148 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit) throws DerivedUnitFacadeNotSupportedException{
149 return new DerivedUnitFacade(derivedUnit, null);
150 }
151
152 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{
153 return new DerivedUnitFacade(derivedUnit, config);
154 }
155
156
157
158 // ****************** CONSTRUCTOR ****************************************************
159
160 private DerivedUnitFacade(DerivedUnitType type){
161 this.config = DerivedUnitFacadeConfigurator.NewInstance();
162
163 //derivedUnit
164 derivedUnit = type.getNewDerivedUnitInstance();
165 setCacheStrategy();
166 }
167
168 private DerivedUnitFacade(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{
169
170 if (config == null){
171 config = DerivedUnitFacadeConfigurator.NewInstance();
172 }
173 this.config = config;
174
175 //derived unit
176 this.derivedUnit = derivedUnit;
177 setCacheStrategy();
178
179 //derivation event
180 if (this.derivedUnit.getDerivedFrom() != null){
181 DerivationEvent derivationEvent = getDerivationEvent(true);
182 //fieldObservation
183 Set<FieldObservation> fieldOriginals = getFieldObservationsOriginals(derivationEvent, null);
184 if (fieldOriginals.size() > 1){
185 throw new DerivedUnitFacadeNotSupportedException("Specimen must not have more than 1 derivation event");
186 }else if (fieldOriginals.size() == 0){
187 //fieldObservation = FieldObservation.NewInstance();
188 }else if (fieldOriginals.size() == 1){
189 fieldObservation = fieldOriginals.iterator().next();
190 //###fieldObservation = getInitializedFieldObservation(fieldObservation);
191 fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());
192 }else{
193 throw new IllegalStateException("Illegal state");
194 }
195 }
196 // #### derivedUnit = getInitializedDerivedUnit(derivedUnit);
197
198 //test if unsupported
199
200 //media
201 //specimen
202 // String objectTypeExceptionText = "Specimen";
203 // SpecimenDescription imageGallery = getImageGalleryWithSupportTest(derivedUnit, objectTypeExceptionText, false);
204 // getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);
205 this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);
206
207 //field observation
208 // objectTypeExceptionText = "Field observation";
209 // imageGallery = getImageGalleryWithSupportTest(fieldObservation, objectTypeExceptionText, false);
210 // getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);
211 fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);
212
213 //handle derivedUnit.getMedia()
214 if (derivedUnit.getMedia().size() > 0){
215 //TODO better changed model here to allow only one place for images
216 if (this.config.isMoveDerivedUnitMediaToGallery()){
217 Set<Media> mediaSet = derivedUnit.getMedia();
218 for (Media media : mediaSet){
219 this.addDerivedUnitMedia(media);
220 }
221 mediaSet.removeAll(getDerivedUnitMedia());
222 }else{
223 throw new DerivedUnitFacadeNotSupportedException("Specimen may not have direct media. Only (one) image gallery is allowed");
224 }
225 }
226
227 //handle fieldObservation.getMedia()
228 if (fieldObservation != null && fieldObservation.getMedia() != null && fieldObservation.getMedia().size() > 0){
229 //TODO better changed model here to allow only one place for images
230 if (this.config.isMoveFieldObjectMediaToGallery()){
231 Set<Media> mediaSet = fieldObservation.getMedia();
232 for (Media media : mediaSet){
233 this.addFieldObjectMedia(media);
234 }
235 mediaSet.removeAll(getFieldObjectMedia());
236 }else{
237 throw new DerivedUnitFacadeNotSupportedException("Field object may not have direct media. Only (one) image gallery is allowed");
238 }
239 }
240
241 //test if descriptions are supported
242 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), false, false);
243 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), false, false);
244 }
245
246
247 private DerivedUnitBase getInitializedDerivedUnit(DerivedUnitBase derivedUnit) {
248 IOccurrenceService occurrenceService = this.config.getOccurrenceService();
249 if (occurrenceService == null){
250 return derivedUnit;
251 }
252 List<String> propertyPaths = this.config.getPropertyPaths();
253 if (propertyPaths == null){
254 return derivedUnit;
255 }
256 propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
257 DerivedUnitBase result = (DerivedUnitBase)occurrenceService.load(derivedUnit.getUuid(), propertyPaths);
258 return result;
259 }
260
261 /**
262 * Initializes the derived unit according to the configuartions property path.
263 * If the property path is <code>null</code> or no occurrence service is given the
264 * returned object is the same as the input parameter.
265 * @param fieldObservation2
266 * @return
267 */
268 private FieldObservation getInitializedFieldObservation(FieldObservation fieldObservation) {
269 IOccurrenceService occurrenceService = this.config.getOccurrenceService();
270 if (occurrenceService == null){
271 return fieldObservation;
272 }
273 List<String> propertyPaths = this.config.getPropertyPaths();
274 if (propertyPaths == null){
275 return fieldObservation;
276 }
277 propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
278 FieldObservation result = (FieldObservation)occurrenceService.load(fieldObservation.getUuid(), propertyPaths);
279 return result;
280 }
281
282 /**
283 * Transforms the property paths in a way that the facade is handled just like an
284 * ordinary CdmBase object.<BR>
285 * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas
286 * @param propertyPaths
287 * @return
288 */
289 private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
290 List<String> result = new ArrayList<String>();
291 for (String facadePath : propertyPaths){
292 // collecting areas (named area)
293 if (facadePath.startsWith("collectingAreas")){
294 facadePath = "gatheringEvent." + facadePath;
295 result.add(facadePath);
296 }
297 // collector (agentBase)
298 else if (facadePath.startsWith("collector")){
299 facadePath = facadePath.replace("collector", "gatheringEvent.actor");
300 result.add(facadePath);
301 }
302 // exactLocation (agentBase)
303 else if (facadePath.startsWith("exactLocation")){
304 facadePath = "gatheringEvent." + facadePath;
305 result.add(facadePath);
306 }
307 // gatheringPeriod (TimePeriod)
308 else if (facadePath.startsWith("gatheringPeriod")){
309 facadePath = facadePath.replace("gatheringPeriod", "gatheringEvent.timeperiod");
310 result.add(facadePath);
311 }
312 // (locality/ localityLanguage , LanguageString)
313 else if (facadePath.startsWith("locality")){
314 facadePath = "gatheringEvent." + facadePath;
315 result.add(facadePath);
316 }
317
318 //*********** FIELD OBJECT ************
319 // fieldObjectDefinitions (Map<language, languageString)
320 else if (facadePath.startsWith("fieldObjectDefinitions")){
321 // TODO or definition ???
322 facadePath = facadePath.replace("fieldObjectDefinitions", "description");
323 result.add(facadePath);
324 }
325 // fieldObjectMedia (Media)
326 else if (facadePath.startsWith("fieldObjectMedia")){
327 // TODO ???
328 facadePath = facadePath.replace("fieldObjectMedia", "descriptions.elements.media");
329 result.add(facadePath);
330 }
331
332 //Gathering Event will always be added
333 result.add("gatheringEvent");
334
335 }
336
337 /*
338 Gathering Event
339 ====================
340 - gatheringEvent (GatheringEvent)
341
342 Field Object
343 =================
344 - ecology/ ecologyAll (String) ???
345 - plant description (like ecology)
346
347 - fieldObjectImageGallery (SpecimenDescription) - is automatically initialized via fieldObjectMedia
348
349 */
350
351 return result;
352 }
353
354 /**
355 * Transforms the property paths in a way that the facade is handled just like an
356 * ordinary CdmBase object.<BR>
357 * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas
358 *
359 * Not needed (?) as the facade works with REST service property paths without using this method.
360 * @param propertyPaths
361 * @return
362 */
363 private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
364 List<String> result = new ArrayList<String>();
365 for (String facadePath : propertyPaths){
366 // determinations (DeterminationEvent)
367 if (facadePath.startsWith("determinations")){
368 facadePath = "" + facadePath; //no change
369 result.add(facadePath);
370 }
371 // storedUnder (TaxonNameBase)
372 else if (facadePath.startsWith("storedUnder")){
373 facadePath = "" + facadePath; //no change
374 result.add(facadePath);
375 }
376 // sources (IdentifiableSource)
377 else if (facadePath.startsWith("sources")){
378 facadePath = "" + facadePath; //no change
379 result.add(facadePath);
380 }
381 // collection (Collection)
382 else if (facadePath.startsWith("collection")){
383 facadePath = "" + facadePath; //no change
384 result.add(facadePath);
385 }
386 // (locality/ localityLanguage , LanguageString)
387 else if (facadePath.startsWith("locality")){
388 facadePath = "gatheringEvent." + facadePath;
389 result.add(facadePath);
390 }
391
392 //*********** FIELD OBJECT ************
393 // derivedUnitDefinitions (Map<language, languageString)
394 else if (facadePath.startsWith("derivedUnitDefinitions")){
395 // TODO or definition ???
396 facadePath = facadePath.replace("derivedUnitDefinitions", "description");
397 result.add(facadePath);
398 }
399
400 // derivedUnitMedia (Media)
401 else if (facadePath.startsWith("derivedUnitMedia")){
402 // TODO ???
403 facadePath = facadePath.replace("derivedUnitMedia", "descriptions.elements.media");
404 result.add(facadePath);
405 }
406
407 }
408
409 /*
410 //TODO
411 Derived Unit
412 =====================
413
414 - derivedUnitImageGallery (SpecimenDescription) - is automatically initialized via derivedUnitMedia
415
416 - derivationEvent (DerivationEvent) - will always be initialized
417 - duplicates (??? Specimen???) ???
418 */
419
420 return result;
421 }
422
423 /**
424 *
425 */
426 private void setCacheStrategy() {
427 derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
428 }
429
430
431 /**
432 * @param feature
433 * @param createIfNotExists
434 * @param isImageGallery
435 * @return
436 * @throws DerivedUnitFacadeNotSupportedException
437 */
438 private TextData initializeFieldObjectTextDataWithSupportTest(Feature feature, boolean createIfNotExists, boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {
439 //field object
440 FieldObservation fieldObject = getFieldObservation(createIfNotExists) ;
441 if (fieldObject == null){
442 return null;
443 }
444 return inititializeTextDataWithSupportTest(feature, fieldObject, createIfNotExists, isImageGallery);
445 }
446
447
448 /**
449 * @param feature
450 * @param specimen
451 * @param createIfNotExists
452 * @param isImageGallery
453 * @return
454 * @throws DerivedUnitFacadeNotSupportedException
455 */
456 private TextData inititializeTextDataWithSupportTest(Feature feature, SpecimenOrObservationBase specimen, boolean createIfNotExists,
457 boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {
458 if (feature == null ){
459 return null;
460 }
461 TextData textData = null;
462 if (createIfNotExists){
463 textData = TextData.NewInstance(feature);
464 }
465
466 Set<SpecimenDescription> descriptions;
467 if (isImageGallery){
468 descriptions = specimen.getSpecimenDescriptionImageGallery();
469 }else{
470 descriptions = specimen.getSpecimenDescriptions(false);
471 }
472 if (descriptions.size() == 0){
473 if (createIfNotExists){
474 SpecimenDescription newSpecimenDescription = SpecimenDescription.NewInstance(specimen);
475 newSpecimenDescription.addElement(textData);
476 return textData;
477 }else{
478 return null;
479 }
480 }
481 Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
482 for (SpecimenDescription description : descriptions){
483 for (DescriptionElementBase element: description.getElements()){
484 if (element.isInstanceOf(TextData.class) && ( feature.equals(element.getFeature() )|| isImageGallery ) ){
485 existingTextData.add(element);
486 }
487 }
488 }
489 if (existingTextData.size() > 1){
490 throw new DerivedUnitFacadeNotSupportedException("Specimen facade does not support more than one description text data of type " + feature.getLabel());
491
492 }else if (existingTextData.size() == 1){
493 return CdmBase.deproxy(existingTextData.iterator().next(), TextData.class);
494 }else{
495 SpecimenDescription description = descriptions.iterator().next();
496 description.addElement(textData);
497 return textData;
498 }
499 }
500
501
502 /**
503 * Tests if a given image gallery is supported by the derived unit facade.
504 * It returns the only text data attached to the given image gallery.
505 * It the given image gallery does not have text data attached, it is created and attached.
506 * @param imageGallery
507 * @return
508 * @throws DerivedUnitFacadeNotSupportedException
509 */
510 private TextData testImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException {
511 if (imageGallery.isImageGallery() == false){
512 throw new DerivedUnitFacadeNotSupportedException("Image gallery needs to have image gallery flag set");
513 }
514 if (imageGallery.getElements().size() > 1){
515 throw new DerivedUnitFacadeNotSupportedException("Image gallery must not have more then one description element");
516 }
517 TextData textData;
518 if (imageGallery.getElements().size() == 0){
519 textData = TextData.NewInstance(Feature.IMAGE());
520 imageGallery.addElement(textData);
521 }else{
522 if (! imageGallery.getElements().iterator().next().isInstanceOf(TextData.class)){
523 throw new DerivedUnitFacadeNotSupportedException("Image gallery must only have TextData as element");
524 }else{
525 textData = CdmBase.deproxy(imageGallery.getElements().iterator().next(), TextData.class);
526 }
527 }
528 return textData;
529 }
530
531 //************************** METHODS *****************************************
532
533 private TextData getDerivedUnitImageGalleryTextData(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{
534 if (this.derivedUnitMediaTextData == null && createIfNotExists){
535 this.derivedUnitMediaTextData = getImageGalleryTextData(derivedUnit, "Specimen");
536 }
537 return this.derivedUnitMediaTextData;
538 }
539
540 private TextData getObservationImageGalleryTextData(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{
541 if (this.fieldObjectMediaTextData == null && createIfNotExists){
542 this.fieldObjectMediaTextData = getImageGalleryTextData(fieldObservation, "Field observation");
543 }
544 return this.fieldObjectMediaTextData;
545 }
546
547
548
549 /**
550 * @param derivationEvent2
551 * @return
552 * @throws DerivedUnitFacadeNotSupportedException
553 */
554 private Set<FieldObservation> getFieldObservationsOriginals(DerivationEvent derivationEvent, Set<SpecimenOrObservationBase> recursionAvoidSet) throws DerivedUnitFacadeNotSupportedException {
555 if (recursionAvoidSet == null){
556 recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
557 }
558 Set<FieldObservation> result = new HashSet<FieldObservation>();
559 Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
560 for (SpecimenOrObservationBase original : originals){
561 if (original.isInstanceOf(FieldObservation.class)){
562 result.add(CdmBase.deproxy(original, FieldObservation.class));
563 }else if (original.isInstanceOf(DerivedUnitBase.class)){
564 //if specimen has already been tested exclude it from further recursion
565 if (recursionAvoidSet.contains(original)){
566 continue;
567 }
568 DerivedUnitBase derivedUnit = CdmBase.deproxy(original, DerivedUnitBase.class);
569 DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();
570 // Set<DerivationEvent> derivationEvents = original.getDerivationEvents();
571 // for (DerivationEvent originalDerivation : derivationEvents){
572 Set<FieldObservation> fieldObservations = getFieldObservationsOriginals(originalDerivation, recursionAvoidSet);
573 result.addAll(fieldObservations);
574 // }
575 }else{
576 throw new DerivedUnitFacadeNotSupportedException("Unhandled specimen or observation base type: " + original.getClass().getName() );
577 }
578
579 }
580 return result;
581 }
582
583 //*********** MEDIA METHODS ******************************
584
585 // /**
586 // * Returns the media list for a specimen. Throws an exception if the existing specimen descriptions
587 // * are not supported by this facade.
588 // * @param specimen the specimen the media belongs to
589 // * @param specimenExceptionText text describing the specimen for exception messages
590 // * @return
591 // * @throws DerivedUnitFacadeNotSupportedException
592 // */
593 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase specimen, String specimenExceptionText) throws DerivedUnitFacadeNotSupportedException{
594 // List<Media> result;
595 // SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
596 // TextData textData = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);
597 // result = textData.getMedia();
598 // return result;
599 // }
600
601 /**
602 * Returns the media list for a specimen. Throws an exception if the existing specimen descriptions
603 * are not supported by this facade.
604 * @param specimen the specimen the media belongs to
605 * @param specimenExceptionText text describing the specimen for exception messages
606 * @return
607 * @throws DerivedUnitFacadeNotSupportedException
608 */
609 private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText) throws DerivedUnitFacadeNotSupportedException{
610 TextData result;
611 SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
612 result = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);
613 return result;
614 }
615
616
617 /**
618 * Returns the image gallery of the according specimen. Throws an exception if the attached
619 * image gallerie(s) are not supported by this facade.
620 * If no image gallery exists a new one is created if <code>createNewIfNotExists</code> is true and
621 * if specimen is not <code>null</code>.
622 * @param specimen
623 * @param specimenText
624 * @param createNewIfNotExists
625 * @return
626 * @throws DerivedUnitFacadeNotSupportedException
627 */
628 private SpecimenDescription getImageGalleryWithSupportTest(SpecimenOrObservationBase<?> specimen, String specimenText, boolean createNewIfNotExists) throws DerivedUnitFacadeNotSupportedException{
629 if (specimen == null){
630 return null;
631 }
632 SpecimenDescription imageGallery;
633 if (hasMultipleImageGalleries(specimen)){
634 throw new DerivedUnitFacadeNotSupportedException( specimenText + " must not have more than 1 image gallery");
635 }else{
636 imageGallery = getImageGallery(specimen, createNewIfNotExists);
637 getImageTextDataWithSupportTest(imageGallery, specimenText);
638 }
639 return imageGallery;
640 }
641
642 /**
643 * Returns the media holding text data element of the image gallery. Throws an exception if multiple
644 * such text data already exist.
645 * Creates a new text data if none exists and adds it to the image gallery.
646 * If image gallery is <code>null</code> nothing happens.
647 * @param imageGallery
648 * @param textData
649 * @return
650 * @throws DerivedUnitFacadeNotSupportedException
651 */
652 private TextData getImageTextDataWithSupportTest(SpecimenDescription imageGallery, String specimenText) throws DerivedUnitFacadeNotSupportedException {
653 if (imageGallery == null){
654 return null;
655 }
656 TextData textData = null;
657 for (DescriptionElementBase element: imageGallery.getElements()){
658 if (element.isInstanceOf(TextData.class) && element.getFeature().equals(Feature.IMAGE())){
659 if (textData != null){
660 throw new DerivedUnitFacadeNotSupportedException( specimenText + " must not have more than 1 image text data element in image gallery");
661 }
662 textData = CdmBase.deproxy(element, TextData.class);
663 }
664 }
665 if (textData == null){
666 textData = TextData.NewInstance(Feature.IMAGE());
667 imageGallery.addElement(textData);
668 }
669 return textData;
670 }
671
672 /**
673 * Checks, if a specimen belongs to more than one description that is an image gallery
674 * @param derivedUnit
675 * @return
676 */
677 private boolean hasMultipleImageGalleries(SpecimenOrObservationBase<?> derivedUnit){
678 int count = 0;
679 Set<SpecimenDescription> descriptions= derivedUnit.getSpecimenDescriptions();
680 for (SpecimenDescription description : descriptions){
681 if (description.isImageGallery()){
682 count++;
683 }
684 }
685 return (count > 1);
686 }
687
688
689 /**
690 * Returns the image gallery for a specimen. If there are multiple specimen descriptions
691 * marked as image galleries an arbitrary one is chosen.
692 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
693 * is <code>true</code>.<Br>
694 * If specimen is <code>null</code> a null pointer exception is thrown.
695 * @param createNewIfNotExists
696 * @return
697 */
698 private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
699 SpecimenDescription result = null;
700 Set<SpecimenDescription> descriptions= specimen.getSpecimenDescriptions();
701 for (SpecimenDescription description : descriptions){
702 if (description.isImageGallery()){
703 result = description;
704 break;
705 }
706 }
707 if (result == null && createIfNotExists){
708 result = SpecimenDescription.NewInstance(specimen);
709 result.setImageGallery(true);
710 }
711 return result;
712 }
713
714 /**
715 * Adds a media to the specimens image gallery. If media is <code>null</code> nothing happens.
716 * @param media
717 * @param specimen
718 * @return true if media is not null (as specified by {@link java.util.Collection#add(Object) Collection.add(E e)}
719 * @throws DerivedUnitFacadeNotSupportedException
720 */
721 private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
722 if (media != null){
723 List<Media> mediaList = getMedia(specimen, true);
724 return mediaList.add(media);
725 }else{
726 return false;
727 }
728 }
729
730 /**
731 * Removes a media from the specimens image gallery.
732 * @param media
733 * @param specimen
734 * @return true if an element was removed as a result of this call (as specified by {@link java.util.Collection#remove(Object) Collection.remove(E e)}
735 * @throws DerivedUnitFacadeNotSupportedException
736 */
737 private boolean removeMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
738 List<Media> mediaList = getMedia(specimen, true);
739 return mediaList == null ? null : mediaList.remove(media);
740 }
741
742 private List<Media> getMedia(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {
743 TextData textData = getMediaTextData(specimen, createIfNotExists);
744 return textData == null ? null : textData.getMedia();
745 }
746
747 /**
748 * Returns the one media list of a specimen which is part of the only image gallery that
749 * this specimen is part of.<BR>
750 * If these conditions are not hold an exception is thrwon.
751 * @param specimen
752 * @return
753 * @throws DerivedUnitFacadeNotSupportedException
754 */
755 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
756 // if (specimen == null){
757 // return null;
758 // }
759 // if (specimen == this.derivedUnit){
760 // return getDerivedUnitImageGalleryMedia();
761 // }else if (specimen == this.fieldObservation){
762 // return getObservationImageGalleryTextData();
763 // }else{
764 // return getImageGalleryMedia(specimen, "Undefined specimen ");
765 // }
766 // }
767
768 /**
769 * Returns the one media list of a specimen which is part of the only image gallery that
770 * this specimen is part of.<BR>
771 * If these conditions are not hold an exception is thrwon.
772 * @param specimen
773 * @return
774 * @throws DerivedUnitFacadeNotSupportedException
775 */
776 private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {
777 if (specimen == null){
778 return null;
779 }
780 if (specimen == this.derivedUnit){
781 return getDerivedUnitImageGalleryTextData(createIfNotExists);
782 }else if (specimen == this.fieldObservation){
783 return getObservationImageGalleryTextData(createIfNotExists);
784 }else{
785 return getImageGalleryTextData(specimen, "Undefined specimen ");
786 }
787 }
788
789
790 //****************** GETTER / SETTER / ADDER / REMOVER ***********************/
791
792 // ****************** Gathering Event *********************************/
793
794 //country
795 @Transient
796 public NamedArea getCountry(){
797 return (hasGatheringEvent() ? getGatheringEvent(true).getCountry() : null);
798 }
799
800 public void setCountry(NamedArea country){
801 getGatheringEvent(true).setCountry(country);
802 }
803
804
805 //Collecting area
806 public void addCollectingArea(NamedArea area) {
807 getGatheringEvent(true).addCollectingArea(area);
808 }
809 public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
810 for (NamedArea area : areas){
811 getGatheringEvent(true).addCollectingArea(area);
812 }
813 }
814 @Transient
815 public Set<NamedArea> getCollectingAreas() {
816 return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingAreas() : null);
817 }
818 public void removeCollectingArea(NamedArea area) {
819 if (hasGatheringEvent()){
820 getGatheringEvent(true).removeCollectingArea(area);
821 }
822 }
823
824 //absolute elevation
825 /** meter above/below sea level of the surface
826 * @see #getAbsoluteElevationError()
827 * @see #getAbsoluteElevationRange()
828 **/
829 @Transient
830 public Integer getAbsoluteElevation() {
831 return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);
832 }
833 public void setAbsoluteElevation(Integer absoluteElevation) {
834 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
835 }
836
837 //absolute elevation error
838 @Transient
839 public Integer getAbsoluteElevationError() {
840 return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevationError() : null);
841 }
842 public void setAbsoluteElevationError(Integer absoluteElevationError) {
843 getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);
844 }
845
846 /**
847 * @see #getAbsoluteElevation()
848 * @see #getAbsoluteElevationError()
849 * @see #setAbsoluteElevationRange(Integer, Integer)
850 * @see #getAbsoluteElevationMaximum()
851 */
852 @Transient
853 public Integer getAbsoluteElevationMinimum(){
854 if ( ! hasGatheringEvent() ){
855 return null;
856 }
857 Integer minimum = getGatheringEvent(true).getAbsoluteElevation();
858 if (getGatheringEvent(true).getAbsoluteElevationError() != null){
859 minimum = minimum - getGatheringEvent(true).getAbsoluteElevationError();
860 }
861 return minimum;
862 }
863 /**
864 * @see #getAbsoluteElevation()
865 * @see #getAbsoluteElevationError()
866 * @see #setAbsoluteElevationRange(Integer, Integer)
867 * @see #getAbsoluteElevationMinimum()
868 */
869 @Transient
870 public Integer getAbsoluteElevationMaximum(){
871 if ( ! hasGatheringEvent() ){
872 return null;
873 }
874 Integer maximum = getGatheringEvent(true).getAbsoluteElevation();
875 if (getGatheringEvent(true).getAbsoluteElevationError() != null){
876 maximum = maximum + getGatheringEvent(true).getAbsoluteElevationError();
877 }
878 return maximum;
879 }
880
881
882 /**
883 * This method replaces absoluteElevation and absoulteElevationError by
884 * internally translating minimum and maximum values into
885 * average and error values. As all these values are integer based
886 * it is necessary that the distance is between minimum and maximum is <b>even</b>,
887 * otherwise we will get a rounding error resulting in a maximum that is increased
888 * by 1.
889 * @see #setAbsoluteElevation(Integer)
890 * @see #setAbsoluteElevationError(Integer)
891 * @param minimumElevation minimum of the range
892 * @param maximumElevation maximum of the range
893 */
894 public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation){
895 if (minimumElevation == null || maximumElevation == null){
896 Integer elevation = minimumElevation;
897 Integer error = 0;
898 if (minimumElevation == null){
899 elevation = maximumElevation;
900 if (elevation == null){
901 error = null;
902 }
903 }
904 getGatheringEvent(true).setAbsoluteElevation(elevation);
905 getGatheringEvent(true).setAbsoluteElevationError(error);
906 }else{
907 if (! isEvenDistance(minimumElevation, maximumElevation) ){
908 throw new IllegalArgumentException("Distance between minimum and maximum elevation must be even but was " + Math.abs(minimumElevation - maximumElevation));
909 }
910 Integer absoluteElevationError = Math.abs(maximumElevation - minimumElevation);
911 absoluteElevationError = absoluteElevationError / 2;
912 Integer absoluteElevation = minimumElevation + absoluteElevationError;
913 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
914 getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);
915 }
916 }
917
918 /**
919 * @param minimumElevation
920 * @param maximumElevation
921 * @return
922 */
923 private boolean isEvenDistance(Integer minimumElevation, Integer maximumElevation) {
924 Integer diff = ( maximumElevation - minimumElevation);
925 Integer testDiff = (diff /2) *2 ;
926 return (testDiff == diff);
927 }
928
929 //collector
930 @Transient
931 public AgentBase getCollector() {
932 return (hasGatheringEvent() ? getGatheringEvent(true).getCollector() : null);
933 }
934 public void setCollector(AgentBase collector){
935 getGatheringEvent(true).setCollector(collector);
936 }
937
938 //collecting method
939 @Transient
940 public String getCollectingMethod() {
941 return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);
942 }
943 public void setCollectingMethod(String collectingMethod) {
944 getGatheringEvent(true).setCollectingMethod(collectingMethod);
945 }
946
947 //distance to ground
948 @Transient
949 public Integer getDistanceToGround() {
950 return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);
951 }
952 public void setDistanceToGround(Integer distanceToGround) {
953 getGatheringEvent(true).setDistanceToGround(distanceToGround);
954 }
955
956 //distance to water surface
957 @Transient
958 public Integer getDistanceToWaterSurface() {
959 return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);
960 }
961 public void setDistanceToWaterSurface(Integer distanceToWaterSurface) {
962 getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);
963 }
964
965 //exact location
966 @Transient
967 public Point getExactLocation() {
968 return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null );
969 }
970
971 /**
972 * Returns a sexagesimal representation of the exact location (e.g. 12°59'N, 35°23E).
973 * If the exact location is <code>null</code> the empty string is returned.
974 * @param includeEmptySeconds
975 * @param includeReferenceSystem
976 * @return
977 */
978 public String getExactLocationText(boolean includeEmptySeconds, boolean includeReferenceSystem){
979 return (this.getExactLocation() == null ? "" : this.getExactLocation().toSexagesimalString(includeEmptySeconds, includeReferenceSystem));
980 }
981 public void setExactLocation(Point exactLocation) {
982 getGatheringEvent(true).setExactLocation(exactLocation);
983 }
984 public void setExactLocationByParsing(String longitudeToParse, String latitudeToParse, ReferenceSystem referenceSystem, Integer errorRadius) throws ParseException{
985 Point point = Point.NewInstance(null, null, referenceSystem, errorRadius);
986 point.setLongitudeByParsing(longitudeToParse);
987 point.setLatitudeByParsing(latitudeToParse);
988 setExactLocation(point);
989 }
990
991 //gathering event description
992 @Transient
993 public String getGatheringEventDescription() {
994 return (hasGatheringEvent() ? getGatheringEvent(true).getDescription() : null);
995 }
996 public void setGatheringEventDescription(String description) {
997 getGatheringEvent(true).setDescription(description);
998 }
999
1000 //gatering period
1001 @Transient
1002 public TimePeriod getGatheringPeriod() {
1003 return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod() : null);
1004 }
1005 public void setGatheringPeriod(TimePeriod timeperiod) {
1006 getGatheringEvent(true).setTimeperiod(timeperiod);
1007 }
1008
1009 //locality
1010 @Transient
1011 public LanguageString getLocality(){
1012 return (hasGatheringEvent() ? getGatheringEvent(true).getLocality() : null);
1013 }
1014 /**
1015 * convienience method for {@link #getLocality()}.{@link LanguageString#getText() getText()}
1016 * @return
1017 */
1018 @Transient
1019 public String getLocalityText(){
1020 LanguageString locality = getLocality();
1021 if(locality != null){
1022 return locality.getText();
1023 }
1024 return null;
1025 }
1026 /**
1027 * convienience method for {@link #getLocality()}.{@link LanguageString#getLanguage() getLanguage()}
1028 * @return
1029 */
1030 @Transient
1031 public Language getLocalityLanguage(){
1032 LanguageString locality = getLocality();
1033 if(locality != null){
1034 return locality.getLanguage();
1035 }
1036 return null;
1037 }
1038
1039 /**
1040 * Sets the locality string in the default language
1041 * @param locality
1042 */
1043 public void setLocality(String locality){
1044 Language language = Language.DEFAULT();
1045 setLocality(locality, language);
1046 }
1047 public void setLocality(String locality, Language language){
1048 LanguageString langString = LanguageString.NewInstance(locality, language);
1049 setLocality(langString);
1050 }
1051 public void setLocality(LanguageString locality){
1052 getGatheringEvent(true).setLocality(locality);
1053 }
1054
1055 /**
1056 * The gathering event will be used for the field object instead of the old gathering event.<BR>
1057 * <B>This method will override all gathering values (see below).</B>
1058 * @see #getAbsoluteElevation()
1059 * @see #getAbsoluteElevationError()
1060 * @see #getDistanceToGround()
1061 * @see #getDistanceToWaterSurface()
1062 * @see #getExactLocation()
1063 * @see #getGatheringEventDescription()
1064 * @see #getGatheringPeriod()
1065 * @see #getCollectingAreas()
1066 * @see #getCollectingMethod()
1067 * @see #getLocality()
1068 * @see #getCollector()
1069 * @param gatheringEvent
1070 */
1071 public void setGatheringEvent(GatheringEvent gatheringEvent) {
1072 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1073 }
1074 public boolean hasGatheringEvent(){
1075 return (getGatheringEvent(false) != null);
1076 }
1077
1078 public GatheringEvent innerGatheringEvent() {
1079 return getGatheringEvent(false);
1080 }
1081
1082 public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
1083 if (! hasFieldObservation() && ! createIfNotExists){
1084 return null;
1085 }
1086 if (createIfNotExists && getFieldObservation(true).getGatheringEvent() == null ){
1087 GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
1088 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1089 }
1090 return getFieldObservation(true).getGatheringEvent();
1091 }
1092
1093 // ****************** Field Object ************************************/
1094
1095 /**
1096 * Returns true if a field observation exists (even if all attributes are empty or <code>null<code>.
1097 * @return
1098 */
1099 public boolean hasFieldObject(){
1100 return this.fieldObservation != null;
1101 }
1102
1103 //ecology
1104 @Transient
1105 public String getEcology(){
1106 return getEcology(Language.DEFAULT());
1107 }
1108 public String getEcology(Language language){
1109 LanguageString languageString = getEcologyAll().get(language);
1110 return (languageString == null ? null : languageString.getText());
1111 }
1112 // public String getEcologyPreferred(List<Language> languages){
1113 // LanguageString languageString = getEcologyAll().getPreferredLanguageString(languages);
1114 // return languageString.getText();
1115 // }
1116 @Transient
1117 public Map<Language, LanguageString> getEcologyAll(){
1118 if (ecology == null){
1119 try {
1120 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), false, false);
1121 } catch (DerivedUnitFacadeNotSupportedException e) {
1122 throw new IllegalStateException(notSupportMessage, e);
1123 }
1124 if (ecology == null){
1125 return new HashMap<Language, LanguageString>();
1126 }
1127 }
1128 return ecology.getMultilanguageText();
1129 }
1130
1131 public void setEcology(String ecology){
1132 setEcology(ecology, null);
1133 }
1134 public void setEcology(String ecologyText, Language language){
1135 if (language == null){
1136 language = Language.DEFAULT();
1137 }
1138 if (ecology == null){
1139 try {
1140 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), true, false);
1141 } catch (DerivedUnitFacadeNotSupportedException e) {
1142 throw new IllegalStateException(notSupportMessage, e);
1143 }
1144 }
1145 if (ecologyText == null){
1146 ecology.removeText(language);
1147 }else{
1148 ecology.putText(ecologyText, language);
1149 }
1150 }
1151 public void removeEcology(Language language){
1152 setEcology(null, language);
1153 }
1154 /**
1155 * Removes ecology for the default language
1156 */
1157 public void removeEcology(){
1158 setEcology(null, null);
1159 }
1160 public void removeEcologyAll(){
1161
1162 }
1163
1164
1165 //plant description
1166 @Transient
1167 public String getPlantDescription(){
1168 return getPlantDescription(null);
1169 }
1170 public String getPlantDescription(Language language){
1171 if (language == null){
1172 language = Language.DEFAULT();
1173 }
1174 LanguageString languageString = getPlantDescriptionAll().get(language);
1175 return (languageString == null ? null : languageString.getText());
1176 }
1177 // public String getPlantDescriptionPreferred(List<Language> languages){
1178 // LanguageString languageString = getPlantDescriptionAll().getPreferredLanguageString(languages);
1179 // return languageString.getText();
1180 // }
1181 @Transient
1182 public Map<Language, LanguageString> getPlantDescriptionAll(){
1183 if (plantDescription == null){
1184 try {
1185 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), false, false);
1186 } catch (DerivedUnitFacadeNotSupportedException e) {
1187 throw new IllegalStateException(notSupportMessage, e);
1188 }
1189 if (plantDescription == null){
1190 return new HashMap<Language, LanguageString>();
1191 }
1192 }
1193 return plantDescription.getMultilanguageText();
1194 }
1195 public void setPlantDescription(String plantDescription){
1196 setPlantDescription(plantDescription, null);
1197 }
1198 public void setPlantDescription(String plantDescriptionText, Language language){
1199 if (language == null){
1200 language = Language.DEFAULT();
1201 }
1202 if (plantDescription == null){
1203 try {
1204 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), true, false);
1205 } catch (DerivedUnitFacadeNotSupportedException e) {
1206 throw new IllegalStateException(notSupportMessage, e);
1207 }
1208 }
1209 if (plantDescriptionText == null){
1210 plantDescription.removeText(language);
1211 }else{
1212 plantDescription.putText(plantDescriptionText, language);
1213 }
1214 }
1215 public void removePlantDescription(Language language){
1216 setPlantDescription(null, language);
1217 }
1218
1219 //field object definition
1220 public void addFieldObjectDefinition(String text, Language language) {
1221 getFieldObservation(true).addDefinition(text, language);
1222 }
1223 @Transient
1224 public Map<Language, LanguageString> getFieldObjectDefinition() {
1225 if (! hasFieldObservation()){
1226 return new HashMap<Language, LanguageString>();
1227 }else{
1228 return getFieldObservation(true).getDefinition();
1229 }
1230 }
1231 public String getFieldObjectDefinition(Language language) {
1232 Map<Language, LanguageString> map = getFieldObjectDefinition();
1233 LanguageString languageString = (map == null? null : map.get(language));
1234 if (languageString != null){
1235 return languageString.getText();
1236 }else {
1237 return null;
1238 }
1239 }
1240 public void removeFieldObjectDefinition(Language lang) {
1241 if (hasFieldObservation()){
1242 getFieldObservation(true).removeDefinition(lang);
1243 }
1244 }
1245
1246
1247 //media
1248 public boolean addFieldObjectMedia(Media media) {
1249 try {
1250 return addMedia(media, getFieldObservation(true));
1251 } catch (DerivedUnitFacadeNotSupportedException e) {
1252 throw new IllegalStateException(notSupportMessage, e);
1253 }
1254 }
1255 /**
1256 * Returns true, if an image gallery for the field object exists.<BR>
1257 * Returns also <code>true</code> if the image gallery is empty.
1258 * @return
1259 */
1260 public boolean hasFieldObjectImageGallery(){
1261 if (! hasFieldObject()){
1262 return false;
1263 }else{
1264 return (getImageGallery(fieldObservation, false) != null);
1265 }
1266 }
1267
1268 public void setFieldObjectImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException{
1269 SpecimenDescription existingGallery = getFieldObjectImageGallery(false);
1270 //TODO test attached fieldObject is the same as this.getFieldObject
1271
1272 if (existingGallery != null){
1273 if (existingGallery != imageGallery){
1274 throw new DerivedUnitFacadeNotSupportedException("DerivedUnitFacade does not allow more than one image gallery");
1275 }else{
1276 //do nothing
1277 }
1278 }else {
1279 TextData textData = testImageGallery(imageGallery);
1280 this.fieldObjectMediaTextData = textData;
1281 }
1282 }
1283
1284
1285 /**
1286 * Returns the field object image gallery. If no such image gallery exists and
1287 * createIfNotExists is true an new one is created. Otherwise null is returned.
1288 * @param createIfNotExists
1289 * @return
1290 */
1291 public SpecimenDescription getFieldObjectImageGallery(boolean createIfNotExists){
1292 TextData textData;
1293 try {
1294 textData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), createIfNotExists, true);
1295 } catch (DerivedUnitFacadeNotSupportedException e) {
1296 throw new IllegalStateException(notSupportMessage, e);
1297 }
1298 if (textData != null){
1299 return CdmBase.deproxy(textData.getInDescription(), SpecimenDescription.class);
1300 }else{
1301 return null;
1302 }
1303 }
1304 /**
1305 * Returns the media for the field object.<BR>
1306 * @return
1307 */
1308 @Transient
1309 public List<Media> getFieldObjectMedia() {
1310 try {
1311 List<Media> result = getMedia(getFieldObservation(false), false);
1312 return result == null ? new ArrayList<Media>() : result;
1313 } catch (DerivedUnitFacadeNotSupportedException e) {
1314 throw new IllegalStateException(notSupportMessage, e);
1315 }
1316 }
1317 public boolean removeFieldObjectMedia(Media media) {
1318 try {
1319 return removeMedia(media, getFieldObservation(false));
1320 } catch (DerivedUnitFacadeNotSupportedException e) {
1321 throw new IllegalStateException(notSupportMessage, e);
1322 }
1323 }
1324
1325 //field number
1326 @Transient
1327 public String getFieldNumber() {
1328 if (! hasFieldObservation()){
1329 return null;
1330 }else{
1331 return getFieldObservation(true).getFieldNumber();
1332 }
1333 }
1334 public void setFieldNumber(String fieldNumber) {
1335 getFieldObservation(true).setFieldNumber(fieldNumber);
1336 }
1337
1338
1339 //field notes
1340 @Transient
1341 public String getFieldNotes() {
1342 if (! hasFieldObservation()){
1343 return null;
1344 }else{
1345 return getFieldObservation(true).getFieldNotes();
1346 }
1347 }
1348 public void setFieldNotes(String fieldNotes) {
1349 getFieldObservation(true).setFieldNotes(fieldNotes);
1350 }
1351
1352
1353 //individual counts
1354 @Transient
1355 public Integer getIndividualCount() {
1356 return (hasFieldObservation()? getFieldObservation(true).getIndividualCount() : null );
1357 }
1358 public void setIndividualCount(Integer individualCount) {
1359 getFieldObservation(true).setIndividualCount(individualCount);
1360 }
1361
1362 //life stage
1363 @Transient
1364 public Stage getLifeStage() {
1365 return (hasFieldObservation()? getFieldObservation(true).getLifeStage() : null );
1366 }
1367 public void setLifeStage(Stage lifeStage) {
1368 getFieldObservation(true).setLifeStage(lifeStage);
1369 }
1370
1371 //sex
1372 @Transient
1373 public Sex getSex() {
1374 return (hasFieldObservation()? getFieldObservation(true).getSex() : null );
1375 }
1376 public void setSex(Sex sex) {
1377 getFieldObservation(true).setSex(sex);
1378 }
1379
1380
1381 //field observation
1382 public boolean hasFieldObservation(){
1383 return (getFieldObservation(false) != null);
1384 }
1385
1386 /**
1387 * Returns the field observation as an object.
1388 * @return
1389 */
1390 public FieldObservation innerFieldObservation(){
1391 return getFieldObservation(false);
1392 }
1393
1394 /**
1395 * Returns the field observation as an object.
1396 * @return
1397 */
1398 public FieldObservation getFieldObservation(boolean createIfNotExists){
1399 if (fieldObservation == null && createIfNotExists){
1400 fieldObservation = FieldObservation.NewInstance();
1401 fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());
1402 DerivationEvent derivationEvent = getDerivationEvent(true);
1403 derivationEvent.addOriginal(fieldObservation);
1404 }
1405 return this.fieldObservation;
1406 }
1407
1408
1409
1410
1411
1412 //****************** Specimen **************************************************
1413
1414 //Definition
1415 public void addDerivedUnitDefinition(String text, Language language) {
1416 derivedUnit.addDefinition(text, language);
1417 }
1418 @Transient
1419 public Map<Language, LanguageString> getDerivedUnitDefinitions(){
1420 return this.derivedUnit.getDefinition();
1421 }
1422 public String getDerivedUnitDefinition(Language language) {
1423 Map<Language,LanguageString> languageMap = derivedUnit.getDefinition();
1424 LanguageString languageString = languageMap.get(language);
1425 if (languageString != null){
1426 return languageString.getText();
1427 }else {
1428 return null;
1429 }
1430 }
1431 public void removeDerivedUnitDefinition(Language lang) {
1432 derivedUnit.removeDefinition(lang);
1433 }
1434
1435 //Determination
1436 public void addDetermination(DeterminationEvent determination) {
1437 derivedUnit.addDetermination(determination);
1438 }
1439 @Transient
1440 public Set<DeterminationEvent> getDeterminations() {
1441 return derivedUnit.getDeterminations();
1442 }
1443 public void removeDetermination(DeterminationEvent determination) {
1444 derivedUnit.removeDetermination(determination);
1445 }
1446
1447 //Media
1448 public boolean addDerivedUnitMedia(Media media) {
1449 try {
1450 return addMedia(media, derivedUnit);
1451 } catch (DerivedUnitFacadeNotSupportedException e) {
1452 throw new IllegalStateException(notSupportMessage, e);
1453 }
1454 }
1455 /**
1456 * Returns true, if an image gallery exists for the specimen.<BR>
1457 * Returns also <code>true</code> if the image gallery is empty.
1458 */
1459 public boolean hasDerivedUnitImageGallery(){
1460 return (getImageGallery(derivedUnit, false) != null);
1461 }
1462
1463 public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists){
1464 TextData textData;
1465 try {
1466 textData = inititializeTextDataWithSupportTest(Feature.IMAGE(), derivedUnit, createIfNotExists, true);
1467 } catch (DerivedUnitFacadeNotSupportedException e) {
1468 throw new IllegalStateException(notSupportMessage, e);
1469 }
1470 if (textData != null){
1471 return CdmBase.deproxy(textData.getInDescription(), SpecimenDescription.class);
1472 }else{
1473 return null;
1474 }
1475 }
1476 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException{
1477 SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);
1478 //TODO test attached fieldObject is the same as this.getFieldObject
1479
1480 if (existingGallery != null){
1481 if (existingGallery != imageGallery){
1482 throw new DerivedUnitFacadeNotSupportedException("DerivedUnitFacade does not allow more than one image gallery");
1483 }else{
1484 //do nothing
1485 }
1486 }else {
1487 TextData textData = testImageGallery(imageGallery);
1488 this.derivedUnitMediaTextData = textData;
1489 }
1490 }
1491
1492 /**
1493 * Returns the media for the specimen.<BR>
1494 * @return
1495 */
1496 @Transient
1497 public List<Media> getDerivedUnitMedia() {
1498 try {
1499 List<Media> result = getMedia(derivedUnit, false);
1500 return result == null ? new ArrayList<Media>() : result;
1501 } catch (DerivedUnitFacadeNotSupportedException e) {
1502 throw new IllegalStateException(notSupportMessage, e);
1503 }
1504 }
1505 public boolean removeDerivedUnitMedia(Media media) {
1506 try {
1507 return removeMedia(media, derivedUnit);
1508 } catch (DerivedUnitFacadeNotSupportedException e) {
1509 throw new IllegalStateException(notSupportMessage, e);
1510 }
1511 }
1512
1513
1514 //Accession Number
1515 @Transient
1516 public String getAccessionNumber() {
1517 return derivedUnit.getAccessionNumber();
1518 }
1519 public void setAccessionNumber(String accessionNumber) {
1520 derivedUnit.setAccessionNumber(accessionNumber);
1521 }
1522
1523 @Transient
1524 public String getCatalogNumber() {
1525 return derivedUnit.getCatalogNumber();
1526 }
1527 public void setCatalogNumber(String catalogNumber) {
1528 derivedUnit.setCatalogNumber(catalogNumber);
1529 }
1530
1531 @Transient
1532 public String getBarcode() {
1533 return derivedUnit.getBarcode();
1534 }
1535 public void setBarcode(String barcode) {
1536 derivedUnit.setCatalogNumber(barcode);
1537 }
1538
1539
1540 //Preservation Method
1541
1542 /**
1543 * Only supported by specimen and fossils
1544 * @see #DerivedUnitType
1545 * @return
1546 */
1547 @Transient
1548 public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {
1549 if (derivedUnit.isInstanceOf(Specimen.class)){
1550 return CdmBase.deproxy(derivedUnit, Specimen.class).getPreservation();
1551 }else{
1552 if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){
1553 throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
1554 }else{
1555 return null;
1556 }
1557 }
1558 }
1559 /**
1560 * Only supported by specimen and fossils
1561 * @see #DerivedUnitType
1562 * @return
1563 */
1564 public void setPreservationMethod(PreservationMethod preservation)throws MethodNotSupportedByDerivedUnitTypeException {
1565 if (derivedUnit.isInstanceOf(Specimen.class)){
1566 CdmBase.deproxy(derivedUnit, Specimen.class).setPreservation(preservation);
1567 }else{
1568 if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){
1569 throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
1570 }else{
1571 return;
1572 }
1573 }
1574 }
1575
1576 //Stored under name
1577 @Transient
1578 public TaxonNameBase getStoredUnder() {
1579 return derivedUnit.getStoredUnder();
1580 }
1581 public void setStoredUnder(TaxonNameBase storedUnder) {
1582 derivedUnit.setStoredUnder(storedUnder);
1583 }
1584
1585 //colletors number
1586 @Transient
1587 public String getCollectorsNumber() {
1588 return derivedUnit.getCollectorsNumber();
1589 }
1590 public void setCollectorsNumber(String collectorsNumber) {
1591 this.derivedUnit.setCollectorsNumber(collectorsNumber);
1592 }
1593
1594 //title cache
1595 public String getTitleCache() {
1596 if (! derivedUnit.isProtectedTitleCache()){
1597 //always compute title cache anew as long as there are no property change listeners on
1598 //field observation, gathering event etc
1599 derivedUnit.setTitleCache(null, false);
1600 }
1601 return this.derivedUnit.getTitleCache();
1602 }
1603 public void setTitleCache(String titleCache, boolean isProtected) {
1604 this.derivedUnit.setTitleCache(titleCache, isProtected);
1605 }
1606
1607
1608 /**
1609 * Returns the derived unit itself.
1610 * @return the derived unit
1611 */
1612 public DerivedUnitBase innerDerivedUnit() {
1613 return this.derivedUnit;
1614 }
1615
1616 private boolean hasDerivationEvent(){
1617 return getDerivationEvent() == null ? false : true;
1618 }
1619 private DerivationEvent getDerivationEvent(){
1620 return getDerivationEvent(false);
1621 }
1622 private DerivationEvent getDerivationEvent(boolean createIfNotExists){
1623 DerivationEvent result = derivedUnit.getDerivedFrom();
1624 if (result == null){
1625 result = DerivationEvent.NewInstance();
1626 derivedUnit.setDerivedFrom(result);
1627 }
1628 return result;
1629 }
1630 @Transient
1631 public String getExsiccatum() {
1632 logger.warn("Exsiccatum method not yet supported. Needs model change");
1633 return null;
1634 }
1635
1636 public void setExsiccatum(String exsiccatum) throws Exception{
1637 throw new Exception("Exsiccatum method not yet supported. Needs model change");
1638 }
1639
1640
1641 // **** sources **/
1642 public void addSource(IdentifiableSource source){
1643 this.derivedUnit.addSource(source);
1644 }
1645 /**
1646 * Creates an orignal source, adds it to the specimen and returns it.
1647 * @param reference
1648 * @param microReference
1649 * @param originalNameString
1650 * @return
1651 */
1652 public IdentifiableSource addSource(ReferenceBase reference, String microReference, String originalNameString){
1653 IdentifiableSource source = IdentifiableSource.NewInstance(reference, microReference);
1654 source.setOriginalNameString(originalNameString);
1655 derivedUnit.addSource(source);
1656 return source;
1657 }
1658
1659 @Transient
1660 public Set<IdentifiableSource> getSources(){
1661 return derivedUnit.getSources();
1662 }
1663
1664 public void removeSource(IdentifiableSource source){
1665 this.derivedUnit.removeSource(source);
1666 }
1667
1668
1669 /**
1670 * @return the collection
1671 */
1672 @Transient
1673 public Collection getCollection() {
1674 return derivedUnit.getCollection();
1675 }
1676
1677
1678 /**
1679 * @param collection the collection to set
1680 */
1681 public void setCollection(Collection collection) {
1682 derivedUnit.setCollection(collection);
1683 }
1684
1685 //annotation
1686 public void addAnnotation(Annotation annotation){
1687 this.derivedUnit.addAnnotation(annotation);
1688 }
1689
1690 @Transient
1691 public void getAnnotations(){
1692 this.derivedUnit.getAnnotations();
1693 }
1694
1695 public void removeAnnotation(Annotation annotation){
1696 this.derivedUnit.removeAnnotation(annotation);
1697 }
1698
1699
1700 // ******************************* Events *********************************************
1701
1702 /**
1703 * @return
1704 */
1705 private PropertyChangeListener getNewEventPropagationListener() {
1706 PropertyChangeListener listener = new PropertyChangeListener(){
1707 @Override
1708 public void propertyChange(PropertyChangeEvent event) {
1709 derivedUnit.firePropertyChange(event);
1710 }
1711
1712 };
1713 return listener;
1714 }
1715
1716
1717
1718
1719 //**************** Other Collections ***************************************************
1720
1721 /**
1722 * Creates a duplicate specimen which derives from the same derivation event
1723 * as the facade specimen and adds collection data to it (all data available in
1724 * DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and above
1725 * are not yet shared at the moment.
1726 * @param collection
1727 * @param catalogNumber
1728 * @param accessionNumber
1729 * @param collectorsNumber
1730 * @param storedUnder
1731 * @param preservation
1732 * @return
1733 */
1734 public Specimen addDuplicate(Collection collection, String catalogNumber, String accessionNumber,
1735 String collectorsNumber, TaxonNameBase storedUnder, PreservationMethod preservation){
1736 Specimen duplicate = Specimen.NewInstance();
1737 duplicate.setDerivedFrom(getDerivationEvent(true));
1738 duplicate.setCollection(collection);
1739 duplicate.setCatalogNumber(catalogNumber);
1740 duplicate.setAccessionNumber(accessionNumber);
1741 duplicate.setCollectorsNumber(collectorsNumber);
1742 duplicate.setStoredUnder(storedUnder);
1743 duplicate.setPreservation(preservation);
1744 return duplicate;
1745 }
1746
1747 public void addDuplicate(DerivedUnitBase duplicateSpecimen){
1748 //TODO check derivedUnitType
1749 getDerivationEvent(true).addDerivative(duplicateSpecimen);
1750 }
1751
1752 @Transient
1753 public Set<Specimen> getDuplicates(){
1754 Set<Specimen> result = new HashSet<Specimen>();
1755 if (hasDerivationEvent()){
1756 for (DerivedUnitBase derivedUnit: getDerivationEvent(true).getDerivatives()){
1757 if (derivedUnit.isInstanceOf(Specimen.class) && ! derivedUnit.equals(this.derivedUnit)){
1758 result.add(CdmBase.deproxy(derivedUnit, Specimen.class));
1759 }
1760 }
1761 }
1762 return result;
1763 }
1764 public void removeDuplicate(Specimen duplicateSpecimen){
1765 if (hasDerivationEvent()){
1766 getDerivationEvent(true).removeDerivative(duplicateSpecimen);
1767 }
1768 }
1769
1770
1771
1772
1773 }