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