#4716 reduicing clutter in the lucene index and solving performance problems which...
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / agent / Person.java
index 5506a24d22abb3da74eb8c3f3df8b6083a5df758..e44fe9c6670b31c7c96bf275bf303b1b20120270 100644 (file)
 /**
 * Copyright (C) 2007 EDIT
-* European Distributed Institute of Taxonomy 
+* European Distributed Institute of Taxonomy
 * http://www.e-taxonomy.eu
-* 
+*
 * 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.agent;
 
-import eu.etaxonomy.cdm.model.common.TimePeriod;
-import eu.etaxonomy.cdm.model.common.Keyword;
-import eu.etaxonomy.cdm.strategy.cache.PersonDefaultCacheStrategy;
+import java.util.HashSet;
+import java.util.Set;
+
+import javassist.compiler.ast.Keyword;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.OneToMany;
+import javax.validation.constraints.Size;
+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.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.hibernate.annotations.Cascade;
 import org.hibernate.annotations.CascadeType;
-import java.util.*;
-import javax.persistence.*;
+import org.hibernate.envers.Audited;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.joda.time.Partial;
+import org.springframework.beans.factory.annotation.Configurable;
+
+import eu.etaxonomy.cdm.model.common.TimePeriod;
+import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
+import eu.etaxonomy.cdm.strategy.match.Match;
+import eu.etaxonomy.cdm.strategy.match.MatchMode;
 
 /**
- * A representation of a human being, living or dead.
+ * This class represents human beings, living or dead.<BR>
  * It includes name parts, {@link Contact contact} details, {@link InstitutionalMembership institutional membership},
- * and other possible information such as life {@link common.TimePeriod time period},
- * taxonomic and/or geographical {@link common.Keyword specialization}.
+ * and other possible information such as life {@link TimePeriod time period},
+ * taxonomic and/or geographical {@link Keyword specialization}.
  * For a short abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
- * is to be used.
- * For other alternative (string-)names {@link common.OriginalSource OriginalSource} instances must be created
- * and the inherited attribute {@link common.ReferencedEntityBase#getOriginalNameString() originalNameString} must be used.
- * <p>
- * See also the <a href="http://rs.tdwg.org/ontology/voc/Person.rdf">TDWG Ontology</a>
- * 
+ * is to be used.<BR>
+ * For other alternative (string-)names {@link eu.etaxonomy.cdm.model.common.OriginalSourceBase OriginalSource} instances must be created
+ * and the inherited attribute {@link eu.etaxonomy.cdm.model.common.ReferencedEntityBase#getOriginalNameString() originalNameString} must be used.
+ * <P>
+ * This class corresponds to: <ul>
+ * <li> Person according to the TDWG ontology
+ * <li> AgentName (partially) according to the TCS
+ * <li> Person (PersonName partially) according to the ABCD schema
+ * </ul>
+ *
  * @author m.doering
  * @version 1.0
  * @created 08-Nov-2007 13:06:42
  */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "Person", propOrder = {
+           "prefix",
+           "firstname",
+           "lastname",
+           "suffix",
+           "lifespan",
+           "institutionalMemberships"
+})
+@XmlRootElement(name = "Person")
 @Entity
-public class Person extends TeamOrPersonBase {
-       static Logger logger = Logger.getLogger(Person.class);
+//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
+//@Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
+@Audited
+@Configurable
+public class Person extends TeamOrPersonBase<Person>{
+       private static final long serialVersionUID = 4153566493065539763L;
+       public static final Logger logger = Logger.getLogger(Person.class);
 
+    @XmlElement(name = "Prefix")
+    @Field
+  //TODO Val #3379
+//    @NullOrNotEmpty
+    @Size(max = 255)
        private String prefix;
+
+    @XmlElement(name = "FirstName")
+    @Field
+  //TODO Val #3379
+//    @NullOrNotEmpty
+    @Size(max = 255)
        private String firstname;
+
+    @XmlElement(name = "LastName")
+    @Field
+  //TODO Val #3379
+//    @NullOrNotEmpty
+    @Size(max = 255)
        private String lastname;
+
+    @XmlElement(name = "Suffix")
+    @Field
+  //TODO Val #3379
+//    @NullOrNotEmpty
+    @Size(max = 255)
        private String suffix;
-       private TimePeriod lifespan;
+
+    @XmlElement(name = "Lifespan")
+    @IndexedEmbedded
+    @Match(value=MatchMode.EQUAL_OR_ONE_NULL)
+  //TODO Val #3379    check carefully what the condition is that lifespan is really null in legacy data
+//    @NotNull
+       private TimePeriod lifespan = TimePeriod.NewInstance();
+
+    @XmlElementWrapper(name = "InstitutionalMemberships", nillable = true)
+    @XmlElement(name = "InstitutionalMembership")
+    @OneToMany(fetch=FetchType.LAZY, mappedBy = "person")
+       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
        protected Set<InstitutionalMembership> institutionalMemberships;
-       private Contact contact;
-       private Set<Keyword> keywords = new HashSet<Keyword>();
 
-       /** 
+       /**
         * Creates a new empty instance for a person whose existence is all what is known.
-        * This can be a provisional solution until more information about this person
+        * This can be a provisional solution until more information about <i>this</i> person
         * can be gathered, for instance in case a member of a nomenclatural author team
         * is not explicitly mentioned. It also includes the cache strategy defined in
-        * {@link eu.etaxonomy.cdm.strategy.cache.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
+        * {@link eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
         */
        public static Person NewInstance(){
                return new Person();
        }
-       
-       /** 
+
+       /**
         * Creates a new instance for a person for whom an "identification" string
         * is all what is known. This string is generally a short or a complete name.
-        * As this string is kept in the {@link common.IdentifiableEntity#getTitleCache() titleCache}
+        * As this string is kept in the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache}
         * attribute and should not be overwritten by the {@link #generateTitle() generateTitle} method
-        * the {@link common.IdentifiableEntity#isProtectedTitleCache() protectedTitleCache} flag will be turned on. 
+        * the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#isProtectedTitleCache() protectedTitleCache} flag will be turned on.
         */
        public static Person NewTitledInstance(String titleCache){
                Person result = new Person();
-               result.setTitleCache(titleCache);
+               result.setTitleCache(titleCache, true);
                return result;
        }
-       
-       
-       /** 
+
+
+       /**
         * Class constructor.
-        * 
+        *
         * @see #Person(String, String, String)
         */
-       private Person() {
+       protected Person() {
                super();
                this.cacheStrategy = PersonDefaultCacheStrategy.NewInstance();
 
        }
-       
-       /** 
+
+       /**
         * Class constructor using a "forenames" string (including initials),
         * a surname (family name) and an abbreviated name as used in nomenclature.
         * For the abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
@@ -98,127 +171,68 @@ public class Person extends TeamOrPersonBase {
        public Person(String firstname, String lastname, String nomenclaturalTitel) {
                this.setFirstname(firstname);
                this.setLastname(lastname);
+               logger.debug("before - Set nomenclatural Title");
                this.setNomenclaturalTitle(nomenclaturalTitel);
+               logger.debug("after - Set nomenclatural Title");
        }
-       
-       
-       /** 
-        * Returns the set of {@link InstitutionalMembership institution memberships} corresponding to this person. 
+
+
+       /**
+        * Returns the set of {@link InstitutionalMembership institution memberships} corresponding to <i>this</i> person.
         *
         * @see     InstitutionalMembership
         */
-       @OneToMany
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
        public Set<InstitutionalMembership> getInstitutionalMemberships(){
+               if(institutionalMemberships == null) {
+                       this.institutionalMemberships = new HashSet<InstitutionalMembership>();
+               }
                return this.institutionalMemberships;
        }
-       /** 
-        * @see     #getInstitutionalMemberships()
-        */
-       protected void setInstitutionalMemberships(Set<InstitutionalMembership> institutionalMemberships){
-               this.institutionalMemberships = institutionalMemberships;
+
+       protected void addInstitutionalMembership(InstitutionalMembership ims){
+               getInstitutionalMemberships().add(ims);
+               if (ims.getPerson() != this){
+                       logger.warn("Institutional membership's person has to be changed for adding it to person: " + this);
+                       ims.getPerson().removeInstitutionalMembership(ims);
+                       ims.setPerson(this);
+
+               }
        }
-       
-       /** 
-        * Adds a new {@link InstitutionalMembership membership} of this person in an {@link Institution institution}
+
+       /**
+        * Adds a new {@link InstitutionalMembership membership} of <i>this</i> person in an {@link Institution institution}
         * to the set of his institution memberships.
         * This method also creates a new institutional membership instance.
         *
-        * @param  institution  the institution this person belongs to
-        * @param  period       the time period for which this person has been a member of the institution
-        * @param  department   the string label for the department this person belongs to,
+        * @param  institution  the institution <i>this</i> person belongs to
+        * @param  period       the time period for which <i>this</i> person has been a member of the institution
+        * @param  department   the string label for the department <i>this</i> person belongs to,
         *                                          within the institution
         * @param  role         the string label for the persons's role within the department or institution
         * @see                             #getInstitutionalMemberships()
         * @see                             InstitutionalMembership#InstitutionalMembership(Institution, Person, TimePeriod, String, String)
         */
-       public void addInstitutionalMembership(Institution institution, TimePeriod period, String department, String role){
-               //TODO to be implemented?
-               logger.warn("not yet fully implemented?");
-               InstitutionalMembership ims = new InstitutionalMembership(institution, this, period, department, role);
-               institutionalMemberships.add(ims);
+       public InstitutionalMembership addInstitutionalMembership(Institution institution, TimePeriod period, String department, String role){
+               return new InstitutionalMembership(institution, this, period, department, role);
        }
-       
-       /** 
-        * Removes one element from the set of institutional memberships of this person.
+
+       /**
+        * Removes one element from the set of institutional memberships of <i>this</i> person.
         * Institute and person attributes of the institutional membership object
         * will be nullified.
         *
-        * @param  ims  the institutional membership of this person which should be deleted
+        * @param  ims  the institutional membership of <i>this</i> person which should be deleted
         * @see         #getInstitutionalMemberships()
         */
        public void removeInstitutionalMembership(InstitutionalMembership ims){
-               //TODO to be implemented?
-               logger.warn("not yet fully implemented?");
                ims.setInstitute(null);
                ims.setPerson(null);
-               this.institutionalMemberships.remove(ims);
+               getInstitutionalMemberships().remove(ims);
        }
 
-
-       /** 
-        * Returns the set of {@link common.Keyword keywords} mostly representing a taxonomic or
-        * a geographical specialization of this person.
-        * Keywords are items of a controlled {@link common.TermVocabulary vocabulary}.
-        *
-        * @see         common.Keyword
-        */
-       @OneToMany
-       @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
-       public Set<Keyword> getKeywords(){
-               return this.keywords;
-       }
-       /** 
-        * @see     #getKeywords()
-        */
-       public void setKeywords(Set<Keyword> keywords){
-               this.keywords = keywords;
-       }
-       /** 
-        * Adds a new keyword from the keyword vocabulary to the set of keywords
-        * describing or circumscribing this person's activities.
-        *
-        * @param  keyword  any keyword 
-        * @see                         #getKeywords()
-        * @see                         common.Keyword
-        */
-       public void addKeyword(Keyword keyword){
-               this.keywords.add(keyword);
-       }
-       /** 
-        * Removes one element from the set of keywords for this person.
-        *
-        * @param  keyword  the keyword which should be deleted
-        * @see             #getKeywords()
-        */
-       public void removeKeyword(Keyword keyword){
-               this.keywords.remove(keyword);
-       }
-
-
-
-       /** 
-        * Returns the {@link Contact contact} of this person.
-        * The contact contains several ways to approach this person.
-        *
-        * @see         Contact
-        */
-       @ManyToOne
-       @Cascade({CascadeType.SAVE_UPDATE})
-       public Contact getContact(){
-               return this.contact;
-       }
-       /**
-        * @see  #getContact()
-        */
-       public void setContact(Contact contact){
-               this.contact = contact;
-       }
-
-       
        /**
         * Returns the string representing the prefix (for instance "Prof.&nbsp;Dr.<!-- -->")
-        * to this person's name.
+        * to <i>this</i> person's name.
         */
        public String getPrefix(){
                return this.prefix;
@@ -227,16 +241,16 @@ public class Person extends TeamOrPersonBase {
         * @see  #getPrefix()
         */
        public void setPrefix(String prefix){
-               this.prefix = prefix;
+               this.prefix = StringUtils.isBlank(prefix) ? null : prefix;
        }
 
 
        /**
         * Returns the string representing the given name or forename
-        * (for instance "John") of this person. 
+        * (for instance "John") of <i>this</i> person.
         * This is the part of his name which is not shared with other
-        * family members. Actually it may be just initials (for instance "G. Jr."),
-        * all forenames in full or a combination of expanded names and initials. 
+        * family members. Actually it may be just initials (for instance "G.&nbsp;Jr."),
+        * all forenames in full or a combination of expanded names and initials.
         */
        public String getFirstname(){
                return this.firstname;
@@ -245,15 +259,15 @@ public class Person extends TeamOrPersonBase {
         * @see  #getFirstname()
         */
        public void setFirstname(String firstname){
-               this.firstname = firstname;
+               this.firstname = StringUtils.isBlank(firstname) ? null : firstname;
        }
 
-       
+
        /**
         * Returns the string representing the hereditary name (surname or family name)
-        * (for instance "Smith") of this person. 
+        * (for instance "Smith") of <i>this</i> person.
         * This is the part of his name which is common to (all) other
-        * members of his family, as distinct from the given name or forename. 
+        * members of his family, as distinct from the given name or forename.
         */
        public String getLastname(){
                return this.lastname;
@@ -262,13 +276,13 @@ public class Person extends TeamOrPersonBase {
         * @see  #getLastname()
         */
        public void setLastname(String lastname){
-               this.lastname = lastname;
+               this.lastname = StringUtils.isBlank(lastname) ? null : lastname;
        }
 
 
        /**
         * Returns the string representing the suffix (for instance "Junior")
-        * of this person's name.
+        * of <i>this</i> person's name.
         */
        public String getSuffix(){
                return this.suffix;
@@ -277,48 +291,78 @@ public class Person extends TeamOrPersonBase {
         * @see  #getSuffix()
         */
        public void setSuffix(String suffix){
-               this.suffix = suffix;
+               this.suffix = StringUtils.isBlank(suffix) ? null: suffix;
        }
 
 
-       /** 
-        * Returns the {@link common.TimePeriod period of time}
-        * in which this person was alive (life span).
+       /**
+        * Returns the {@link eu.etaxonomy.cdm.model.common.TimePeriod period of time}
+        * in which <i>this</i> person was alive (life span).
         * The general form is birth date - death date
         * (XXXX - YYYY; XXXX - or - YYYY as appropriate),
         * but a simple flourished date (fl. XXXX) is also possible
         * if that is all what is known.
         *
-        * @see  common.TimePeriod
+        * @see  eu.etaxonomy.cdm.model.common.TimePeriod
         */
        public TimePeriod getLifespan(){
+               if(lifespan == null) {
+                       this.lifespan = TimePeriod.NewInstance(new Partial(), new Partial());
+               }
                return this.lifespan;
        }
        /**
         * @see  #getLifespan()
         */
        public void setLifespan(TimePeriod lifespan){
+               if (lifespan == null){
+                       this.lifespan = TimePeriod.NewInstance(new Partial(), new Partial());
+               }
                this.lifespan = lifespan;
        }
 
+//     /**
+//      * Generates the "full" name string of <i>this</i> person according to the strategy
+//      * defined in {@link eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
+//      * The used attributes are:
+//      * {@link #getPrefix() prefix}, {@link #getFirstname() firstname}, {@link #getLastname() lastname} and {@link #getSuffix() suffix}.
+//      * This method overrides {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle() generateTitle}.
+//      * The result might be kept as {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#setTitleCache(String) titleCache} if the
+//      * flag {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#protectedTitleCache protectedTitleCache} is not set.
+//      *
+//      * @return  the string with the full name of <i>this</i> person
+//      */
+//     @Override
+//     public String generateTitle() {
+//             String title = null;
+//             if (cacheStrategy != null) {
+//             title = cacheStrategy.getTitleCache(this);
+//             }
+//        return title;
+//     }
+
+//*********************** CLONE ********************************************************/
+
        /**
-        * Generates the "full" name string of this person according to the strategy
-        * defined in {@link eu.etaxonomy.cdm.strategy.cache.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
-        * The used attributes are:
-        * {@link #prefix prefix}, {@link #firstname firstname}, {@link #lastname lastname} and {@link #suffix suffix}.
-        * This method overrides {@link common.IdentifiableEntity#generateTitle() generateTitle}.
-        * The result might be kept as {@link common.IdentifiableEntity#setTitleCache(String) titleCache} if the
-        * flag {@link common.IdentifiableEntity#protectedTitleCache protectedTitleCache} is not set.
-        * 
-        * @return  the string with the full name of this person
+        * Clones <i>this</i> Person. This is a shortcut that enables to create
+        * a new instance that differs only slightly from <i>this</i> Person.
+        *
+        * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
+        * @see java.lang.Object#clone()
         */
        @Override
-       public String generateTitle() {
-               String title = null;
-               if (cacheStrategy != null) {
-               title = cacheStrategy.getTitleCache(this);
-               } 
-        return title;
+       public Object clone() {
+               try{
+                       Person result = (Person)super.clone();
+                       //no changes to firstname, lastname, lifespan, prefix, suffix
+                       return result;
+               } catch (CloneNotSupportedException e){
+                       logger.warn("Object does not implement cloneable");
+                       e.printStackTrace();
+                       return null;
+               }
+
+
        }
 
 }
\ No newline at end of file