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
// *********************** CONSTRUCTOR **********************************/
193

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

    
203
	/**
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}
207
	 * is used.
208
	 *
209
	 * @param  givenname     		the given name
210
	 * @param  familyname      		the hereditary name
211
	 * @param  nomenclaturalTitel 	the abbreviated name
212
	 * @see                  		#Person()
213
	 * @see                  		#NewInstance()
214
	 */
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");
221
	}
222

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

    
228
    @Override
229
    public void initListener(){
230
        PropertyChangeListener listener = new PropertyChangeListener() {
231
            @Override
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()){
237
                        titleCache = null;
238
                    }
239
                    nomenclaturalTitleCache = null;
240
                    collectorTitleCache = null;
241
                }
242
            }
243
        };
244
        addPropertyChangeListener(listener);
245
    }
246

    
247
// *********************** GETTER SETTER ADDER **********************************/
248

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

    
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);
266
			ims.setPerson(this);
267
		}
268
	}
269

    
270
	/**
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.
274
	 *
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)
282
	 */
283
	public InstitutionalMembership addInstitutionalMembership(Institution institution, TimePeriod period, String department, String role){
284
		return new InstitutionalMembership(institution, this, period, department, role);
285
	}
286

    
287
	/**
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
290
	 * will be nullified.
291
	 *
292
	 * @param  ims  the institutional membership of <i>this</i> person which should be deleted
293
	 * @see     	#getInstitutionalMemberships()
294
	 */
295
	public void removeInstitutionalMembership(InstitutionalMembership ims){
296
		ims.setInstitute(null);
297
		ims.setPerson(null);
298
		getInstitutionalMemberships().remove(ims);
299
	}
300

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

    
315
	/**
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 }.
324
	 *
325
	 * @see #getInitials()
326
	 * @see #getFamilyName()
327
	 * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
328
	 */
329
	public String getGivenName(){
330
		return this.givenName;
331
	}
332
	/**
333
	 * @see  #getGivenName()
334
	 */
335
	public void setGivenName(String givenName){
336
		this.givenName = isBlank(givenName) ? null : givenName;
337
	}
338

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

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

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

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

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

    
409

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

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

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

    
455
         return result;
456
    }
457

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

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

    
482
//*********************** CLONE ********************************************************/
483

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