Project

General

Profile

Download (12.5 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.ArrayList;
15
import java.util.List;
16

    
17
import javax.persistence.Entity;
18
import javax.persistence.FetchType;
19
import javax.persistence.JoinColumn;
20
import javax.persistence.JoinTable;
21
import javax.persistence.ManyToMany;
22
import javax.persistence.OrderColumn;
23
import javax.persistence.Transient;
24
import javax.xml.bind.annotation.XmlAccessType;
25
import javax.xml.bind.annotation.XmlAccessorType;
26
import javax.xml.bind.annotation.XmlElement;
27
import javax.xml.bind.annotation.XmlElementWrapper;
28
import javax.xml.bind.annotation.XmlIDREF;
29
import javax.xml.bind.annotation.XmlRootElement;
30
import javax.xml.bind.annotation.XmlSchemaType;
31
import javax.xml.bind.annotation.XmlType;
32

    
33
import org.apache.log4j.Logger;
34
import org.hibernate.annotations.Cascade;
35
import org.hibernate.annotations.CascadeType;
36
import org.hibernate.annotations.ListIndexBase;
37
import org.hibernate.envers.Audited;
38
import org.springframework.beans.factory.annotation.Configurable;
39

    
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
 * @version 1.0
61
 * @created 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
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
73
//@Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
74
@Audited
75
@Configurable
76
public class Team extends TeamOrPersonBase<Team> {
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
    //under construction #4311
84
    @XmlElement(name = "ProtectedCollectorTitleCache")
85
	private final boolean protectedCollectorTitleCache = false;
86

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

    
103
    @XmlElement(name = "hasMoreMembers")
104
	private boolean hasMoreMembers;
105

    
106

    
107

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

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

    
126
	/**
127
	 * Class constructor (including the cache strategy defined in
128
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}).
129
	 */
130
	public Team() {
131
		super();
132
		this.cacheStrategy = TeamDefaultCacheStrategy.NewInstance();
133
		addListenersToMembers();
134
	}
135

    
136
	/**
137
	 * Adds a property change listener to all team members.
138
	 */
139
	private void addListenersToMembers() {
140
		List<Person> members = getTeamMembers();
141
		for (Person member : members){
142
			addListenerForTeamMember(member);
143
		}
144
	}
145

    
146
	/**
147
	 * @return
148
	 */
149
	private void addListenerForTeamMember(Person member) {
150
		PropertyChangeListener listener = new PropertyChangeListener() {
151
			@Override
152
            public void propertyChange(PropertyChangeEvent e) {
153

    
154
// 			   ---- code with no effect below -----
155
//				if (! isProtectedTitleCache()){
156
//					titleCache = titleCache;
157
//				}
158
//				if (! isProtectedNomenclaturalTitleCache()){
159
//					nomenclaturalTitle = nomenclaturalTitle;
160
//				}
161
			}
162
		};
163
		member.addPropertyChangeListener(listener);
164
	}
165

    
166
	/**
167
	 * Returns the list of {@link Person members} belonging to <i>this</i> team.
168
	 * A person may be a member of several distinct teams.
169
	 */
170
	public List<Person> getTeamMembers(){
171
		if(teamMembers == null) {
172
			this.teamMembers = new ArrayList<Person>();
173
		}
174
		return this.teamMembers;
175
	}
176

    
177
	protected void setTeamMembers(List<Person> teamMembers) {
178
		this.teamMembers = teamMembers;
179
		addListenersToMembers();
180
	}
181

    
182
	/**
183
	 * Adds a new {@link Person person} to <i>this</i> team at the end of the members' list.
184
	 *
185
	 * @param  person  the person who should be added to the other team members
186
	 * @see     	   #getTeamMembers()
187
	 * @see 		   Person
188
	 */
189
	public void addTeamMember(Person person){
190
		if (person != null){
191
			getTeamMembers().add(person);
192
			firePropertyChange("teamMember", null, person);
193
			addListenerForTeamMember(person);
194
		}
195
	}
196

    
197
	/**
198
	 * Adds a new {@link Person person} to <i>this</i> team
199
	 * at the given index place of the members' list. If the person is already
200
	 * a member of the list he will be moved to the given index place.
201
	 * The index must be an integer (>=0). If the index is larger than
202
	 * the present number of members the person will be added at the end of the list.
203
	 *
204
	 * @param  person  the person who should be added to the other team members
205
	 * @param  index   the position at which the person should be placed within the members' list (starting with 0)
206
	 * @see     	   #getTeamMembers()
207
	 * @see 		   Person
208
	 */
209
	public void addTeamMember(Person person, int index){
210
		if (person != null){
211
			int oldIndex = getTeamMembers().indexOf(person);
212
			if (oldIndex != -1 ){
213
				getTeamMembers().remove(person);
214
			}
215
			if (index >= getTeamMembers().size()){
216
				index = getTeamMembers().size();
217
			}
218
			getTeamMembers().add(index, person);
219
			addListenerForTeamMember(person);
220
			firePropertyChange("teamMember", null, person);
221
		}
222
	}
223

    
224
	/**
225
	 * Removes one person from the list of members of <i>this</i> team.
226
	 *
227
	 * @param  person  the person who should be deleted from <i>this</i> team
228
	 * @see            #getTeamMembers()
229
	 */
230
	public void removeTeamMember(Person person){
231
		boolean wasMember = getTeamMembers().remove(person);
232
		if (wasMember){
233
			firePropertyChange("teamMember", person, null);
234
		}
235
	}
236

    
237
    public boolean replaceTeamMember(Person newObject, Person oldObject){
238
        return replaceInList(this.teamMembers, newObject, oldObject);
239
    }
240

    
241

    
242
	/**
243
	 * Generates or returns the {@link TeamOrPersonBase#getnomenclaturalTitle() nomenclatural identification} string for <i>this</i> team.
244
	 * This method overrides {@link TeamOrPersonBase#getNomenclaturalTitle() getNomenclaturalTitle}.
245
	 * This string is built with the {@link TeamOrPersonBase#getNomenclaturalTitle() abbreviated names}
246
	 * of all persons belonging to its (ordered) members' list if the flag
247
	 * {@link #protectedNomenclaturalTitleCache protectedNomenclaturalTitleCache} is not set.
248
	 * Otherwise this method returns the present nomenclatural abbreviation.
249
	 * In case the string is generated the cache strategy used is defined in
250
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}.
251
	 * The result might be kept as nomenclatural abbreviation
252
	 * by using the {@link #setNomenclaturalTitle(String) setNomenclaturalTitle} method.
253
	 *
254
	 * @return  a string which identifies <i>this</i> team for nomenclature
255
	 */
256
	@Override
257
	@Transient
258
	public String getNomenclaturalTitle() {
259
		if (protectedNomenclaturalTitleCache == PROTECTED){
260
			return this.nomenclaturalTitle;
261
		}
262
		if (nomenclaturalTitle == null){
263
			this.nomenclaturalTitle = cacheStrategy.getNomenclaturalTitle(this);
264
		}else{
265
			//as long as team members to not inform the team about changes the cache must be created new each time
266
			nomenclaturalTitle = cacheStrategy.getNomenclaturalTitle(this);
267
		}
268
		return nomenclaturalTitle;
269
	}
270

    
271
	/**
272
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
273
	 * and protects it from overwriting.
274
	 * This method overrides {@link TeamOrPersonBase#setNomenclaturalTitle(String) setNomenclaturalTitle}.
275
	 *
276
	 * @see  #getNomenclaturalTitle()
277
	 * @see  #setNomenclaturalTitle(String, boolean)
278
	 */
279
	@Override
280
	public void setNomenclaturalTitle(String nomenclaturalTitle) {
281
		this.setNomenclaturalTitle(nomenclaturalTitle, PROTECTED);
282
	}
283

    
284
	/**
285
	 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
286
	 * and a protection flag status to this string.
287
	 *
288
	 * @see  #getNomenclaturalTitle()
289
	 */
290
	public void setNomenclaturalTitle(String nomenclaturalTitle, boolean protectedNomenclaturalTitleCache) {
291
		firePropertyChange("nomenclaturalTitle", this.nomenclaturalTitle, nomenclaturalTitle);
292
		this.nomenclaturalTitle = nomenclaturalTitle == "" ? null: nomenclaturalTitle;
293
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
294
	}
295

    
296

    
297
	@Override
298
	//@Transient //TODO a.kohlbecker remove??
299
	public String getTitleCache() {
300
		isGeneratingTitleCache = true;
301
		String result = "";
302
		if (isProtectedTitleCache()){
303
			result = this.titleCache;
304
		}else{
305
			result = generateTitle();
306
			result = replaceEmptyTitleByNomTitle(result);
307
			result = getTruncatedCache(result);
308
			this.titleCache = result;
309
		}
310
		isGeneratingTitleCache = false;
311
		return result;
312
	}
313

    
314
	/**
315
	 * Protected nomenclatural title cache flag should be set to true, if
316
	 * the title cache is to be preferred against the atomized data.
317
	 * This may be the case if no atomized data exists or if atomization
318
	 * was incomplete for whatever reason.
319
	 * @return
320
	 */
321
	public boolean isProtectedNomenclaturalTitleCache() {
322
		return protectedNomenclaturalTitleCache;
323
	}
324

    
325
	public void setProtectedNomenclaturalTitleCache(
326
			boolean protectedNomenclaturalTitleCache) {
327
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
328
	}
329

    
330

    
331
	/**
332
	 * The hasMoreMembers flag is true if this team has more members than
333
	 * mentioned in the {@link #teamMembers} list. This is usually the case
334
	 * when "et al." is used in the team representation. Formatters should add
335
	 * "et al." or an according post fix to the team string representation
336
	 *  if this flag is set.
337
	 * @return
338
	 */
339
	public boolean isHasMoreMembers() {
340
		return hasMoreMembers;
341
	}
342

    
343
	public void setHasMoreMembers(boolean hasMoreMembers) {
344
		this.hasMoreMembers = hasMoreMembers;
345
	}
346

    
347
//*********************** CLONE ********************************************************/
348

    
349
	/**
350
	 * Clones <i>this</i> Team. This is a shortcut that enables to create
351
	 * a new instance that differs only slightly from <i>this</i> Team.
352
	 * The corresponding person is cloned.
353
	 *
354
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
355
	 * @see java.lang.Object#clone()
356
	 */
357
	@Override
358
	public Object clone() {
359
		try{
360
			Team result = (Team)super.clone();
361
			result.teamMembers = new ArrayList<Person>();
362
			for (Person teamMember: this.teamMembers){
363
				result.addTeamMember(teamMember);
364
			}
365
			//no changes to protectedNomenclaturalTitleCache
366
			return result;
367
		} catch (CloneNotSupportedException e){
368
			logger.warn("Object does not implement cloneable");
369
			e.printStackTrace();
370
			return null;
371
		}
372
	}
373
}
(8-8/11)