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