Project

General

Profile

Download (16.8 KB) Statistics
| Branch: | Tag: | Revision:
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.beans.PropertyChangeEvent;
13
import java.beans.PropertyChangeListener;
14
import java.util.HashSet;
15
import java.util.Set;
16

    
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;
27

    
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;
38

    
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;
47

    
48
/**
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}
54
 * is to be used.<BR>
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.
57
 * <P>
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
62
 * </ul>
63
 *
64
 * @author m.doering
65
 * @since 08-Nov-2007 13:06:42
66
 */
67
@XmlAccessorType(XmlAccessType.FIELD)
68
@XmlType(name = "Person", propOrder = {
69
	    "prefix",
70
	    "familyName",
71
	    "givenName",
72
	    "initials",
73
	    "suffix",
74
	    "nomenclaturalTitle",
75
	    "collectorTitle",
76
	    "lifespan",
77
	    "orcid",
78
	    "institutionalMemberships"
79
})
80
@XmlRootElement(name = "Person")
81
@Entity
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")
84
@Audited
85
@Configurable
86
public class Person extends TeamOrPersonBase<Person>{
87

    
88
    private static final long serialVersionUID = 4153566493065539763L;
89
	public static final Logger logger = Logger.getLogger(Person.class);
90

    
91
    @XmlElement(name="NomenclaturalTitle")
92
    @Field(index=Index.YES)
93
    @NullOrNotEmpty
94
    @Column(length=255)
95
    protected String nomenclaturalTitle;
96

    
97
    @XmlElement(name="CollectorTitle")
98
    @Field(index=Index.YES)
99
    @NullOrNotEmpty
100
    @Column(length=255)
101
    private String collectorTitle;
102

    
103
    @XmlElement(name = "Prefix")
104
    @Field
105
  //TODO Val #3379
106
//    @NullOrNotEmpty
107
    @Column(length=255)
108
	private String prefix;
109

    
110
    @XmlElement(name = "GivenName")
111
    @Field
112
  //TODO Val #3379
113
//    @NullOrNotEmpty
114
    @Column(length=255)
115
	private String givenName;
116

    
117
    @XmlElement(name = "Initials")
118
    @Field
119
    @NullOrNotEmpty
120
    @Column(length=80)
121
    private String initials;
122

    
123
    @XmlElement(name = "FamilyName")
124
    @Field
125
  //TODO Val #3379
126
//    @NullOrNotEmpty
127
    @Column(length=255)
128
	private String familyName;
129

    
130
    @XmlElement(name = "Suffix")
131
    @Field
132
  //TODO Val #3379
133
//    @NullOrNotEmpty
134
    @Column(length=255)
135
	private String suffix;
136

    
137
    @XmlElement(name = "Lifespan")
138
    @IndexedEmbedded
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
141
//    @NotNull
142
	private TimePeriod lifespan = TimePeriod.NewInstance();
143

    
144
    @XmlElement(name = "Orcid")
145
    @Field
146
    @FieldBridge(impl = OrcidBridge.class)
147
    @Type(type="orcidUserType")
148
    @Column(length=16)
149
    private ORCID orcid;
150

    
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;
156

    
157
// *********************** FACTORY **********************************/
158

    
159
	/**
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}.
165
	 */
166
	public static Person NewInstance(){
167
		return new Person();
168
	}
169

    
170
	/**
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.
176
	 */
177
	public static Person NewTitledInstance(String titleCache){
178
		Person result = new Person();
179
		result.setTitleCache(titleCache, true);
180
		return result;
181
	}
182

    
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);
189
        return result;
190
    }
191

    
192

    
193
// *********************** CONSTRUCTOR **********************************/
194

    
195
	/**
196
	 * Class constructor.
197
	 *
198
	 * @see #Person(String, String, String)
199
	 */
200
	protected Person() {
201
		super();
202
	}
203

    
204
	/**
205
	 * Class constructor using a "forenames" string (including initials),
206
	 * a surname (family name) and an abbreviated name as used in nomenclature.
207
	 * For the abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
208
	 * is used.
209
	 *
210
	 * @param  givenname     		the given name
211
	 * @param  familyname      		the hereditary name
212
	 * @param  nomenclaturalTitel 	the abbreviated name
213
	 * @see                  		#Person()
214
	 * @see                  		#NewInstance()
215
	 */
216
	public Person(String givenname, String familyname, String nomenclaturalTitel) {
217
		this.setGivenName(givenname);
218
		this.setFamilyName(familyname);
219
		logger.debug("before - Set nomenclatural Title");
220
		this.setNomenclaturalTitle(nomenclaturalTitel);
221
		logger.debug("after - Set nomenclatural Title");
222
	}
223

    
224
    @Override
225
    protected void initDefaultCacheStrategy() {
226
        this.cacheStrategy = PersonDefaultCacheStrategy.NewInstance();
227
    }
228

    
229
    @Override
230
    public void initListener(){
231
        PropertyChangeListener listener = new PropertyChangeListener() {
232
            @Override
233
            public void propertyChange(PropertyChangeEvent ev) {
234
                if (!ev.getPropertyName().equals("nomenclaturalTitleCache") //not sure if called at all
235
                        && !ev.getPropertyName().equals("collectorTitleCache") //not sure if called at all
236
                        && !ev.getPropertyName().equals("cacheStrategy")){
237
                    if (!ev.getPropertyName().equals("titleCache") && ! isProtectedTitleCache()){
238
                        titleCache = null;
239
                    }
240
                    nomenclaturalTitleCache = null;
241
                    collectorTitleCache = null;
242
                }
243
            }
244
        };
245
        addPropertyChangeListener(listener);
246
    }
247

    
248
// *********************** GETTER SETTER ADDER **********************************/
249

    
250

    
251
    /**
252
	 * Returns the set of {@link InstitutionalMembership institution memberships} corresponding to <i>this</i> person.
253
	 *
254
	 * @see     InstitutionalMembership
255
	 */
256
	public Set<InstitutionalMembership> getInstitutionalMemberships(){
257
		if(institutionalMemberships == null) {
258
			this.institutionalMemberships = new HashSet<>();
259
		}
260
		return this.institutionalMemberships;
261
	}
262

    
263
	protected void addInstitutionalMembership(InstitutionalMembership ims){
264
		getInstitutionalMemberships().add(ims);
265
		if (ims.getPerson() != this){
266
			logger.warn("Institutional membership's person has to be changed for adding it to person: " + this);
267
			ims.getPerson().removeInstitutionalMembership(ims);
268
			ims.setPerson(this);
269
		}
270
	}
271

    
272
	/**
273
	 * Adds a new {@link InstitutionalMembership membership} of <i>this</i> person in an {@link Institution institution}
274
	 * to the set of his institution memberships.
275
	 * This method also creates a new institutional membership instance.
276
	 *
277
	 * @param  institution  the institution <i>this</i> person belongs to
278
	 * @param  period       the time period for which <i>this</i> person has been a member of the institution
279
	 * @param  department   the string label for the department <i>this</i> person belongs to,
280
	 * 					    within the institution
281
	 * @param  role         the string label for the persons's role within the department or institution
282
	 * @see 			    #getInstitutionalMemberships()
283
	 * @see 			    InstitutionalMembership#InstitutionalMembership(Institution, Person, TimePeriod, String, String)
284
	 */
285
	public InstitutionalMembership addInstitutionalMembership(Institution institution, TimePeriod period, String department, String role){
286
		return new InstitutionalMembership(institution, this, period, department, role);
287
	}
288

    
289
	/**
290
	 * Removes one element from the set of institutional memberships of <i>this</i> person.
291
	 * Institute and person attributes of the institutional membership object
292
	 * will be nullified.
293
	 *
294
	 * @param  ims  the institutional membership of <i>this</i> person which should be deleted
295
	 * @see     	#getInstitutionalMemberships()
296
	 */
297
	public void removeInstitutionalMembership(InstitutionalMembership ims){
298
		ims.setInstitute(null);
299
		ims.setPerson(null);
300
		getInstitutionalMemberships().remove(ims);
301
	}
302

    
303
	/**
304
	 * Returns the string representing the prefix (for instance "Prof.&nbsp;Dr.<!-- -->")
305
	 * to <i>this</i> person's name.
306
	 */
307
	public String getPrefix(){
308
		return this.prefix;
309
	}
310
	/**
311
	 * @see  #getPrefix()
312
	 */
313
	public void setPrefix(String prefix){
314
		this.prefix = isBlank(prefix) ? null : prefix;
315
	}
316

    
317
	/**
318
	 * Returns the string representing the given name or forename
319
	 * (for instance "John") of <i>this</i> person.
320
	 * This is the part of his name which is not shared with other
321
	 * family members. <BR>
322
	 * Pure initials should be stored in {@link #getInitials() initials}
323
	 * A combination of expanded names and initials maybe stored here.
324
	 * <BR> In user interfaces (UI) this field should better be called
325
	 * "Other/given names" according to {@link https://www.w3.org/International/questions/qa-personal-names.en#fielddesign }.
326
	 *
327
	 * @see #getInitials()
328
	 * @see #getFamilyName()
329
	 * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
330
	 */
331
	public String getGivenName(){
332
		return this.givenName;
333
	}
334
	/**
335
	 * @see  #getGivenName()
336
	 */
337
	public void setGivenName(String givenName){
338
		this.givenName = isBlank(givenName) ? null : givenName;
339
	}
340

    
341
	//#4311
342
    public String getCollectorTitle() {
343
        return collectorTitle;
344
    }
345
    public void setCollectorTitle(String collectorTitle) {
346
        this.collectorTitle = collectorTitle;
347
    }
348

    
349
    public String getNomenclaturalTitle() {
350
        return nomenclaturalTitle;
351
    }
352
    @Override
353
    public void setNomenclaturalTitle(String nomenclaturalTitle) {
354
        this.nomenclaturalTitle = isBlank(nomenclaturalTitle) ? null : nomenclaturalTitle;
355
    }
356

    
357
    /**
358
     * Returns the initials of this person as used in bibliographic
359
     * references. Usually these are the first letters of each givenname
360
     * followed by "." per givenname. For East Asian names it may
361
     * be the first 2 letters. Also dashes are kept.
362
     * @return the initials
363
     */
364
    public String getInitials(){
365
        return this.initials;
366
    }
367
    /**
368
     * @see  #getInitals()
369
     */
370
    public void setInitials(String initials){
371
        this.initials = isBlank(initials) ? null : initials;
372
    }
373

    
374
	/**
375
	 * Returns the string representing the hereditary name (surname or family name)
376
	 * (for instance "Smith") of <i>this</i> person.
377
	 * This is the part of his name which is common to (all) other
378
	 * members of his family, as distinct from the given name or forename.
379
	 *
380
     * <BR> In user interfaces (UI) this field should better be called
381
     * "Family name" according to {@link https://www.w3.org/International/questions/qa-personal-names.en#fielddesign }.
382
     *
383
     * @see #getInitials()
384
     * @see #getGivenName()
385
     * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
386
	 */
387
	public String getFamilyName(){
388
		return this.familyName;
389
	}
390
	/**
391
	 * @see  #getfamilyName()
392
	 */
393
	public void setFamilyName(String familyName){
394
		this.familyName = isBlank(familyName) ? null : familyName;
395
	}
396

    
397
	/**
398
	 * Returns the string representing the suffix (for instance "Junior")
399
	 * of <i>this</i> person's name.
400
	 */
401
	public String getSuffix(){
402
		return this.suffix;
403
	}
404
	/**
405
	 * @see  #getSuffix()
406
	 */
407
	public void setSuffix(String suffix){
408
		this.suffix = isBlank(suffix) ? null: suffix;
409
	}
410

    
411

    
412
	/**
413
	 * Returns the {@link eu.etaxonomy.cdm.model.common.TimePeriod period of time}
414
	 * in which <i>this</i> person was alive (life span).
415
	 * The general form is birth date - death date
416
	 * (XXXX - YYYY; XXXX - or - YYYY as appropriate),
417
	 * but a simple flourished date (fl. XXXX) is also possible
418
	 * if that is all what is known.
419
	 *
420
	 * @see  eu.etaxonomy.cdm.model.common.TimePeriod
421
	 */
422
	public TimePeriod getLifespan(){
423
		if(lifespan == null) {
424
			this.lifespan = TimePeriod.NewInstance();
425
		}
426
		return this.lifespan;
427
	}
428
	/**
429
	 * @see  #getLifespan()
430
	 */
431
	public void setLifespan(TimePeriod lifespan){
432
		this.lifespan = lifespan != null? lifespan : TimePeriod.NewInstance();
433
	}
434

    
435
    /**
436
     * The {@link ORCID ORCiD} of this person.<BR>
437
     * See https://orcid.org/ for information on ORCiD.
438
     * @return the ORCiD
439
     */
440
    public ORCID getOrcid() {
441
        return orcid;
442
    }
443
    /**
444
     * @see #getOrcid()
445
     */
446
    public void setOrcid(ORCID orcid) {
447
        this.orcid = orcid;
448
    }
449

    
450
    @Override
451
    public boolean updateCaches(){
452
        boolean result = false;
453
        result |= super.updateCaches();
454
        result |= updateNomenclaturalCache();
455
        result |= updateCollectorCache();
456

    
457
         return result;
458
    }
459

    
460
    private boolean updateNomenclaturalCache() {
461
        //updates the nomenclaturalTitleCache if necessary
462
        String oldCache = this.nomenclaturalTitleCache;
463
        String newCache = getCacheStrategy().getNomenclaturalTitleCache(this);
464
        if (!CdmUtils.nullSafeEqual(oldCache, newCache)){
465
//            this.setNomenclaturalTitleCache(null, false);
466
            this.getNomenclaturalTitleCache();
467
            return true;
468
        }
469
        return false;
470
    }
471

    
472
    private boolean updateCollectorCache() {
473
        //updates the collectorTitleCache if necessary
474
        String oldCache = this.collectorTitleCache;
475
        String newCache = getCacheStrategy().getCollectorTitleCache(this);
476
        if (!CdmUtils.nullSafeEqual(oldCache, newCache)){
477
//            this.setNomenclaturalTitleCache(null, false);
478
            this.getCollectorTitleCache();
479
            return true;
480
        }
481
        return false;
482
     }
483

    
484
//*********************** CLONE ********************************************************/
485

    
486
	/**
487
	 * Clones <i>this</i> Person. This is a shortcut that enables to create
488
	 * a new instance that differs only slightly from <i>this</i> Person.
489
	 *
490
	 * @see java.lang.Object#clone()
491
	 */
492
	@Override
493
	public Person clone() {
494
		try{
495
			Person result = (Person)super.clone();
496
			//no changes to givenname, familyname, lifespan, prefix, suffix
497
			return result;
498
		} catch (CloneNotSupportedException e){
499
			logger.warn("Object does not implement cloneable");
500
			e.printStackTrace();
501
			return null;
502
		}
503
	}
504
}
(8-8/12)