2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.model
.agent
;
12 import java
.beans
.PropertyChangeEvent
;
13 import java
.beans
.PropertyChangeListener
;
14 import java
.util
.HashSet
;
17 import javax
.persistence
.Column
;
18 import javax
.persistence
.Entity
;
19 import javax
.persistence
.FetchType
;
20 import javax
.persistence
.OneToMany
;
21 import javax
.xml
.bind
.annotation
.XmlAccessType
;
22 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
23 import javax
.xml
.bind
.annotation
.XmlElement
;
24 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
25 import javax
.xml
.bind
.annotation
.XmlRootElement
;
26 import javax
.xml
.bind
.annotation
.XmlType
;
28 import org
.apache
.log4j
.Logger
;
29 import org
.hibernate
.annotations
.Cascade
;
30 import org
.hibernate
.annotations
.CascadeType
;
31 import org
.hibernate
.annotations
.Type
;
32 import org
.hibernate
.envers
.Audited
;
33 import org
.hibernate
.search
.annotations
.Field
;
34 import org
.hibernate
.search
.annotations
.FieldBridge
;
35 import org
.hibernate
.search
.annotations
.Index
;
36 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
37 import org
.springframework
.beans
.factory
.annotation
.Configurable
;
39 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
40 import eu
.etaxonomy
.cdm
.hibernate
.search
.OrcidBridge
;
41 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
42 import eu
.etaxonomy
.cdm
.strategy
.cache
.agent
.PersonDefaultCacheStrategy
;
43 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
44 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
45 import eu
.etaxonomy
.cdm
.validation
.annotation
.NullOrNotEmpty
;
46 import javassist
.compiler
.ast
.Keyword
;
49 * This class represents human beings, living or dead.<BR>
50 * It includes name parts, {@link Contact contact} details, {@link InstitutionalMembership institutional membership},
51 * and other possible information such as life {@link TimePeriod time period},
52 * taxonomic and/or geographical {@link Keyword specialization}.
53 * For a short abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
55 * For other alternative (string-)names {@link eu.etaxonomy.cdm.model.reference.OriginalSourceBase OriginalSource} instances must be created
56 * and the attribute {@link eu.etaxonomy.cdm.model.common.OriginalSourceBase#getOriginalNameString() originalNameString} must be used.
58 * This class corresponds to: <ul>
59 * <li> Person according to the TDWG ontology
60 * <li> AgentName (partially) according to the TCS
61 * <li> Person (PersonName partially) according to the ABCD schema
65 * @since 08-Nov-2007 13:06:42
67 @XmlAccessorType(XmlAccessType
.FIELD
)
68 @XmlType(name
= "Person", propOrder
= {
78 "institutionalMemberships"
80 @XmlRootElement(name
= "Person")
82 //@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
83 //@Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
86 public class Person
extends TeamOrPersonBase
<Person
>{
88 private static final long serialVersionUID
= 4153566493065539763L;
89 public static final Logger logger
= Logger
.getLogger(Person
.class);
91 @XmlElement(name
="NomenclaturalTitle")
92 @Field(index
=Index
.YES
)
95 protected String nomenclaturalTitle
;
97 @XmlElement(name
="CollectorTitle")
98 @Field(index
=Index
.YES
)
101 private String collectorTitle
;
103 @XmlElement(name
= "Prefix")
108 private String prefix
;
110 @XmlElement(name
= "GivenName")
115 private String givenName
;
117 @XmlElement(name
= "Initials")
121 private String initials
;
123 @XmlElement(name
= "FamilyName")
128 private String familyName
;
130 @XmlElement(name
= "Suffix")
135 private String suffix
;
137 @XmlElement(name
= "Lifespan")
139 @Match(value
=MatchMode
.EQUAL_OR_ONE_NULL
)
140 //TODO Val #3379 check carefully what the condition is that lifespan is really null in legacy data
142 private TimePeriod lifespan
= TimePeriod
.NewInstance();
144 @XmlElement(name
= "Orcid")
146 @FieldBridge(impl
= OrcidBridge
.class)
147 @Type(type
="orcidUserType")
151 @XmlElementWrapper(name
= "InstitutionalMemberships", nillable
= true)
152 @XmlElement(name
= "InstitutionalMembership")
153 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "person")
154 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
155 protected Set
<InstitutionalMembership
> institutionalMemberships
;
157 // *********************** FACTORY **********************************/
160 * Creates a new empty instance for a person whose existence is all what is known.
161 * This can be a provisional solution until more information about <i>this</i> person
162 * can be gathered, for instance in case a member of a nomenclatural author team
163 * is not explicitly mentioned. It also includes the cache strategy defined in
164 * {@link eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
166 public static Person
NewInstance(){
171 * Creates a new instance for a person for whom an "identification" string
172 * is all what is known. This string is generally a short or a complete name.
173 * As this string is kept in the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache}
174 * attribute and should not be overwritten by the {@link #generateTitle() generateTitle} method
175 * the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#isProtectedTitleCache() protectedTitleCache} flag will be turned on.
177 public static Person
NewTitledInstance(String titleCache
){
178 Person result
= new Person();
179 result
.setTitleCache(titleCache
, true);
183 public static Person
NewInstance(String nomRefTitle
, String familyName
, String initials
, String givenName
){
184 Person result
= new Person();
185 result
.setNomenclaturalTitle(nomRefTitle
);
186 result
.setFamilyName(familyName
);
187 result
.setInitials(initials
);
188 result
.setGivenName(givenName
);
192 // *********************** CONSTRUCTOR **********************************/
197 * @see #Person(String, String, String)
204 * Class constructor using a "forenames" string (including initials),
205 * a surname (family name) and an abbreviated name as used in nomenclature.
206 * For the abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
209 * @param givenname the given name
210 * @param familyname the hereditary name
211 * @param nomenclaturalTitel the abbreviated name
213 * @see #NewInstance()
215 public Person(String givenname
, String familyname
, String nomenclaturalTitel
) {
216 this.setGivenName(givenname
);
217 this.setFamilyName(familyname
);
218 logger
.debug("before - Set nomenclatural Title");
219 this.setNomenclaturalTitle(nomenclaturalTitel
);
220 logger
.debug("after - Set nomenclatural Title");
224 protected void initDefaultCacheStrategy() {
225 this.cacheStrategy
= PersonDefaultCacheStrategy
.NewInstance();
229 public void initListener(){
230 PropertyChangeListener listener
= new PropertyChangeListener() {
232 public void propertyChange(PropertyChangeEvent ev
) {
233 if (!ev
.getPropertyName().equals("nomenclaturalTitleCache") //not sure if called at all
234 && !ev
.getPropertyName().equals("collectorTitleCache") //not sure if called at all
235 && !ev
.getPropertyName().equals("cacheStrategy")){
236 if (!ev
.getPropertyName().equals("titleCache") && ! isProtectedTitleCache()){
239 nomenclaturalTitleCache
= null;
240 collectorTitleCache
= null;
244 addPropertyChangeListener(listener
);
247 // *********************** GETTER SETTER ADDER **********************************/
250 * Returns the set of {@link InstitutionalMembership institution memberships} corresponding to <i>this</i> person.
252 * @see InstitutionalMembership
254 public Set
<InstitutionalMembership
> getInstitutionalMemberships(){
255 if(institutionalMemberships
== null) {
256 this.institutionalMemberships
= new HashSet
<>();
258 return this.institutionalMemberships
;
261 protected void addInstitutionalMembership(InstitutionalMembership ims
){
262 getInstitutionalMemberships().add(ims
);
263 if (ims
.getPerson() != this){
264 logger
.warn("Institutional membership's person has to be changed for adding it to person: " + this);
265 ims
.getPerson().removeInstitutionalMembership(ims
);
271 * Adds a new {@link InstitutionalMembership membership} of <i>this</i> person in an {@link Institution institution}
272 * to the set of his institution memberships.
273 * This method also creates a new institutional membership instance.
275 * @param institution the institution <i>this</i> person belongs to
276 * @param period the time period for which <i>this</i> person has been a member of the institution
277 * @param department the string label for the department <i>this</i> person belongs to,
278 * within the institution
279 * @param role the string label for the persons's role within the department or institution
280 * @see #getInstitutionalMemberships()
281 * @see InstitutionalMembership#InstitutionalMembership(Institution, Person, TimePeriod, String, String)
283 public InstitutionalMembership
addInstitutionalMembership(Institution institution
, TimePeriod period
, String department
, String role
){
284 return new InstitutionalMembership(institution
, this, period
, department
, role
);
288 * Removes one element from the set of institutional memberships of <i>this</i> person.
289 * Institute and person attributes of the institutional membership object
292 * @param ims the institutional membership of <i>this</i> person which should be deleted
293 * @see #getInstitutionalMemberships()
295 public void removeInstitutionalMembership(InstitutionalMembership ims
){
296 ims
.setInstitute(null);
298 getInstitutionalMemberships().remove(ims
);
302 * Returns the string representing the prefix (for instance "Prof. Dr.<!-- -->")
303 * to <i>this</i> person's name.
305 public String
getPrefix(){
311 public void setPrefix(String prefix
){
312 this.prefix
= isBlank(prefix
) ?
null : prefix
;
316 * Returns the string representing the given name or forename
317 * (for instance "John") of <i>this</i> person.
318 * This is the part of his name which is not shared with other
319 * family members. <BR>
320 * Pure initials should be stored in {@link #getInitials() initials}
321 * A combination of expanded names and initials maybe stored here.
322 * <BR> In user interfaces (UI) this field should better be called
323 * "Other/given names" according to {@link https://www.w3.org/International/questions/qa-personal-names.en#fielddesign }.
325 * @see #getInitials()
326 * @see #getFamilyName()
327 * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
329 public String
getGivenName(){
330 return this.givenName
;
333 * @see #getGivenName()
335 public void setGivenName(String givenName
){
336 this.givenName
= isBlank(givenName
) ?
null : givenName
;
340 public String
getCollectorTitle() {
341 return collectorTitle
;
343 public void setCollectorTitle(String collectorTitle
) {
344 this.collectorTitle
= collectorTitle
;
347 public String
getNomenclaturalTitle() {
348 return nomenclaturalTitle
;
351 public void setNomenclaturalTitle(String nomenclaturalTitle
) {
352 this.nomenclaturalTitle
= isBlank(nomenclaturalTitle
) ?
null : nomenclaturalTitle
;
355 public void setNomenclaturalTitleCache(String nomenclaturalTitle
) {
356 this.nomenclaturalTitle
= isBlank(nomenclaturalTitle
) ?
null : nomenclaturalTitle
;
360 * Returns the initials of this person as used in bibliographic
361 * references. Usually these are the first letters of each givenname
362 * followed by "." per givenname. For East Asian names it may
363 * be the first 2 letters. Also dashes are kept.
364 * @return the initials
366 public String
getInitials(){
367 return this.initials
;
372 public void setInitials(String initials
){
373 this.initials
= isBlank(initials
) ?
null : initials
;
377 * Returns the string representing the hereditary name (surname or family name)
378 * (for instance "Smith") of <i>this</i> person.
379 * This is the part of his name which is common to (all) other
380 * members of his family, as distinct from the given name or forename.
382 * <BR> In user interfaces (UI) this field should better be called
383 * "Family name" according to {@link https://www.w3.org/International/questions/qa-personal-names.en#fielddesign }.
385 * @see #getInitials()
386 * @see #getGivenName()
387 * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
389 public String
getFamilyName(){
390 return this.familyName
;
393 * @see #getfamilyName()
395 public void setFamilyName(String familyName
){
396 this.familyName
= isBlank(familyName
) ?
null : familyName
;
400 * Returns the string representing the suffix (for instance "Junior")
401 * of <i>this</i> person's name.
403 public String
getSuffix(){
409 public void setSuffix(String suffix
){
410 this.suffix
= isBlank(suffix
) ?
null: suffix
;
415 * Returns the {@link eu.etaxonomy.cdm.model.common.TimePeriod period of time}
416 * in which <i>this</i> person was alive (life span).
417 * The general form is birth date - death date
418 * (XXXX - YYYY; XXXX - or - YYYY as appropriate),
419 * but a simple flourished date (fl. XXXX) is also possible
420 * if that is all what is known.
422 * @see eu.etaxonomy.cdm.model.common.TimePeriod
424 public TimePeriod
getLifespan(){
425 if(lifespan
== null) {
426 this.lifespan
= TimePeriod
.NewInstance();
428 return this.lifespan
;
431 * @see #getLifespan()
433 public void setLifespan(TimePeriod lifespan
){
434 this.lifespan
= lifespan
!= null? lifespan
: TimePeriod
.NewInstance();
438 * The {@link ORCID ORCiD} of this person.<BR>
439 * See https://orcid.org/ for information on ORCiD.
442 public ORCID
getOrcid() {
448 public void setOrcid(ORCID orcid
) {
453 public boolean updateCaches(){
454 boolean result
= false;
455 result
|= super.updateCaches();
456 result
|= updateNomenclaturalCache();
457 result
|= updateCollectorCache();
462 private boolean updateNomenclaturalCache() {
463 //updates the nomenclaturalTitleCache if necessary
464 String oldCache
= this.nomenclaturalTitleCache
;
465 String newCache
= getCacheStrategy().getNomenclaturalTitleCache(this);
466 if (!CdmUtils
.nullSafeEqual(oldCache
, newCache
)){
467 // this.setNomenclaturalTitleCache(null, false);
468 this.getNomenclaturalTitleCache();
474 private boolean updateCollectorCache() {
475 //updates the collectorTitleCache if necessary
476 String oldCache
= this.collectorTitleCache
;
477 String newCache
= getCacheStrategy().getCollectorTitleCache(this);
478 if (!CdmUtils
.nullSafeEqual(oldCache
, newCache
)){
479 // this.setNomenclaturalTitleCache(null, false);
480 this.getCollectorTitleCache();
486 //*********************** CLONE ********************************************************/
489 * Clones <i>this</i> Person. This is a shortcut that enables to create
490 * a new instance that differs only slightly from <i>this</i> Person.
492 * @see java.lang.Object#clone()
495 public Person
clone() {
497 Person result
= (Person
)super.clone();
498 //no changes to givenname, familyname, lifespan, prefix, suffix
500 } catch (CloneNotSupportedException e
){
501 logger
.warn("Object does not implement cloneable");