Credits in model
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / IdentifiableEntity.java
index 415d9c9c12bf99681b17a23d1ea51fdcea5fe1d3..b4b69e8895acfda218e9e337d67f1adcc1f88dfd 100644 (file)
 package eu.etaxonomy.cdm.model.common;
 
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Embedded;
+import javax.persistence.FetchType;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.OneToMany;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+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.IndexColumn;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.FieldBridge;
+import org.hibernate.search.annotations.Fields;
 
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.persistence.*;
+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.NonViralName;
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 
 /**
  * Superclass for the primary CDM classes that can be referenced from outside via LSIDs and contain a simple generated title string as a label for human reading.
@@ -32,114 +55,261 @@ import javax.persistence.*;
  * @version 1.0
  * @created 08-Nov-2007 13:06:27
  */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "IdentifiableEntity", propOrder = {
+    "lsid",
+    "titleCache",
+    "protectedTitleCache",
+    "rights",
+    "extensions",
+    "credits",
+    "sources"
+})
 @MappedSuperclass
-public abstract class IdentifiableEntity<T extends IdentifiableEntity> extends AnnotatableEntity<T> implements IOriginalSource {
-       static Logger logger = Logger.getLogger(IdentifiableEntity.class);
+public abstract class IdentifiableEntity extends AnnotatableEntity 
+implements ISourceable, IIdentifiableEntity, Comparable<IdentifiableEntity> {
+       private static final long serialVersionUID = -5610995424730659058L;
+       private static final Logger logger = Logger.getLogger(IdentifiableEntity.class);
 
-       public final boolean PROTECTED = true;
-       public final boolean NOT_PROTECTED = true;
+       @XmlTransient
+       public static final boolean PROTECTED = true;
+       @XmlTransient
+       public static final boolean NOT_PROTECTED = false;
        
-       private String lsid;
+       @XmlElement(name = "LSID", type = String.class)
+       @XmlJavaTypeAdapter(LSIDAdapter.class)
+       @Embedded
+       private LSID lsid;
+       
+       @XmlElement(name = "TitleCache", required = true)
+       @XmlJavaTypeAdapter(FormattedTextAdapter.class)
+       @Column(length=255, name="titleCache")
+       @Fields({@Field(index = org.hibernate.search.annotations.Index.TOKENIZED),
+                @Field(name = "titleCache_forSort", index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
+       })
+       @FieldBridge(impl=StripHtmlBridge.class)
        private String titleCache;
+       
        //if true titleCache will not be automatically generated/updated
+       @XmlElement(name = "ProtectedTitleCache")
        private boolean protectedTitleCache;
+       
+    @XmlElementWrapper(name = "Rights")
+    @XmlElement(name = "Rights")
+    @OneToMany(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE})
        private Set<Rights> rights = new HashSet<Rights>();
+    
+    @XmlElementWrapper(name = "Credits")
+    @XmlElement(name = "Credit")
+    @IndexColumn(name="sortIndex", base = 0)
+       @OneToMany(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE})
+    private List<Credit> credits = new ArrayList<Credit>();
+       
+    @XmlElementWrapper(name = "Extensions")
+    @XmlElement(name = "Extension")
+    @OneToMany(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE})
        private Set<Extension> extensions = new HashSet<Extension>();
+       
+    @XmlElementWrapper(name = "Sources")
+    @XmlElement(name = "OriginalSource")
+    @OneToMany(fetch = FetchType.LAZY)         
+       @Cascade({CascadeType.SAVE_UPDATE})
        private Set<OriginalSource> sources = new HashSet<OriginalSource>();
 
        
-       public String getLsid(){
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getLsid()
+        */
+       public LSID getLsid(){
                return this.lsid;
        }
-       public void setLsid(String lsid){
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setLsid(java.lang.String)
+        */
+       public void setLsid(LSID lsid){
                this.lsid = lsid;
        }
 
+       /**
+        * By default, we expect most cdm objects to be abstract things 
+        * i.e. unable to return a data representation.
+        * 
+        * Specific subclasses (e.g. Sequence) can override if necessary.
+        */
+       public byte[] getData() {
+               return null;
+       }
+
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#generateTitle()
+        */
        public abstract String generateTitle();
 
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getTitleCache()
+        */
+       //@Transient
        public String getTitleCache(){
                if (protectedTitleCache){
                        return this.titleCache;                 
                }
                // is title dirty, i.e. equal NULL?
                if (titleCache == null){
-                       this.titleCache = generateTitle();
+                       this.setTitleCache(generateTitle(),protectedTitleCache) ; //for truncating
                }
                return titleCache;
        }
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String)
+        */
        public void setTitleCache(String titleCache){
-               this.titleCache = titleCache;
-               this.setProtectedTitleCache(true);
+               setTitleCache(titleCache, PROTECTED);
        }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String, boolean)
+        */
        public void setTitleCache(String titleCache, boolean protectCache){
+               //TODO truncation of title cache
+               if (titleCache != null && titleCache.length() > 254){
+                       logger.warn("Truncation of title cache: " + this.toString() + "/" + titleCache);
+                       titleCache = titleCache.substring(0, 249) + "...";
+               }
                this.titleCache = titleCache;
                this.setProtectedTitleCache(protectCache);
        }
        
-       @ManyToMany
-       @Cascade({CascadeType.SAVE_UPDATE})
-       public Set<Rights> getRights(){
-               return this.rights;
-       }
-
-       protected void setRights(Set<Rights> rights) {
-               this.rights = rights;
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
+        */
+       public Set<Rights> getRights() {
+               return this.rights;             
        }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addRights(eu.etaxonomy.cdm.model.media.Rights)
+        */
        public void addRights(Rights right){
                this.rights.add(right);
        }
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeRights(eu.etaxonomy.cdm.model.media.Rights)
+        */
        public void removeRights(Rights right){
                this.rights.remove(right);
        }
 
-       @OneToMany//(mappedBy="extendedObj")
-       @Cascade({CascadeType.SAVE_UPDATE})
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits()
+        */
+       public List<Credit> getCredits() {
+               return this.credits;            
+       }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits(int)
+        */
+       public Credit getCredits(int index){
+               return this.credits.get(index);
+       }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit)
+        */
+       public void addCredit(Credit credit){
+               this.credits.add(credit);
+       }
+       
+
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit, int)
+        */
+       public void addCredit(Credit credit, int index){
+               this.credits.add(index, credit);
+       }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(eu.etaxonomy.cdm.model.common.Credit)
+        */
+       public void removeCredit(Credit credit){
+               this.credits.remove(credit);
+       }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(int)
+        */
+       public void removeCredit(int index){
+               this.credits.remove(index);
+       }
+       
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
+        */
        public Set<Extension> getExtensions(){
                return this.extensions;
        }
-       protected void setExtensions(Set<Extension> extensions) {
-               this.extensions = extensions;
-       }
+       
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addExtension(eu.etaxonomy.cdm.model.common.Extension)
+        */
        public void addExtension(Extension extension){
                this.extensions.add(extension);
        }
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeExtension(eu.etaxonomy.cdm.model.common.Extension)
+        */
        public void removeExtension(Extension extension){
                this.extensions.remove(extension);
        }
 
        
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#isProtectedTitleCache()
+        */
        public boolean isProtectedTitleCache() {
                return protectedTitleCache;
        }
 
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setProtectedTitleCache(boolean)
+        */
        public void setProtectedTitleCache(boolean protectedTitleCache) {
                this.protectedTitleCache = protectedTitleCache;
        }
 
-       @OneToMany //(mappedBy="sourcedObj")            
-       @Cascade({CascadeType.SAVE_UPDATE})
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getSources()
+        */
        public Set<OriginalSource> getSources() {
                return this.sources;            
        }
-       protected void setSources(Set<OriginalSource> sources) {
-               this.sources = sources;         
-       }
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addSource(eu.etaxonomy.cdm.model.common.OriginalSource)
+        */
        public void addSource(OriginalSource source) {
-               this.sources.add(source);               
+               if (source != null){
+                       IdentifiableEntity oldSourcedObj = source.getSourcedObj();
+                       if (oldSourcedObj != null && oldSourcedObj != this){
+                               oldSourcedObj.getSources().remove(source);
+                       }
+                       this.sources.add(source);
+                       source.setSourcedObj(this);
+               }
        }
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeSource(eu.etaxonomy.cdm.model.common.OriginalSource)
+        */
        public void removeSource(OriginalSource source) {
                this.sources.remove(source);            
        }
        
-       /**
-        * Overrides {@link eu.etaxonomy.cdm.model.common.CdmBase#toString()}.
-        * This returns an String that identifies the object well without beeing necessarily unique.
-        * Specification: This method should never call other object' methods so it can be well used for debugging 
-        * without problems like lazy loading, unreal states etc.
-        * Note: If overriding this method's javadoc always copy or link the above requirement. 
-        * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object. 
-        * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
-        * @see java.lang.Object#toString()
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
         */
         @Override
        public String toString() {
@@ -151,5 +321,90 @@ public abstract class IdentifiableEntity<T extends IdentifiableEntity> extends A
                }
                return result;  
        }
+        
+        public int compareTo(IdentifiableEntity identifiableEntity) {
+
+                int result = 0;
+                
+                if (identifiableEntity == null) {
+                        throw new NullPointerException("Cannot compare to null.");
+                }
+
+                // First, compare the name cache.
+                // TODO: Avoid using instanceof operator
+                // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
 
+                // Compare name cache
+                String specifiedNameCache = "";
+                String thisNameCache = "";
+                
+                if(identifiableEntity instanceof NonViralName) {
+                        specifiedNameCache = ((NonViralName<?>)identifiableEntity).getNameCache();
+                } else if(identifiableEntity instanceof TaxonBase) {
+                        TaxonNameBase<?,?> taxonNameBase= ((TaxonBase)identifiableEntity).getName();
+                        specifiedNameCache = ((NonViralName<?>)taxonNameBase).getNameCache();
+                }
+                
+                if(this instanceof NonViralName) {
+                        thisNameCache = ((NonViralName<?>)this).getNameCache();
+                } else if(this instanceof TaxonBase) {
+                        TaxonNameBase<?,?> taxonNameBase= ((TaxonBase)this).getName();
+                        thisNameCache = ((NonViralName<?>)taxonNameBase).getNameCache();
+                }
+                
+                if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
+                        result = thisNameCache.compareTo(specifiedNameCache);
+                }
+                
+                // Compare title cache
+                if (result == 0) {
+                        String thisTitleCache = getTitleCache();
+                        String specifiedTitleCache = identifiableEntity.getTitleCache();
+                        result = thisTitleCache.compareTo(specifiedTitleCache);
+                }
+                return result;
+        }
+        
+        
+//****************** CLONE ************************************************/
+        
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
+        */
+       @Override
+       public Object clone() throws CloneNotSupportedException{
+               IdentifiableEntity result = (IdentifiableEntity)super.clone();
+               
+               //Extensions
+               result.extensions = new HashSet<Extension>();
+               for (Extension extension : this.extensions ){
+                       Extension newExtension = (Extension)extension.clone();
+                       result.addExtension(newExtension);
+               }
+               
+               //OriginalSources
+               result.sources = new HashSet<OriginalSource>();
+               for (OriginalSource originalSource : this.sources){
+                       OriginalSource newSource = (OriginalSource)originalSource.clone();
+                       result.addSource(newSource);
+               }
+               
+               //Rights
+               result.rights = new HashSet<Rights>();
+        for(Rights rights : this.rights) {
+               result.addRights(rights);
+        }
+               
+               //result.setLsid(lsid);
+               //result.setTitleCache(titleCache); 
+               //result.setProtectedTitleCache(protectedTitleCache);  //must be after setTitleCache
+               
+               //no changes to: lsid, titleCache, protectedTitleCache
+               
+               //empty titleCache
+               if (! protectedTitleCache){
+                       titleCache = null;
+               }
+               return result;
+       }
 }
\ No newline at end of file