Project

General

Profile

Download (16 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.hibernate.search.OrcidBridge;
40
import eu.etaxonomy.cdm.model.common.TimePeriod;
41
import eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy;
42
import eu.etaxonomy.cdm.strategy.match.Match;
43
import eu.etaxonomy.cdm.strategy.match.MatchMode;
44
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
45
import javassist.compiler.ast.Keyword;
46

    
47
/**
48
 * This class represents human beings, living or dead.<BR>
49
 * It includes name parts, {@link Contact contact} details, {@link InstitutionalMembership institutional membership},
50
 * and other possible information such as life {@link TimePeriod time period},
51
 * taxonomic and/or geographical {@link Keyword specialization}.
52
 * For a short abbreviated name the inherited attribute {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle}
53
 * is to be used.<BR>
54
 * For other alternative (string-)names {@link eu.etaxonomy.cdm.model.reference.OriginalSourceBase OriginalSource} instances must be created
55
 * and the attribute {@link eu.etaxonomy.cdm.model.common.OriginalSourceBase#getOriginalNameString() originalNameString} must be used.
56
 * <P>
57
 * This class corresponds to: <ul>
58
 * <li> Person according to the TDWG ontology
59
 * <li> AgentName (partially) according to the TCS
60
 * <li> Person (PersonName partially) according to the ABCD schema
61
 * </ul>
62
 *
63
 * @author m.doering
64
 * @since 08-Nov-2007 13:06:42
65
 */
66
@XmlAccessorType(XmlAccessType.FIELD)
67
@XmlType(name = "Person", propOrder = {
68
	    "prefix",
69
	    "familyName",
70
	    "givenName",
71
	    "initials",
72
	    "suffix",
73
	    "nomenclaturalTitle",
74
	    "collectorTitle",
75
	    "lifespan",
76
	    "orcid",
77
	    "institutionalMemberships"
78
})
79
@XmlRootElement(name = "Person")
80
@Entity
81
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
82
//@Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
83
@Audited
84
@Configurable
85
public class Person extends TeamOrPersonBase<Person>{
86

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

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

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

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

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

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

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

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

    
136
    @XmlElement(name = "Lifespan")
137
    @IndexedEmbedded
138
    @Match(value=MatchMode.EQUAL_OR_ONE_NULL)
139
  //TODO Val #3379    check carefully what the condition is that lifespan is really null in legacy data
140
//    @NotNull
141
	private TimePeriod lifespan = TimePeriod.NewInstance();
142

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

    
150
    @XmlElementWrapper(name = "InstitutionalMemberships", nillable = true)
151
    @XmlElement(name = "InstitutionalMembership")
152
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "person")
153
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
154
	protected Set<InstitutionalMembership> institutionalMemberships;
155

    
156
// *********************** FACTORY **********************************/
157

    
158
	/**
159
	 * Creates a new empty instance for a person whose existence is all what is known.
160
	 * This can be a provisional solution until more information about <i>this</i> person
161
	 * can be gathered, for instance in case a member of a nomenclatural author team
162
	 * is not explicitly mentioned. It also includes the cache strategy defined in
163
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.PersonDefaultCacheStrategy PersonDefaultCacheStrategy}.
164
	 */
165
	public static Person NewInstance(){
166
		return new Person();
167
	}
168

    
169
	/**
170
	 * Creates a new instance for a person for whom an "identification" string
171
	 * is all what is known. This string is generally a short or a complete name.
172
	 * As this string is kept in the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache}
173
	 * attribute and should not be overwritten by the {@link #generateTitle() generateTitle} method
174
	 * the {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#isProtectedTitleCache() protectedTitleCache} flag will be turned on.
175
	 */
176
	public static Person NewTitledInstance(String titleCache){
177
		Person result = new Person();
178
		result.setTitleCache(titleCache, true);
179
		return result;
180
	}
181

    
182
	public static Person NewInstance(String nomRefTitle, String familyName, String initials, String givenName){
183
        Person result = new Person();
184
        result.setNomenclaturalTitle(nomRefTitle);
185
        result.setFamilyName(familyName);
186
        result.setInitials(initials);
187
        result.setGivenName(givenName);
188
        return result;
189
    }
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("titleCache")
234
                        && !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 (! 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
	/**
319
	 * Returns the string representing the given name or forename
320
	 * (for instance "John") of <i>this</i> person.
321
	 * This is the part of his name which is not shared with other
322
	 * family members. <BR>
323
	 * Pure initials should be stored in {@link #getInitials() initials}
324
	 * A combination of expanded names and initials maybe stored here.
325
	 * <BR> In user interfaces (UI) this field should better be called
326
	 * "Other/given names" according to {@link https://www.w3.org/International/questions/qa-personal-names.en#fielddesign }.
327
	 *
328
	 * @see #getInitials()
329
	 * @see #getFamilyName()
330
	 * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
331
	 */
332
	public String getGivenName(){
333
		return this.givenName;
334
	}
335
	/**
336
	 * @see  #getGivenName()
337
	 */
338
	public void setGivenName(String givenName){
339
		this.givenName = isBlank(givenName) ? null : givenName;
340
	}
341

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

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

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

    
375

    
376
	/**
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.
381
	 *
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 }.
384
     *
385
     * @see #getInitials()
386
     * @see #getGivenName()
387
     * @see https://www.w3.org/International/questions/qa-personal-names.en#fielddesign
388
	 */
389
	public String getFamilyName(){
390
		return this.familyName;
391
	}
392
	/**
393
	 * @see  #getfamilyName()
394
	 */
395
	public void setFamilyName(String familyName){
396
		this.familyName = isBlank(familyName) ? null : familyName;
397
	}
398

    
399

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

    
414

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

    
438

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

    
454
    @Override
455
    public boolean updateCaches(){
456
        boolean result = false;
457
        result |= super.updateCaches();
458
        if (this.nomenclaturalTitle == null){
459
            this.nomenclaturalTitle = this.getTitleCache();
460
            if ( this.nomenclaturalTitle != null ){
461
                 result = true;
462
            }
463
         }
464

    
465
         return result;
466
     }
467

    
468
//*********************** CLONE ********************************************************/
469

    
470
	/**
471
	 * Clones <i>this</i> Person. This is a shortcut that enables to create
472
	 * a new instance that differs only slightly from <i>this</i> Person.
473
	 *
474
	 * @see java.lang.Object#clone()
475
	 */
476
	@Override
477
	public Person clone() {
478
		try{
479
			Person result = (Person)super.clone();
480
			//no changes to givenname, familyname, lifespan, prefix, suffix
481
			return result;
482
		} catch (CloneNotSupportedException e){
483
			logger.warn("Object does not implement cloneable");
484
			e.printStackTrace();
485
			return null;
486
		}
487
	}
488
}
(8-8/12)