Project

General

Profile

Download (16.4 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.model.EntityCollectionSetterAdapter;
38
import eu.etaxonomy.cdm.model.EntityCollectionSetterAdapter.SetterAdapterException;
39
import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
40
import eu.etaxonomy.cdm.strategy.match.Match;
41
import eu.etaxonomy.cdm.strategy.match.MatchMode;
42

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

    
126
    public static Team NewInstance(Person... members) {
127
        Team team = new Team();
128
        for (Person member : members){
129
            team.addTeamMember(member);
130
        }
131
        return team;
132
    }
133

    
134
//************************ CONSTRUCTOR *******************************/
135

    
136
	/**
137
	 * Class constructor (including the cache strategy defined in
138
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}).
139
	 */
140
	public Team() {
141
		addListenersToMembers();
142
	}
143

    
144
// ******************************************************************/
145

    
146
    @Override
147
    protected void initDefaultCacheStrategy() {
148
        this.cacheStrategy = TeamDefaultCacheStrategy.NewInstance();
149
    }
150

    
151
    @Override
152
    public void initListener(){
153
        PropertyChangeListener listener = new PropertyChangeListener() {
154
            @Override
155
            public void propertyChange(PropertyChangeEvent ev) {
156
                if (!ev.getPropertyName().equals("titleCache") &&
157
                        !ev.getPropertyName().equals("collectorTitleCache") &&
158
                        !ev.getPropertyName().equals("cacheStrategy")){
159
                    if (! isProtectedTitleCache()){
160
                        titleCache = null;
161
                    }
162
                    if (! isProtectedCollectorTitleCache()){
163
                        collectorTitleCache = null;
164
                    }
165
                }
166
            }
167
        };
168
        addPropertyChangeListener(listener);
169
    }
170

    
171
	/**
172
	 * Adds a property change listener to all team members.
173
	 */
174
	private void addListenersToMembers() {
175
		List<Person> members = getTeamMembers();
176
		for (Person member : members){
177
			addListenerForTeamMember(member);
178
		}
179
	}
180

    
181
	private void addListenerForTeamMember(Person member) {
182
		PropertyChangeListener listener = new PropertyChangeListener() {
183
			@Override
184
            public void propertyChange(PropertyChangeEvent e) {
185

    
186
// 			   ---- code with no effect below -----
187
				if (! isProtectedTitleCache()){
188
					titleCache = null;
189
				}
190
//				if (! isProtectedNomenclaturalTitleCache()){
191
//					nomenclaturalTitle = nomenclaturalTitle;
192
//				}
193
              if (! isProtectedCollectorTitleCache()){
194
                  collectorTitleCache = null;
195
              }
196
			}
197
		};
198
		member.addPropertyChangeListener(listener);
199
	}
200

    
201
    public void resetCaches() {
202
        if(!protectedTitleCache){
203
            titleCache = null;
204
        }
205
        if (!protectedCollectorTitleCache){
206
            collectorTitleCache = null;
207
        }
208
    }
209

    
210
	/**
211
	 * Returns the list of {@link Person members} belonging to <i>this</i> team.
212
	 * A person may be a member of several distinct teams.
213
	 */
214
	public List<Person> getTeamMembers(){
215
		if(teamMembers == null) {
216
			this.teamMembers = new ArrayList<>();
217
		}
218
		return this.teamMembers;
219
	}
220

    
221
	public void setTeamMembers(List<Person> teamMembers) throws SetterAdapterException {
222
	    new EntityCollectionSetterAdapter<Team, Person>(Team.class, Person.class, "teamMembers").setCollection(this, teamMembers);
223
    }
224

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

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

    
267
	/**
268
	 * Removes one person from the list of members of <i>this</i> team.
269
	 *
270
	 * @param  person  the person who should be deleted from <i>this</i> team
271
	 * @see            #getTeamMembers()
272
	 */
273
	public void removeTeamMember(Person person){
274
		boolean wasMember = getTeamMembers().remove(person);
275
		if (wasMember){
276
			firePropertyChange("teamMember", person, null);
277
		}
278
	}
279

    
280
    public boolean replaceTeamMember(Person newObject, Person oldObject){
281
        return replaceInList(this.teamMembers, newObject, oldObject);
282
    }
283

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

    
313
	/**
314
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
315
	 * and protects it from overwriting.
316
	 * This method overrides {@link TeamOrPersonBase#setNomenclaturalTitle(String) setNomenclaturalTitle}.
317
	 *
318
	 * @see  #getNomenclaturalTitle()
319
	 * @see  #setNomenclaturalTitle(String, boolean)
320
	 */
321
	@Override
322
	public void setNomenclaturalTitle(String nomenclaturalTitle) {
323
		this.setNomenclaturalTitle(nomenclaturalTitle, PROTECTED);
324
	}
325

    
326
	/**
327
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
328
	 * and a protection flag status to this string.
329
	 *
330
	 * @see  #getNomenclaturalTitle()
331
	 */
332
	public void setNomenclaturalTitle(String nomenclaturalTitle, boolean protectedNomenclaturalTitleCache) {
333
		firePropertyChange("nomenclaturalTitle", this.nomenclaturalTitle, nomenclaturalTitle);
334
		this.nomenclaturalTitle = nomenclaturalTitle == "" ? null: nomenclaturalTitle;
335
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
336
	}
337

    
338
	@Override
339
	//@Transient //TODO a.kohlbecker remove??
340
	public String getTitleCache() {
341
		isGeneratingTitleCache = true;
342
		String result = "";
343
		if (isProtectedTitleCache()){
344
			result = this.titleCache;
345
		}else{
346
			result = generateTitle();
347
			result = replaceEmptyTitleByNomTitle(result);
348
			result = getTruncatedCache(result);
349
			this.titleCache = result;
350
		}
351
		isGeneratingTitleCache = false;
352
		return result;
353
	}
354

    
355
    //#4311
356
    @Override
357
    public String getCollectorTitleCache() {
358
        if (protectedCollectorTitleCache){
359
            return this.collectorTitleCache;
360
        }else{
361
            return super.getCollectorTitleCache();
362
        }
363
    }
364

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

    
379

    
380
	public boolean isProtectedCollectorTitleCache() {
381
        return protectedCollectorTitleCache;
382
    }
383
    public void setProtectedCollectorTitleCache(boolean protectedCollectorTitleCache) {
384
        this.protectedCollectorTitleCache = protectedCollectorTitleCache;
385
    }
386

    
387
    public void setCollectorTitleCache(String collectorTitleCache, boolean protectedCache) {
388
        this.collectorTitleCache = collectorTitleCache;
389
        this.protectedCollectorTitleCache = protectedCache;
390
    }
391

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

    
407
    @Override
408
    public boolean updateCaches(){
409
        boolean result = super.updateCaches();
410
        if (this.protectedNomenclaturalTitleCache == false){
411
            String oldNomTitleCache = this.nomenclaturalTitle;
412

    
413
            String newNomTitleCache = getCacheStrategy().getNomenclaturalTitle(this);
414

    
415
            if ( oldNomTitleCache == null   || ! oldNomTitleCache.equals(newNomTitleCache) ){
416
                this.setNomenclaturalTitle(null, false);
417
                String newCache = this.getNomenclaturalTitle();
418

    
419
                if (newCache == null){
420
                    logger.warn("New nomTitleCache should never be null");
421
                }
422
                if (oldNomTitleCache == null){
423
                    logger.info("Old nomTitleCache should never be null");
424
                }
425
                result = true;
426
            }
427
        }
428
        if (this.protectedCollectorTitleCache == false){
429
            String oldCollTitleCache = this.collectorTitleCache;
430
            String newCollTitleCache = getCacheStrategy().getCollectorTitleCache(this);
431

    
432
            if ( oldCollTitleCache == null || ! oldCollTitleCache.equals(newCollTitleCache) ){
433
                 this.setCollectorTitleCache(null, false);
434
                 String newCache = this.getCollectorTitleCache();
435

    
436
                 if (newCache == null){
437
                     logger.warn("New collectorTitleCache should never be null");
438
                 }
439
                 if (oldCollTitleCache == null){
440
                     logger.info("Old collectorTitleCache should never be null");
441
                 }
442
                 result = true;
443
             }
444
         }
445
        return result;
446
     }
447

    
448
//*********************** CLONE ********************************************************/
449

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

    
474
}
(9-9/12)