Merge branch 'develop' into feature/cdm-4.7
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / agent / Person.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.cdm.model.agent;
11
12 import java.util.HashSet;
13 import java.util.Set;
14
15 import javax.persistence.Column;
16 import javax.persistence.Entity;
17 import javax.persistence.FetchType;
18 import javax.persistence.OneToMany;
19 import javax.xml.bind.annotation.XmlAccessType;
20 import javax.xml.bind.annotation.XmlAccessorType;
21 import javax.xml.bind.annotation.XmlElement;
22 import javax.xml.bind.annotation.XmlElementWrapper;
23 import javax.xml.bind.annotation.XmlRootElement;
24 import javax.xml.bind.annotation.XmlType;
25
26 import org.apache.log4j.Logger;
27 import org.hibernate.annotations.Cascade;
28 import org.hibernate.annotations.CascadeType;
29 import org.hibernate.envers.Audited;
30 import org.hibernate.search.annotations.Field;
31 import org.hibernate.search.annotations.IndexedEmbedded;
32 import org.joda.time.Partial;
33 import org.springframework.beans.factory.annotation.Configurable;
34
35 import eu.etaxonomy.cdm.model.common.TimePeriod;
36 import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
37 import eu.etaxonomy.cdm.strategy.match.Match;
38 import eu.etaxonomy.cdm.strategy.match.MatchMode;
39 import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
40 import javassist.compiler.ast.Keyword;
41
42 /**
43 * This class represents human beings, living or dead.<BR>
44 * It includes name parts, {@link Contact contact} details, {@link InstitutionalMembership institutional membership},
45 * and other possible information such as life {@link TimePeriod time period},
46 * taxonomic and/or geographical {@link Keyword specialization}.
47 * For a short abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
48 * is to be used.<BR>
49 * For other alternative (string-)names {@link eu.etaxonomy.cdm.model.common.OriginalSourceBase OriginalSource} instances must be created
50 * and the inherited attribute {@link eu.etaxonomy.cdm.model.common.ReferencedEntityBase#getOriginalNameString() originalNameString} must be used.
51 * <P>
52 * This class corresponds to: <ul>
53 * <li> Person according to the TDWG ontology
54 * <li> AgentName (partially) according to the TCS
55 * <li> Person (PersonName partially) according to the ABCD schema
56 * </ul>
57 *
58 * @author m.doering
59 * @created 08-Nov-2007 13:06:42
60 */
61 @XmlAccessorType(XmlAccessType.FIELD)
62 @XmlType(name = "Person", propOrder = {
63 "prefix",
64 "firstname",
65 "initials",
66 "lastname",
67 "suffix",
68 "lifespan",
69 "institutionalMemberships"
70 })
71 @XmlRootElement(name = "Person")
72 @Entity
73 //@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
74 //@Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
75 @Audited
76 @Configurable
77 public class Person extends TeamOrPersonBase<Person>{
78 private static final long serialVersionUID = 4153566493065539763L;
79 public static final Logger logger = Logger.getLogger(Person.class);
80
81 @XmlElement(name = "Prefix")
82 @Field
83 //TODO Val #3379
84 // @NullOrNotEmpty
85 @Column(length=255)
86 private String prefix;
87
88 @XmlElement(name = "FirstName")
89 @Field
90 //TODO Val #3379
91 // @NullOrNotEmpty
92 @Column(length=255)
93 private String firstname;
94
95 @XmlElement(name = "FirstName")
96 @Field
97 @NullOrNotEmpty
98 @Column(length=50)
99 private String initials;
100
101 @XmlElement(name = "LastName")
102 @Field
103 //TODO Val #3379
104 // @NullOrNotEmpty
105 @Column(length=255)
106 private String lastname;
107
108 @XmlElement(name = "Suffix")
109 @Field
110 //TODO Val #3379
111 // @NullOrNotEmpty
112 @Column(length=255)
113 private String suffix;
114
115 @XmlElement(name = "Lifespan")
116 @IndexedEmbedded
117 @Match(value=MatchMode.EQUAL_OR_ONE_NULL)
118 //TODO Val #3379 check carefully what the condition is that lifespan is really null in legacy data
119 // @NotNull
120 private TimePeriod lifespan = TimePeriod.NewInstance();
121
122 @XmlElementWrapper(name = "InstitutionalMemberships", nillable = true)
123 @XmlElement(name = "InstitutionalMembership")
124 @OneToMany(fetch=FetchType.LAZY, mappedBy = "person")
125 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
126 protected Set<InstitutionalMembership> institutionalMemberships;
127
128 // *********************** FACTORY **********************************/
129
130 /**
131 * Creates a new empty instance for a person whose existence is all what is known.
132 * This can be a provisional solution until more information about <i>this</i> person
133 * can be gathered, for instance in case a member of a nomenclatural author team
134 * is not explicitly mentioned. It also includes the cache strategy defined in
135 * {@link eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
136 */
137 public static Person NewInstance(){
138 return new Person();
139 }
140
141 /**
142 * Creates a new instance for a person for whom an "identification" string
143 * is all what is known. This string is generally a short or a complete name.
144 * As this string is kept in the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache}
145 * attribute and should not be overwritten by the {@link #generateTitle() generateTitle} method
146 * the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#isProtectedTitleCache() protectedTitleCache} flag will be turned on.
147 */
148 public static Person NewTitledInstance(String titleCache){
149 Person result = new Person();
150 result.setTitleCache(titleCache, true);
151 return result;
152 }
153
154 // *********************** CONSTRUCTOR **********************************/
155
156 /**
157 * Class constructor.
158 *
159 * @see #Person(String, String, String)
160 */
161 protected Person() {
162 super();
163 this.cacheStrategy = PersonDefaultCacheStrategy.NewInstance();
164
165 }
166
167 /**
168 * Class constructor using a "forenames" string (including initials),
169 * a surname (family name) and an abbreviated name as used in nomenclature.
170 * For the abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
171 * is used.
172 *
173 * @param firstname the given name
174 * @param lastname the hereditary name
175 * @param nomenclaturalTitel the abbreviated name
176 * @see #Person()
177 * @see #NewInstance()
178 */
179 public Person(String firstname, String lastname, String nomenclaturalTitel) {
180 this.setFirstname(firstname);
181 this.setLastname(lastname);
182 logger.debug("before - Set nomenclatural Title");
183 this.setNomenclaturalTitle(nomenclaturalTitel);
184 logger.debug("after - Set nomenclatural Title");
185 }
186
187 // *********************** GETTER SETTER ADDER **********************************/
188
189 /**
190 * Returns the set of {@link InstitutionalMembership institution memberships} corresponding to <i>this</i> person.
191 *
192 * @see InstitutionalMembership
193 */
194 public Set<InstitutionalMembership> getInstitutionalMemberships(){
195 if(institutionalMemberships == null) {
196 this.institutionalMemberships = new HashSet<InstitutionalMembership>();
197 }
198 return this.institutionalMemberships;
199 }
200
201 protected void addInstitutionalMembership(InstitutionalMembership ims){
202 getInstitutionalMemberships().add(ims);
203 if (ims.getPerson() != this){
204 logger.warn("Institutional membership's person has to be changed for adding it to person: " + this);
205 ims.getPerson().removeInstitutionalMembership(ims);
206 ims.setPerson(this);
207
208 }
209 }
210
211 /**
212 * Adds a new {@link InstitutionalMembership membership} of <i>this</i> person in an {@link Institution institution}
213 * to the set of his institution memberships.
214 * This method also creates a new institutional membership instance.
215 *
216 * @param institution the institution <i>this</i> person belongs to
217 * @param period the time period for which <i>this</i> person has been a member of the institution
218 * @param department the string label for the department <i>this</i> person belongs to,
219 * within the institution
220 * @param role the string label for the persons's role within the department or institution
221 * @see #getInstitutionalMemberships()
222 * @see InstitutionalMembership#InstitutionalMembership(Institution, Person, TimePeriod, String, String)
223 */
224 public InstitutionalMembership addInstitutionalMembership(Institution institution, TimePeriod period, String department, String role){
225 return new InstitutionalMembership(institution, this, period, department, role);
226 }
227
228 /**
229 * Removes one element from the set of institutional memberships of <i>this</i> person.
230 * Institute and person attributes of the institutional membership object
231 * will be nullified.
232 *
233 * @param ims the institutional membership of <i>this</i> person which should be deleted
234 * @see #getInstitutionalMemberships()
235 */
236 public void removeInstitutionalMembership(InstitutionalMembership ims){
237 ims.setInstitute(null);
238 ims.setPerson(null);
239 getInstitutionalMemberships().remove(ims);
240 }
241
242 /**
243 * Returns the string representing the prefix (for instance "Prof.&nbsp;Dr.<!-- -->")
244 * to <i>this</i> person's name.
245 */
246 public String getPrefix(){
247 return this.prefix;
248 }
249 /**
250 * @see #getPrefix()
251 */
252 public void setPrefix(String prefix){
253 this.prefix = isBlank(prefix) ? null : prefix;
254 }
255
256
257 /**
258 * Returns the string representing the given name or forename
259 * (for instance "John") of <i>this</i> person.
260 * This is the part of his name which is not shared with other
261 * family members. Actually it may be just initials (for instance "G.&nbsp;Jr."),
262 * all forenames in full or a combination of expanded names and initials.
263 */
264 public String getFirstname(){
265 return this.firstname;
266 }
267 /**
268 * @see #getFirstname()
269 */
270 public void setFirstname(String firstname){
271 this.firstname = isBlank(firstname) ? null : firstname;
272 }
273
274 /**
275 * Returns the initials of this person as used in bibliographic
276 * references. Usually these are the first letters of each firstname
277 * followed by "." per firstname. For East Asian names it may
278 * be the first 2 letters. Also dashes are kept.
279 * @return the initials
280 */
281 public String getInitials(){
282 return this.initials;
283 }
284 /**
285 * @see #getInitals()
286 */
287 public void setInitials(String initials){
288 this.initials = isBlank(initials) ? null : initials;
289 }
290
291
292 /**
293 * Returns the string representing the hereditary name (surname or family name)
294 * (for instance "Smith") of <i>this</i> person.
295 * This is the part of his name which is common to (all) other
296 * members of his family, as distinct from the given name or forename.
297 */
298 public String getLastname(){
299 return this.lastname;
300 }
301 /**
302 * @see #getLastname()
303 */
304 public void setLastname(String lastname){
305 this.lastname = isBlank(lastname) ? null : lastname;
306 }
307
308
309 /**
310 * Returns the string representing the suffix (for instance "Junior")
311 * of <i>this</i> person's name.
312 */
313 public String getSuffix(){
314 return this.suffix;
315 }
316 /**
317 * @see #getSuffix()
318 */
319 public void setSuffix(String suffix){
320 this.suffix = isBlank(suffix) ? null: suffix;
321 }
322
323
324 /**
325 * Returns the {@link eu.etaxonomy.cdm.model.common.TimePeriod period of time}
326 * in which <i>this</i> person was alive (life span).
327 * The general form is birth date - death date
328 * (XXXX - YYYY; XXXX - or - YYYY as appropriate),
329 * but a simple flourished date (fl. XXXX) is also possible
330 * if that is all what is known.
331 *
332 * @see eu.etaxonomy.cdm.model.common.TimePeriod
333 */
334 public TimePeriod getLifespan(){
335 if(lifespan == null) {
336 this.lifespan = TimePeriod.NewInstance(new Partial(), new Partial());
337 }
338 return this.lifespan;
339 }
340 /**
341 * @see #getLifespan()
342 */
343 public void setLifespan(TimePeriod lifespan){
344 if (lifespan == null){
345 this.lifespan = TimePeriod.NewInstance(new Partial(), new Partial());
346 }
347 this.lifespan = lifespan;
348 }
349
350 //*********************** CLONE ********************************************************/
351
352 /**
353 * Clones <i>this</i> Person. This is a shortcut that enables to create
354 * a new instance that differs only slightly from <i>this</i> Person.
355 *
356 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
357 * @see java.lang.Object#clone()
358 */
359 @Override
360 public Object clone() {
361 try{
362 Person result = (Person)super.clone();
363 //no changes to firstname, lastname, lifespan, prefix, suffix
364 return result;
365 } catch (CloneNotSupportedException e){
366 logger.warn("Object does not implement cloneable");
367 e.printStackTrace();
368 return null;
369 }
370 }
371
372 }