ref #9556 avoiding collection of full derivatives tree in DTOs
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / occurrence / SpecimenOrObservationBase.java
index c839e7020178b2c393e9c24690d7c102149d8e6e..da3d34d54246d5ff8549beb2601dac79c048215e 100644 (file)
@@ -6,10 +6,8 @@
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * See LICENSE.TXT at the top of this package for the full license terms.
 */
-
 package eu.etaxonomy.cdm.model.occurrence;
 
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -20,14 +18,15 @@ import java.util.Set;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
+import javax.persistence.Index;
 import javax.persistence.Inheritance;
 import javax.persistence.InheritanceType;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
 import javax.persistence.MapKeyJoinColumn;
 import javax.persistence.OneToMany;
+import javax.persistence.Table;
 import javax.persistence.Transient;
-import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -43,8 +42,6 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.log4j.Logger;
 import org.hibernate.annotations.Cascade;
 import org.hibernate.annotations.CascadeType;
-import org.hibernate.annotations.Index;
-import org.hibernate.annotations.Table;
 import org.hibernate.annotations.Type;
 import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Analyze;
@@ -52,28 +49,31 @@ import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.FieldBridge;
 import org.hibernate.search.annotations.Fields;
 import org.hibernate.search.annotations.IndexedEmbedded;
-import org.hibernate.search.annotations.NumericField;
 import org.hibernate.search.annotations.SortableField;
 import org.hibernate.search.annotations.Store;
+import org.hibernate.search.bridge.builtin.BooleanBridge;
 
+import eu.etaxonomy.cdm.common.URI;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
+import eu.etaxonomy.cdm.hibernate.search.UriBridge;
 import eu.etaxonomy.cdm.jaxb.FormattedTextAdapter;
 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
-import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
 import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
 import eu.etaxonomy.cdm.model.common.IPublishable;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
 import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.LanguageString;
 import eu.etaxonomy.cdm.model.common.MultilanguageText;
-import eu.etaxonomy.cdm.model.common.TermType;
 import eu.etaxonomy.cdm.model.description.DescriptionBase;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
 import eu.etaxonomy.cdm.model.description.IDescribable;
 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
+import eu.etaxonomy.cdm.model.term.DefinedTerm;
+import eu.etaxonomy.cdm.model.term.TermType;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
 import eu.etaxonomy.cdm.strategy.match.Match;
 import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
@@ -82,7 +82,7 @@ import eu.etaxonomy.cdm.strategy.match.MatchMode;
 /**
  * type figures are observations with at least a figure object in media
  * @author m.doering
- * @created 08-Nov-2007 13:06:41
+ * @since 08-Nov-2007 13:06:41
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "SpecimenOrObservationBase", propOrder = {
@@ -104,11 +104,12 @@ import eu.etaxonomy.cdm.strategy.match.MatchMode;
 @Entity
 @Audited
 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
-@Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }),
-        @Index(name = "specimenOrObservationBaseIdentityCacheIndex", columnNames = { "identityCache" }) })
+@Table(name="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnList = "titleCache"),
+        @Index(name = "specimenOrObservationBaseIdentityCacheIndex", columnList = "identityCache") })
 public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy<?>>
                 extends IdentifiableEntity<S>
-                implements IMultiLanguageTextHolder, IDescribable<DescriptionBase<S>>, IPublishable  {
+                implements IMultiLanguageTextHolder, IIntextReferenceTarget, IDescribable<DescriptionBase<S>>, IPublishable  {
+
     private static final long serialVersionUID = 6932680139334408031L;
     private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
 
@@ -126,7 +127,7 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
     @Column(name="recordBasis")
     @NotNull
     @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
-        parameters = {@org.hibernate.annotations.Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
+        parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
     )
     @Audited
     private SpecimenOrObservationType recordBasis;
@@ -176,16 +177,15 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
 
     @XmlElement(name = "IndividualCount")
     @Field(analyze = Analyze.NO)
-    @NumericField
-    @Min(0)
-    private Integer individualCount;
+    private String individualCount;
 
     /**
-     * The preferred stable identifer (URI) as discussed in
-     * {@link  http://dev.e-taxonomy.eu/trac/ticket/5606}
+     * The preferred stable identifier (URI) as discussed in
+     * {@link  https://dev.e-taxonomy.eu/redmine/issues/5606}
      */
     @XmlElement(name = "PreferredStableUri")
     @Field(analyze = Analyze.NO)
+    @FieldBridge(impl = UriBridge.class)
     @Type(type="uriUserType")
     private URI preferredStableUri;
 
@@ -208,15 +208,17 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
     @ManyToMany(fetch=FetchType.LAZY)
     @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
     @NotNull
-    protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
+    protected Set<DerivationEvent> derivationEvents = new HashSet<>();
 
     @XmlAttribute(name = "publish")
+    @Field(analyze = Analyze.NO)
+    @FieldBridge(impl=BooleanBridge.class)
     private boolean publish = true;
 
     @XmlElement(name = "IdentityCache", required = false)
     @XmlJavaTypeAdapter(FormattedTextAdapter.class)
     @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
-//    @NotEmpty(groups = Level2.class) // implictly NotNull
+//    @NotEmpty(groups = Level2.class) // implicitly NotNull
     @Fields({
         @Field(store=Store.YES),
         //  If the field is only needed for sorting and nothing else, you may configure it as
@@ -247,16 +249,8 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
         this.recordBasis = recordBasis;
     }
 
-
-    /**
-     * Subclasses should implement setting the default cache strate
-     */
-    protected abstract void initDefaultCacheStrategy();
-
-
 //************************* GETTER / SETTER ***********************/
 
-
     /**@see #recordBasis */
     public SpecimenOrObservationType getRecordBasis() {
         return recordBasis;
@@ -274,12 +268,19 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
         return identityCache;
     }
     /**
-     * @param identityCache the identityCache to set
+     * @Deprecated For special use only.
+     * Use {@link #setIdentityCache(String, boolean)} instead
      */
+    @Deprecated
     public void setIdentityCache(String identityCache) {
         this.identityCache = identityCache;
     }
 
+    public void setIdentityCache(String identityCache, boolean isProtected) {
+        this.protectedIdentityCache = isProtected;
+        setIdentityCache(identityCache);
+    }
+
     /**
      * @return the protectedIdentityCache
      */
@@ -358,8 +359,8 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
      */
     @Transient
     public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
-        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
-        for (DescriptionBase descriptionBase : getDescriptions()){
+        Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
+        for (DescriptionBase<?> descriptionBase : getDescriptions()){
             if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
                 if (includeImageGallery || descriptionBase.isImageGallery() == false){
                     specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
@@ -377,8 +378,8 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
      */
     @Transient
     public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
-        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
-        for (DescriptionBase descriptionBase : getDescriptions()){
+        Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
+        for (DescriptionBase<?> descriptionBase : getDescriptions()){
             if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
                 if (descriptionBase.isImageGallery() == true){
                     specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
@@ -416,7 +417,7 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
 
     public Set<DerivationEvent> getDerivationEvents() {
         if(derivationEvents == null) {
-            this.derivationEvents = new HashSet<DerivationEvent>();
+            this.derivationEvents = new HashSet<>();
         }
         return this.derivationEvents;
     }
@@ -437,7 +438,7 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
 
     public Set<DeterminationEvent> getDeterminations() {
         if(determinations == null) {
-            this.determinations = new HashSet<DeterminationEvent>();
+            this.determinations = new HashSet<>();
         }
         return this.determinations;
     }
@@ -485,11 +486,11 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
         this.kindOfUnit = kindOfUnit;
     }
 
-    public Integer getIndividualCount() {
+    public String getIndividualCount() {
         return individualCount;
     }
 
-    public void setIndividualCount(Integer individualCount) {
+    public void setIndividualCount(String individualCount) {
         this.individualCount = individualCount;
     }
 
@@ -564,7 +565,7 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
      */
     @Transient
     public Collection<DescriptionElementBase> characterData() {
-        Collection<DescriptionElementBase> states = new ArrayList<DescriptionElementBase>();
+        Collection<DescriptionElementBase> states = new ArrayList<>();
         Set<DescriptionBase<S>> descriptions = this.getDescriptions();
         for (DescriptionBase<?> descriptionBase : descriptions) {
             if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
@@ -580,17 +581,23 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
         return states;
     }
 
-
+    public Collection<DerivedUnit> collectDerivedUnits(boolean addFullSubTree) {
+        Collection<DerivedUnit> derivedUnits = new ArrayList<>();
+        for (DerivationEvent derivationEvent : getDerivationEvents()) {
+            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
+                derivedUnits.add(derivative);
+                if(addFullSubTree) {
+                    derivedUnits.addAll(derivative.collectDerivedUnits(false));
+                }
+            }
+        }
+        return derivedUnits;
+    }
 
 //******************** CLONE **********************************************/
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
-     * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
-     * @see java.lang.Object#clone()
-     */
     @Override
-    public Object clone() throws CloneNotSupportedException {
+    public SpecimenOrObservationBase<S> clone() throws CloneNotSupportedException {
         SpecimenOrObservationBase<S> result = (SpecimenOrObservationBase<S>)super.clone();
 
         //defininion (description, languageString)
@@ -601,25 +608,25 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
         //life stage
         result.setLifeStage(this.lifeStage);
 
+        result.descriptions = new HashSet<>();
         //Descriptions
         for(DescriptionBase<S> description : this.descriptions) {
-            result.addDescription(description);
+            result.addDescription(description.clone());
         }
 
-        //DeterminationEvent FIXME should clone() the determination
-        // as the relationship is OneToMany
+        result.determinations = new HashSet<>();
         for(DeterminationEvent determination : this.determinations) {
-            result.addDetermination(determination);
+            result.addDetermination(determination.clone());
         }
 
+        result.derivationEvents = new HashSet<>();
         //DerivationEvent
         for(DerivationEvent derivationEvent : this.derivationEvents) {
+            //TODO should we clone this?
             result.addDerivationEvent(derivationEvent);
         }
 
         //no changes to: individualCount
         return result;
     }
-
-
 }