Project

General

Profile

Download (17.1 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
package eu.etaxonomy.cdm.model.agent;
10

    
11
import java.beans.PropertyChangeEvent;
12
import java.beans.PropertyChangeListener;
13
import java.util.ArrayList;
14
import java.util.List;
15

    
16
import javax.persistence.Entity;
17
import javax.persistence.FetchType;
18
import javax.persistence.ManyToMany;
19
import javax.persistence.OrderColumn;
20
import javax.persistence.Transient;
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.XmlIDREF;
26
import javax.xml.bind.annotation.XmlRootElement;
27
import javax.xml.bind.annotation.XmlSchemaType;
28
import javax.xml.bind.annotation.XmlType;
29

    
30
import org.apache.logging.log4j.LogManager;
31
import org.apache.logging.log4j.Logger;
32
import org.hibernate.annotations.Cascade;
33
import org.hibernate.annotations.CascadeType;
34
import org.hibernate.annotations.ListIndexBase;
35
import org.hibernate.envers.Audited;
36
import org.springframework.beans.factory.annotation.Configurable;
37

    
38
import eu.etaxonomy.cdm.common.CdmUtils;
39
import eu.etaxonomy.cdm.model.EntityCollectionSetterAdapter;
40
import eu.etaxonomy.cdm.model.EntityCollectionSetterAdapter.SetterAdapterException;
41
import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
42
import eu.etaxonomy.cdm.strategy.match.Match;
43
import eu.etaxonomy.cdm.strategy.match.MatchMode;
44

    
45
/**
46
 * This class represents teams of {@link Person persons}. A team exists either for itself
47
 * or is built with the list of (distinct) persons who belong to it.
48
 * In the first case the inherited attribute {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} is to be used.
49
 * In the second case at least all abbreviated names
50
 * (the inherited attributes {@link TeamOrPersonBase#getNomenclaturalTitle() nomenclaturalTitle})
51
 * or all full names (the strings returned by Person.generateTitle)
52
 * of the persons must exist. A team is a {@link java.util.List list} of persons.
53
 * <P>
54
 * This class corresponds to: <ul>
55
 * <li> Team according to the TDWG ontology
56
 * <li> AgentNames (partially) according to the TCS
57
 * <li> MicroAgent (partially) according to the ABCD schema
58
 * </ul>
59
 *
60
 * @author m.doering
61
 * @since 08-Nov-2007 13:06:58
62
 */
63
@XmlAccessorType(XmlAccessType.FIELD)
64
@XmlType(name = "Team", propOrder = {
65
	"protectedNomenclaturalTitleCache",
66
	"protectedCollectorTitleCache",
67
    "teamMembers",
68
    "hasMoreMembers"
69
})
70
@XmlRootElement(name = "Team")
71
@Entity
72
@Audited
73
@Configurable
74
public class Team extends TeamOrPersonBase<Team> {
75

    
76
    private static final long serialVersionUID = 97640416905934622L;
77
	public static final Logger logger = LogManager.getLogger(Team.class);
78

    
79
    @XmlElement(name = "ProtectedNomenclaturalTitleCache")
80
	private boolean protectedNomenclaturalTitleCache = false;
81

    
82
    @XmlElement(name = "ProtectedCollectorTitleCache")
83
	private boolean protectedCollectorTitleCache = false;
84

    
85
	//An abbreviated name for the team (e. g. in case of nomenclatural authorteams).
86
    //A non abbreviated name for the team (e. g.
87
	//in case of some bibliographical references)
88
    @XmlElementWrapper(name = "TeamMembers", nillable = true)
89
    @XmlElement(name = "TeamMember")
90
    @XmlIDREF
91
    @XmlSchemaType(name = "IDREF")
92
    @OrderColumn(name="sortIndex")
93
    @ListIndexBase(value=0)  //not really needed as this is the default
94
	@ManyToMany(fetch = FetchType.LAZY)
95
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
96
	@Match(MatchMode.MATCH)
97
	private List<Person> teamMembers;
98

    
99
    @XmlElement(name = "hasMoreMembers")
100
	private boolean hasMoreMembers;
101

    
102
// ********************************** FACTORY ***************************/
103

    
104
	/**
105
	 * Creates a new team instance without any concrete {@link Person members}.
106
	 */
107
	static public Team NewInstance(){
108
		return new Team();
109
	}
110

    
111
	/**
112
	 * Creates a new team instance with a bibliographic and nomenclatural title
113
	 * but without any {@link Person members}. The caches are set to protected.
114
	 */
115
	static public Team NewTitledInstance(String title, String nomTitle){
116
		Team result = new Team();
117
		if (isNotBlank(title)){
118
		    result.setTitleCache(title, true);
119
		}
120
		if (isNotBlank(nomTitle)){
121
		    result.setNomenclaturalTitleCache(nomTitle, true);
122
		}
123
		return result;
124
	}
125

    
126
	/**
127
     * Creates a new team instance with a bibliographic, nomenclatural and collector title
128
     * but without any {@link Person members}. The caches are set to protected if not blank.
129
     */
130
    static public Team NewTitledInstance(String title, String nomTitle, String collectorTitle){
131
        Team result = new Team();
132
        if (isNotBlank(title)){
133
            result.setTitleCache(title, true);
134
        }
135
        if (isNotBlank(nomTitle)){
136
            result.setNomenclaturalTitleCache(nomTitle, true);
137
        }
138
        if (isNotBlank(collectorTitle)){
139
            result.setCollectorTitleCache(collectorTitle, true);
140
        }
141
        return result;
142
    }
143

    
144
    public static Team NewInstance(Person... members) {
145
        Team team = new Team();
146
        for (Person member : members){
147
            team.addTeamMember(member);
148
        }
149
        return team;
150
    }
151

    
152
//************************ CONSTRUCTOR *******************************/
153

    
154
	/**
155
	 * Class constructor (including the cache strategy defined in
156
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}).
157
	 */
158
	public Team() {
159
		addListenersToMembers();
160
	}
161

    
162
// ******************************************************************/
163

    
164
    @Override
165
    protected void initDefaultCacheStrategy() {
166
        this.cacheStrategy = TeamDefaultCacheStrategy.NewInstance();
167
    }
168

    
169
    @Override
170
    public void initListener(){
171
        PropertyChangeListener listener = new PropertyChangeListener() {
172
            @Override
173
            public void propertyChange(PropertyChangeEvent ev) {
174
                if (!ev.getPropertyName().equals("titleCache") &&
175
                        !ev.getPropertyName().equals("collectorTitleCache") &&
176
                        !ev.getPropertyName().equals("nomenclaturalTitleCache") &&
177
                        !ev.getPropertyName().equals("cacheStrategy")){
178
                    resetCaches();
179
                }
180
            }
181
        };
182
        addPropertyChangeListener(listener);
183
    }
184

    
185
	/**
186
	 * Adds a property change listener to all team members.
187
	 */
188
	private void addListenersToMembers() {
189
		List<Person> members = getTeamMembers();
190
		for (Person member : members){
191
			addListenerForTeamMember(member);
192
		}
193
	}
194

    
195
	private void addListenerForTeamMember(Person member) {
196
		PropertyChangeListener listener = new PropertyChangeListener() {
197
			@Override
198
            public void propertyChange(PropertyChangeEvent e) {
199
				resetCaches();
200
			}
201
		};
202
		member.addPropertyChangeListener(listener);
203
	}
204

    
205
    public void resetCaches() {
206
        if(!protectedTitleCache){
207
            titleCache = null;
208
        }
209
        if (! protectedNomenclaturalTitleCache){
210
            nomenclaturalTitleCache = null;
211
        }
212
        if (!protectedCollectorTitleCache){
213
            collectorTitleCache = null;
214
        }
215
    }
216

    
217
	/**
218
	 * Returns the list of {@link Person members} belonging to <i>this</i> team.
219
	 * A person may be a member of several distinct teams.
220
	 */
221
	public List<Person> getTeamMembers(){
222
		if(teamMembers == null) {
223
			this.teamMembers = new ArrayList<>();
224
		}
225
		return this.teamMembers;
226
	}
227

    
228
	public void setTeamMembers(List<Person> teamMembers) throws SetterAdapterException {
229
	    new EntityCollectionSetterAdapter<Team, Person>(Team.class, Person.class, "teamMembers").setCollection(this, teamMembers);
230
    }
231

    
232
	/**
233
	 * Adds a new {@link Person person} to <i>this</i> team at the end of the members' list.
234
	 *
235
	 * @param  person  the person who should be added to the other team members
236
	 * @see     	   #getTeamMembers()
237
	 * @see 		   Person
238
	 */
239
	public void addTeamMember(Person person){
240
		if (person != null){
241
			getTeamMembers().add(person);
242
			firePropertyChange("teamMember", null, person);
243
			addListenerForTeamMember(person);
244
		}
245
	}
246

    
247
	/**
248
	 * Adds a new {@link Person person} to <i>this</i> team
249
	 * at the given index place of the members' list. If the person is already
250
	 * a member of the list he will be moved to the given index place.
251
	 * The index must be an integer (>=0). If the index is larger than
252
	 * the present number of members the person will be added at the end of the list.
253
	 *
254
	 * @param  person  the person who should be added to the other team members
255
	 * @param  index   the position at which the person should be placed within the members' list (starting with 0)
256
	 * @see     	   #getTeamMembers()
257
	 * @see 		   Person
258
	 */
259
	public void addTeamMember(Person person, int index){
260
		if (person != null){
261
			int oldIndex = getTeamMembers().indexOf(person);
262
			if (oldIndex != -1 ){
263
				getTeamMembers().remove(person);
264
			}
265
			if (index >= getTeamMembers().size()){
266
				index = getTeamMembers().size();
267
			}
268
			getTeamMembers().add(index, person);
269
			addListenerForTeamMember(person);
270
			firePropertyChange("teamMember", null, person);
271
		}
272
	}
273

    
274
	/**
275
	 * Removes one person from the list of members of <i>this</i> team.
276
	 *
277
	 * @param  person  the person who should be deleted from <i>this</i> team
278
	 * @see    #getTeamMembers()
279
	 */
280
	public void removeTeamMember(Person person){
281
		boolean wasMember = getTeamMembers().remove(person);
282
		if (wasMember){
283
			firePropertyChange("teamMember", person, null);
284
		}
285
	}
286

    
287
    public boolean replaceTeamMember(Person newObject, Person oldObject){
288
        return replaceInList(this.teamMembers, newObject, oldObject);
289
    }
290

    
291
	/**
292
	 * Generates or returns the {@link TeamOrPersonBase#getnomenclaturalTitle() nomenclatural identification} string for <i>this</i> team.
293
	 * This method overrides {@link TeamOrPersonBase#getNomenclaturalTitle() getNomenclaturalTitle}.
294
	 * This string is built with the {@link TeamOrPersonBase#getNomenclaturalTitle() abbreviated names}
295
	 * of all persons belonging to its (ordered) members' list if the flag
296
	 * {@link #protectedNomenclaturalTitleCache protectedNomenclaturalTitleCache} is not set.
297
	 * Otherwise this method returns the present nomenclatural abbreviation.
298
	 * In case the string is generated the cache strategy used is defined in
299
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}.
300
	 * The result might be kept as nomenclatural abbreviation
301
	 * by using the {@link #setNomenclaturalTitle(String) setNomenclaturalTitle} method.
302
	 *
303
	 * @return  a string which identifies <i>this</i> team for nomenclature
304
	 */
305
	@Override
306
	@Transient
307
	public String getNomenclaturalTitleCache() {
308
		if (protectedNomenclaturalTitleCache == PROTECTED){
309
			return this.nomenclaturalTitleCache;
310
		}
311
		if (nomenclaturalTitleCache == null){
312
			this.nomenclaturalTitleCache = cacheStrategy().getNomenclaturalTitleCache(this);
313
		}else{
314
			//as long as team members do not inform the team about changes the cache must be created new each time
315
		    nomenclaturalTitleCache = cacheStrategy().getNomenclaturalTitleCache(this);
316
		}
317
		return nomenclaturalTitleCache;
318
	}
319

    
320
	/**
321
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
322
	 * and a protection flag status to this string.
323
	 *
324
	 * @see  #getNomenclaturalTitleCache()
325
	 */
326
	@Override
327
    public void setNomenclaturalTitleCache(String nomenclaturalTitleCache, boolean protectedNomenclaturalTitleCache) {
328
		firePropertyChange("nomenclaturalTitleCache", this.nomenclaturalTitleCache, nomenclaturalTitleCache);
329
		this.nomenclaturalTitleCache = CdmUtils.Nb(nomenclaturalTitleCache);
330
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
331
	}
332

    
333
	@Override
334
	//@Transient //TODO a.kohlbecker remove??
335
	public String getTitleCache() {
336
		String result = "";
337
		if (isProtectedTitleCache()){
338
			result = this.titleCache;
339
		}else{
340
			result = generateTitle();
341
			result = replaceEmptyTitleByNomTitle(result);
342
			result = getTruncatedCache(result);
343
			this.titleCache = result;
344
		}
345
		return result;
346
	}
347

    
348
    //#4311
349
    @Override
350
    public String getCollectorTitleCache(){
351
        if (protectedCollectorTitleCache == PROTECTED){
352
            return this.collectorTitleCache;
353
        }
354
        if (collectorTitleCache == null){
355
            this.collectorTitleCache = cacheStrategy().getCollectorTitleCache(this);
356
        }else{
357
            try {
358

    
359
                //as long as team members do not inform the team about changes the cache must be created new each time
360
                collectorTitleCache = cacheStrategy().getCollectorTitleCache(this);
361
            } catch (Exception e) {
362
                //see #10090, getCollectorTitleCache is called e.g. if team is a secundum author, in this case if members are not initialized by BeanInitializer, however, jsonlib later tries to initialize the team (the exception is swallowed so it is not critical but a stacktrace is logged, which is not necessary)
363
                //see also comment on TitleAndNameCacheAutoInitializer.initialize() for TaxonBase
364
                if (logger.isDebugEnabled()){logger.debug("LIE during getCollectorTitleCache");}
365
                return collectorTitleCache;
366
            }
367
        }
368
        return collectorTitleCache;
369
    }
370

    
371
	/**
372
	 * Protected nomenclatural title cache flag should be set to true, if
373
	 * the title cache is to be preferred against the atomized data.
374
	 * This may be the case if no atomized data exists or if atomization
375
	 * was incomplete for whatever reason.
376
	 * @return
377
	 */
378
	public boolean isProtectedNomenclaturalTitleCache() {
379
		return protectedNomenclaturalTitleCache;
380
	}
381
	public void setProtectedNomenclaturalTitleCache(boolean protectedNomenclaturalTitleCache) {
382
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
383
	}
384

    
385

    
386
	public boolean isProtectedCollectorTitleCache() {
387
        return protectedCollectorTitleCache;
388
    }
389
    public void setProtectedCollectorTitleCache(boolean protectedCollectorTitleCache) {
390
        this.protectedCollectorTitleCache = protectedCollectorTitleCache;
391
    }
392

    
393
    public void setCollectorTitleCache(String collectorTitleCache, boolean protectedCache) {
394
        this.collectorTitleCache = CdmUtils.Nb(collectorTitleCache);
395
        this.protectedCollectorTitleCache = protectedCache;
396
    }
397

    
398
    /**
399
	 * The hasMoreMembers flag is true if this team has more members than
400
	 * mentioned in the {@link #teamMembers} list. This is usually the case
401
	 * when "et al." is used in the team representation. Formatters should add
402
	 * "et al." or an according post fix to the team string representation
403
	 *  if this flag is set.
404
	 * @return
405
	 */
406
	public boolean isHasMoreMembers() {
407
		return hasMoreMembers;
408
	}
409
	public void setHasMoreMembers(boolean hasMoreMembers) {
410
		this.hasMoreMembers = hasMoreMembers;
411
	}
412

    
413
    @Override
414
    public boolean updateCaches(){
415
        boolean result = super.updateCaches();
416
        if (this.protectedNomenclaturalTitleCache == false){
417
            String oldNomTitleCache = this.nomenclaturalTitleCache;
418

    
419
            String newNomTitleCache = cacheStrategy().getNomenclaturalTitleCache(this);
420

    
421
            if ( oldNomTitleCache == null   || ! oldNomTitleCache.equals(newNomTitleCache) ){
422
                this.setNomenclaturalTitleCache(null, false);
423
                String newCache = this.getNomenclaturalTitleCache();
424

    
425
                if (newCache == null){
426
                    logger.warn("New nomTitleCache should never be null");
427
                }
428
                if (oldNomTitleCache == null){
429
                    logger.info("Old nomTitleCache should never be null");
430
                }
431
                result = true;
432
            }
433
        }
434
        if (this.protectedCollectorTitleCache == false){
435
            String oldCollTitleCache = this.collectorTitleCache;
436
            String newCollTitleCache = cacheStrategy().getCollectorTitleCache(this);
437

    
438
            if ( oldCollTitleCache == null || ! oldCollTitleCache.equals(newCollTitleCache) ){
439
                 this.setCollectorTitleCache(null, false);
440
                 String newCache = this.getCollectorTitleCache();
441

    
442
                 if (newCache == null){
443
                     logger.warn("New collectorTitleCache should never be null");
444
                 }
445
                 if (oldCollTitleCache == null){
446
                     logger.info("Old collectorTitleCache should never be null");
447
                 }
448
                 result = true;
449
             }
450
         }
451
        return result;
452
     }
453

    
454
//*********************** CLONE ********************************************************/
455

    
456
    /**
457
	 * Clones <i>this</i> Team. This is a shortcut that enables to create
458
	 * a new instance that differs only slightly from <i>this</i> Team.
459
	 * The corresponding person is cloned.
460
	 *
461
	 * @see java.lang.Object#clone()
462
	 */
463
	@Override
464
	public Team clone() {
465
		try{
466
			Team result = (Team)super.clone();
467
			result.teamMembers = new ArrayList<>();
468
			for (Person teamMember: this.teamMembers){
469
				result.addTeamMember(teamMember);
470
			}
471
			//no changes to protectedNomenclaturalTitleCache
472
			return result;
473
		} catch (CloneNotSupportedException e){
474
			logger.warn("Object does not implement cloneable");
475
			e.printStackTrace();
476
			return null;
477
		}
478
	}
479
}
(9-9/12)