- re-enabled identifier set in IdentifiableEntity
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / IdentifiableEntity.java
index 470f44c22008e6071cf6d935927e7209c752f522..5cbec1f13b8f2c02d5304bb0ab93478dda18cc52 100644 (file)
@@ -16,6 +16,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.persistence.Column;
 import javax.persistence.Embedded;
@@ -37,16 +38,20 @@ import org.apache.log4j.Logger;
 import org.hibernate.annotations.Cascade;
 import org.hibernate.annotations.CascadeType;
 import org.hibernate.annotations.IndexColumn;
+import org.hibernate.envers.Audited;
+import org.hibernate.search.annotations.Analyze;
 import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.FieldBridge;
-import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.search.annotations.Fields;
+import org.hibernate.search.annotations.Store;
 import org.hibernate.validator.constraints.NotEmpty;
 
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
-import eu.etaxonomy.cdm.hibernate.StripHtmlBridge;
+import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
 import eu.etaxonomy.cdm.jaxb.FormattedTextAdapter;
 import eu.etaxonomy.cdm.jaxb.LSIDAdapter;
 import eu.etaxonomy.cdm.model.media.Rights;
+import eu.etaxonomy.cdm.model.name.BotanicalName;
 import eu.etaxonomy.cdm.model.name.NonViralName;
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
 import eu.etaxonomy.cdm.model.reference.Reference;
@@ -81,6 +86,7 @@ import eu.etaxonomy.cdm.validation.Level2;
     "credits",
     "sources"
 })
+@Audited
 @MappedSuperclass
 public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrategy> extends AnnotatableEntity
         implements IIdentifiableEntity /*, ISourceable<IdentifiableSource> */ {
@@ -99,13 +105,15 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
 
     @XmlElement(name = "TitleCache", required = true)
     @XmlJavaTypeAdapter(FormattedTextAdapter.class)
-    @Column(length=255, name="titleCache")
-
-    @FieldBridge(impl=StripHtmlBridge.class)
+    @Column(name="titleCache")
     @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
     @NotEmpty(groups = Level2.class) // implictly NotNull
-    @Size(max = 255)
-    @Field
+    @Size(max = 800)  //see #1592
+    @Fields({
+        @Field(store=Store.YES),
+        @Field(name = "titleCache__sort", analyze = Analyze.NO, store=Store.YES)
+    })
+    @FieldBridge(impl=StripHtmlBridge.class)
     protected String titleCache;
 
     //if true titleCache will not be automatically generated/updated
@@ -114,8 +122,8 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
 
     @XmlElementWrapper(name = "Rights", nillable = true)
     @XmlElement(name = "Rights")
-    @OneToMany(fetch = FetchType.LAZY)
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
     //TODO
     @Merge(MergeMode.ADD_CLONE)
     @NotNull
@@ -124,8 +132,8 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
     @XmlElementWrapper(name = "Credits", nillable = true)
     @XmlElement(name = "Credit")
     @IndexColumn(name="sortIndex", base = 0)
-    @OneToMany(fetch = FetchType.LAZY)
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
     //TODO
     @Merge(MergeMode.ADD_CLONE)
     @NotNull
@@ -133,16 +141,24 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
 
     @XmlElementWrapper(name = "Extensions", nillable = true)
     @XmlElement(name = "Extension")
-    @OneToMany(fetch = FetchType.LAZY)
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
     @Merge(MergeMode.ADD_CLONE)
     @NotNull
     private Set<Extension> extensions = new HashSet<Extension>();
 
+    @XmlElementWrapper(name = "Identifiers", nillable = true)
+    @XmlElement(name = "Identifier")
+    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+    @Merge(MergeMode.ADD_CLONE)
+    @NotNull
+    private Set<Identifier> identifiers = new HashSet<Identifier>();
+
     @XmlElementWrapper(name = "Sources", nillable = true)
     @XmlElement(name = "IdentifiableSource")
-    @OneToMany(fetch = FetchType.LAZY)
-    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
+    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
     @Merge(MergeMode.ADD_CLONE)
     @NotNull
     private Set<IdentifiableSource> sources = new HashSet<IdentifiableSource>();
@@ -157,8 +173,9 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
 
     protected void initListener(){
         PropertyChangeListener listener = new PropertyChangeListener() {
-            public void propertyChange(PropertyChangeEvent e) {
-                if (!e.getPropertyName().equals("titleCache") && !e.getPropertyName().equals("cacheStrategy") && ! isProtectedTitleCache()){
+            @Override
+            public void propertyChange(PropertyChangeEvent ev) {
+                if (!ev.getPropertyName().equals("titleCache") && !ev.getPropertyName().equals("cacheStrategy") && ! isProtectedTitleCache()){
                     titleCache = null;
                 }
             }
@@ -172,17 +189,15 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
      *
      * Specific subclasses (e.g. Sequence) can override if necessary.
      */
+    @Override
     public byte[] getData() {
         return null;
     }
 
 //******************************** CACHE *****************************************************/
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getTitleCache()
-     */
     // @Transient  - must not be transient, since this property needs to to be included in all serializations produced by the remote layer
+    @Override
     public String getTitleCache(){
         if (protectedTitleCache){
             return this.titleCache;
@@ -194,16 +209,27 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         }
         return titleCache;
     }
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String)
+
+    /**
+     * The titleCache will be regenerated from scratch if not protected
+     * @return <code>true</code> if title cache was regenerated, <code>false</code> otherwise
      */
+    protected boolean regenerateTitleCache() {
+        if (!protectedTitleCache) {
+            this.titleCache = null;
+            getTitleCache();
+        }
+        return protectedTitleCache;
+    }
+
+    @Deprecated
+    @Override
     public void setTitleCache(String titleCache){
-        this.titleCache = titleCache;
+       //TODO shouldn't we call setTitleCache(String, boolean),but is this conformant with Java Bean Specification?
+       this.titleCache = getTruncatedCache(titleCache);
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String, boolean)
-     */
+    @Override
     public void setTitleCache(String titleCache, boolean protectCache){
         titleCache = getTruncatedCache(titleCache);
         this.titleCache = titleCache;
@@ -217,30 +243,25 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
      */
     @Transient
     protected String getTruncatedCache(String cache) {
-        if (cache != null && cache.length() > 255){
+        int maxLength = 800;
+       if (cache != null && cache.length() > maxLength){
             logger.warn("Truncation of cache: " + this.toString() + "/" + cache);
-            cache = cache.substring(0, 252) + "...";
+            cache = cache.substring(0, maxLength - 4) + "...";   //TODO do we need -4 or is -3 enough
         }
         return cache;
     }
 
 //**************************************************************************************
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getLsid()
-     */
+    @Override
     public LSID getLsid(){
         return this.lsid;
     }
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setLsid(java.lang.String)
-     */
+    @Override
     public void setLsid(LSID lsid){
         this.lsid = lsid;
     }
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
-     */
+    @Override
     public Set<Rights> getRights() {
         if(rights == null) {
             this.rights = new HashSet<Rights>();
@@ -248,20 +269,17 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         return this.rights;
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addRights(eu.etaxonomy.cdm.model.media.Rights)
-     */
+    @Override
     public void addRights(Rights right){
         getRights().add(right);
     }
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeRights(eu.etaxonomy.cdm.model.media.Rights)
-     */
+    @Override
     public void removeRights(Rights right){
         getRights().remove(right);
     }
 
 
+    @Override
     public List<Credit> getCredits() {
         if(credits == null) {
             this.credits = new ArrayList<Credit>();
@@ -269,69 +287,121 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         return this.credits;
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits(int)
-     */
+    @Override
     public Credit getCredits(Integer index){
         return getCredits().get(index);
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit)
-     */
+    @Override
     public void addCredit(Credit credit){
         getCredits().add(credit);
     }
 
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit, int)
-     */
+    @Override
     public void addCredit(Credit credit, int index){
         getCredits().add(index, credit);
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(eu.etaxonomy.cdm.model.common.Credit)
-     */
+    @Override
     public void removeCredit(Credit credit){
         getCredits().remove(credit);
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(int)
-     */
+    @Override
     public void removeCredit(int index){
         getCredits().remove(index);
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
+    @Override
+    public Set<Identifier> getIdentifiers(){
+        if(this.identifiers == null) {
+            this.identifiers = new HashSet<Identifier>();
+        }
+        return this.identifiers;
+    }
+    /**
+     * @param type
+     * @return a Set of extension value strings
+     */
+    public Set<String> getIdentifiers(DefinedTerm type){
+       return getIdentifiers(type.getUuid());
+    }
+    /**
+     * @param extensionTypeUuid
+     * @return a Set of extension value strings
      */
+    public Set<String> getIdentifiers(UUID identifierTypeUuid){
+        Set<String> result = new HashSet<String>();
+        for (Identifier identifier : getIdentifiers()){
+            if (identifier.getType().getUuid().equals(identifierTypeUuid)){
+                result.add(identifier.getIdentifier());
+            }
+        }
+        return result;
+    }
+
+    public Identifier addIdentifier(String identifier, DefinedTerm identifierType){
+       Identifier result = Identifier.NewInstance(this, identifier, identifierType);
+       return result;
+    }
+
+    @Override
+    public void addIdentifier(Identifier identifier){
+        if (identifier != null){
+               identifier.setIdentifiedObj(this);
+            getIdentifiers().add(identifier);
+        }
+    }
+    @Override
+    public void removeIdentifier(Identifier identifier){
+        if (identifier != null){
+               logger.warn("TODO");
+//             identifier.setExtendedObj(null);
+            getIdentifiers().remove(identifier);
+        }
+    }
+
+    @Override
     public Set<Extension> getExtensions(){
         if(extensions == null) {
             this.extensions = new HashSet<Extension>();
         }
         return this.extensions;
     }
+    /**
+     * @param type
+     * @return a Set of extension value strings
+     */
+    public Set<String> getExtensions(ExtensionType type){
+       return getExtensions(type.getUuid());
+    }
+    /**
+     * @param extensionTypeUuid
+     * @return a Set of extension value strings
+     */
+    public Set<String> getExtensions(UUID extensionTypeUuid){
+        Set<String> result = new HashSet<String>();
+        for (Extension extension : getExtensions()){
+            if (extension.getType().getUuid().equals(extensionTypeUuid)){
+                result.add(extension.getValue());
+            }
+        }
+        return result;
+    }
 
     public void addExtension(String value, ExtensionType extensionType){
         Extension.NewInstance(this, value, extensionType);
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addExtension(eu.etaxonomy.cdm.model.common.Extension)
-     */
+    @Override
     public void addExtension(Extension extension){
         if (extension != null){
             extension.setExtendedObj(this);
             getExtensions().add(extension);
         }
     }
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeExtension(eu.etaxonomy.cdm.model.common.Extension)
-     */
+    @Override
     public void removeExtension(Extension extension){
         if (extension != null){
             extension.setExtendedObj(null);
@@ -339,24 +409,17 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         }
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#isProtectedTitleCache()
-     */
+    @Override
     public boolean isProtectedTitleCache() {
         return protectedTitleCache;
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setProtectedTitleCache(boolean)
-     */
+    @Override
     public void setProtectedTitleCache(boolean protectedTitleCache) {
         this.protectedTitleCache = protectedTitleCache;
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
-     */
+    @Override
     public Set<IdentifiableSource> getSources() {
         if(sources == null) {
             this.sources = new HashSet<IdentifiableSource>();
@@ -364,12 +427,10 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         return this.sources;
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.OriginalSourceBase)
-     */
+    @Override
     public void addSource(IdentifiableSource source) {
         if (source != null){
-            IdentifiableEntity oldSourcedObj = source.getSourcedObj();
+            IdentifiableEntity<?> oldSourcedObj = source.getSourcedObj();
             if (oldSourcedObj != null && oldSourcedObj != this){
                 oldSourcedObj.getSources().remove(source);
             }
@@ -378,21 +439,48 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
         }
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
-     */
-    public IdentifiableSource addSource(String id, String idNamespace, Reference citation, String microCitation) {
+    @Override
+    public void addSources(Set<IdentifiableSource> sources) {
+        if (sources != null){
+               for (IdentifiableSource source: sources){
+                   IdentifiableEntity<?> oldSourcedObj = source.getSourcedObj();
+                   if (oldSourcedObj != null && oldSourcedObj != this){
+                       oldSourcedObj.getSources().remove(source);
+                   }
+                   getSources().add(source);
+                   source.setSourcedObj(this);
+               }
+        }
+    }
+    
+    @Override
+    public void removeSources() {
+       this.sources.clear();
+    }
+    
+    @Override
+    public IdentifiableSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
         if (id == null && idNamespace == null && citation == null && microCitation == null){
             return null;
         }
-        IdentifiableSource source = IdentifiableSource.NewInstance(id, idNamespace, citation, microCitation);
+        IdentifiableSource source = IdentifiableSource.NewInstance(type, id, idNamespace, citation, microCitation);
         addSource(source);
         return source;
     }
 
-     /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
-     */
+
+    @Override
+    public IdentifiableSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
+        if (id == null && idNamespace == null && citation == null && microCitation == null){
+            return null;
+        }
+        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
+        addSource(source);
+        return source;
+    }
+
+
+    @Override
     public void removeSource(IdentifiableSource source) {
         getSources().remove(source);
     }
@@ -436,9 +524,25 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
          String specifiedGenusString = "";
          int thisrank_order = 0;
 
+         //TODO we can remove all the deproxies here except for the first one
+         identifiableEntity = HibernateProxyHelper.deproxy(identifiableEntity, IdentifiableEntity.class);
          if(identifiableEntity instanceof NonViralName) {
              specifiedNameCache = HibernateProxyHelper.deproxy(identifiableEntity, NonViralName.class).getNameCache();
              specifiedTitleCache = identifiableEntity.getTitleCache();
+            if (identifiableEntity instanceof BotanicalName){
+                if (((BotanicalName)identifiableEntity).isAutonym()){
+                        boolean isProtected = false;
+                        String oldNameCache = ((BotanicalName) identifiableEntity).getNameCache();
+                        if ( ((BotanicalName)identifiableEntity).isProtectedNameCache()){
+                                isProtected = true;
+                        }
+                        ((BotanicalName)identifiableEntity).setProtectedNameCache(false);
+                        ((BotanicalName)identifiableEntity).setNameCache(null, false);
+                        specifiedNameCache = ((BotanicalName) identifiableEntity).getNameCache();
+                        ((BotanicalName)identifiableEntity).setNameCache(oldNameCache, isProtected);
+
+                }
+             }
 
          } else if(identifiableEntity instanceof TaxonBase) {
              TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
@@ -458,20 +562,36 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
              }
          }
 
-         if(this instanceof NonViralName) {
+         if(this.isInstanceOf(NonViralName.class)) {
              thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
              thisTitleCache = getTitleCache();
-         } else if(this instanceof TaxonBase) {
+
+             if (this instanceof BotanicalName){
+                if (((BotanicalName)this).isAutonym()){
+                        boolean isProtected = false;
+                        String oldNameCache = ((BotanicalName) this).getNameCache();
+                        if ( ((BotanicalName)this).isProtectedNameCache()){
+                                isProtected = true;
+                        }
+                        ((BotanicalName)this).setProtectedNameCache(false);
+                        ((BotanicalName)this).setNameCache(null, false);
+                        thisNameCache = ((BotanicalName) this).getNameCache();
+                        ((BotanicalName)this).setNameCache(oldNameCache, isProtected);
+                }
+             }
+         } else if(this.isInstanceOf(TaxonBase.class)) {
              TaxonNameBase<?,?> taxonNameBase= HibernateProxyHelper.deproxy(this, TaxonBase.class).getName();
              NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
              thisNameCache = nonViralName.getNameCache();
              thisTitleCache = taxonNameBase.getTitleCache();
-             thisReferenceTitleCache = getTitleCache();
+             thisReferenceTitleCache = ((TaxonBase)this).getSec().getTitleCache();
              thisGenusString = nonViralName.getGenusOrUninomial();
          }
 
          // Compare name cache of taxon names
 
+
+
          if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
              result = thisNameCache.compareTo(specifiedNameCache);
          }
@@ -510,12 +630,13 @@ public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrat
             this.cacheStrategy = cacheStrategy;
         }
 
+        @Override
         public String generateTitle() {
-            if (cacheStrategy == null){
+            if (getCacheStrategy() == null){
                 //logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
                 return this.getClass() + ": " + this.getUuid();
             }else{
-                return cacheStrategy.getTitleCache(this);
+                return getCacheStrategy().getTitleCache(this);
             }
         }