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.log4j.Logger;
31
import org.hibernate.annotations.Cascade;
32
import org.hibernate.annotations.CascadeType;
33
import org.hibernate.annotations.ListIndexBase;
34
import org.hibernate.envers.Audited;
35
import org.springframework.beans.factory.annotation.Configurable;
36

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

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

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

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

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

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

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

    
103
// ********************************** FACTORY ***************************/
104

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

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

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

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

    
153
//************************ CONSTRUCTOR *******************************/
154

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

    
163
// ******************************************************************/
164

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

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

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

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

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

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

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

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

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

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

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

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

    
321
	/**
322
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
323
	 * and protects it from overwriting.
324
	 * This method overrides {@link TeamOrPersonBase#setNomenclaturalTitle(String) setNomenclaturalTitle}.
325
	 *
326
	 * @see  #getNomenclaturalTitle()
327
	 * @see  #setNomenclaturalTitle(String, boolean)
328
	 */
329
	@Override
330
	public void setNomenclaturalTitle(String nomenclaturalTitle) {
331
		this.setNomenclaturalTitleCache(nomenclaturalTitle, PROTECTED);
332
	}
333

    
334
	/**
335
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
336
	 * and a protection flag status to this string.
337
	 *
338
	 * @see  #getNomenclaturalTitle()
339
	 */
340
	public void setNomenclaturalTitleCache(String nomenclaturalTitleCache, boolean protectedNomenclaturalTitleCache) {
341
		firePropertyChange("nomenclaturalTitleCache", this.nomenclaturalTitleCache, nomenclaturalTitleCache);
342
		this.nomenclaturalTitleCache = CdmUtils.Nb(nomenclaturalTitleCache);
343
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
344
	}
345

    
346
	@Override
347
	//@Transient //TODO a.kohlbecker remove??
348
	public String getTitleCache() {
349
		String result = "";
350
		if (isProtectedTitleCache()){
351
			result = this.titleCache;
352
		}else{
353
			result = generateTitle();
354
			result = replaceEmptyTitleByNomTitle(result);
355
			result = getTruncatedCache(result);
356
			this.titleCache = result;
357
		}
358
		return result;
359
	}
360

    
361
    //#4311
362
    @Override
363
    public String getCollectorTitleCache() {
364
        if (protectedCollectorTitleCache == PROTECTED){
365
            return this.collectorTitleCache;
366
        }
367
        if (collectorTitleCache == null){
368
            this.collectorTitleCache = getCacheStrategy().getCollectorTitleCache(this);
369
        }else{
370
            //as long as team members do not inform the team about changes the cache must be created new each time
371
            collectorTitleCache = getCacheStrategy().getCollectorTitleCache(this);
372
        }
373
        return collectorTitleCache;
374
    }
375

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

    
390

    
391
	public boolean isProtectedCollectorTitleCache() {
392
        return protectedCollectorTitleCache;
393
    }
394
    public void setProtectedCollectorTitleCache(boolean protectedCollectorTitleCache) {
395
        this.protectedCollectorTitleCache = protectedCollectorTitleCache;
396
    }
397

    
398
    public void setCollectorTitleCache(String collectorTitleCache, boolean protectedCache) {
399
        this.collectorTitleCache = CdmUtils.Nb(collectorTitleCache);
400
        this.protectedCollectorTitleCache = protectedCache;
401
    }
402

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

    
418
    @Override
419
    public boolean updateCaches(){
420
        boolean result = super.updateCaches();
421
        if (this.protectedNomenclaturalTitleCache == false){
422
            String oldNomTitleCache = this.nomenclaturalTitleCache;
423

    
424
            String newNomTitleCache = getCacheStrategy().getNomenclaturalTitleCache(this);
425

    
426
            if ( oldNomTitleCache == null   || ! oldNomTitleCache.equals(newNomTitleCache) ){
427
                this.setNomenclaturalTitleCache(null, false);
428
                String newCache = this.getNomenclaturalTitleCache();
429

    
430
                if (newCache == null){
431
                    logger.warn("New nomTitleCache should never be null");
432
                }
433
                if (oldNomTitleCache == null){
434
                    logger.info("Old nomTitleCache should never be null");
435
                }
436
                result = true;
437
            }
438
        }
439
        if (this.protectedCollectorTitleCache == false){
440
            String oldCollTitleCache = this.collectorTitleCache;
441
            String newCollTitleCache = getCacheStrategy().getCollectorTitleCache(this);
442

    
443
            if ( oldCollTitleCache == null || ! oldCollTitleCache.equals(newCollTitleCache) ){
444
                 this.setCollectorTitleCache(null, false);
445
                 String newCache = this.getCollectorTitleCache();
446

    
447
                 if (newCache == null){
448
                     logger.warn("New collectorTitleCache should never be null");
449
                 }
450
                 if (oldCollTitleCache == null){
451
                     logger.info("Old collectorTitleCache should never be null");
452
                 }
453
                 result = true;
454
             }
455
         }
456
        return result;
457
     }
458

    
459
//*********************** CLONE ********************************************************/
460

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