Project

General

Profile

Download (12.3 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.ManyToMany;
20
import javax.persistence.OrderColumn;
21
import javax.persistence.Transient;
22
import javax.xml.bind.annotation.XmlAccessType;
23
import javax.xml.bind.annotation.XmlAccessorType;
24
import javax.xml.bind.annotation.XmlElement;
25
import javax.xml.bind.annotation.XmlElementWrapper;
26
import javax.xml.bind.annotation.XmlIDREF;
27
import javax.xml.bind.annotation.XmlRootElement;
28
import javax.xml.bind.annotation.XmlSchemaType;
29
import javax.xml.bind.annotation.XmlType;
30

    
31
import org.apache.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.strategy.cache.agent.TeamDefaultCacheStrategy;
39
import eu.etaxonomy.cdm.strategy.match.Match;
40
import eu.etaxonomy.cdm.strategy.match.MatchMode;
41

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

    
77
    @XmlElement(name = "ProtectedNomenclaturalTitleCache")
78
	private boolean protectedNomenclaturalTitleCache = false;
79

    
80
    //under construction #4311
81
    @XmlElement(name = "ProtectedCollectorTitleCache")
82
	private final boolean protectedCollectorTitleCache = false;
83

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

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

    
101

    
102

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

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

    
121
	/**
122
	 * Class constructor (including the cache strategy defined in
123
	 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}).
124
	 */
125
	public Team() {
126
		super();
127
		this.cacheStrategy = TeamDefaultCacheStrategy.NewInstance();
128
		addListenersToMembers();
129
	}
130

    
131
	/**
132
	 * Adds a property change listener to all team members.
133
	 */
134
	private void addListenersToMembers() {
135
		List<Person> members = getTeamMembers();
136
		for (Person member : members){
137
			addListenerForTeamMember(member);
138
		}
139
	}
140

    
141
	/**
142
	 * @return
143
	 */
144
	private void addListenerForTeamMember(Person member) {
145
		PropertyChangeListener listener = new PropertyChangeListener() {
146
			@Override
147
            public void propertyChange(PropertyChangeEvent e) {
148

    
149
// 			   ---- code with no effect below -----
150
//				if (! isProtectedTitleCache()){
151
//					titleCache = titleCache;
152
//				}
153
//				if (! isProtectedNomenclaturalTitleCache()){
154
//					nomenclaturalTitle = nomenclaturalTitle;
155
//				}
156
			}
157
		};
158
		member.addPropertyChangeListener(listener);
159
	}
160

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

    
172
	protected void setTeamMembers(List<Person> teamMembers) {
173
		this.teamMembers = teamMembers;
174
		addListenersToMembers();
175
	}
176

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

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

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

    
232
    public boolean replaceTeamMember(Person newObject, Person oldObject){
233
        return replaceInList(this.teamMembers, newObject, oldObject);
234
    }
235

    
236

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

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

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

    
291

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

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

    
320
	public void setProtectedNomenclaturalTitleCache(
321
			boolean protectedNomenclaturalTitleCache) {
322
		this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
323
	}
324

    
325

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

    
338
	public void setHasMoreMembers(boolean hasMoreMembers) {
339
		this.hasMoreMembers = hasMoreMembers;
340
	}
341

    
342
//*********************** CLONE ********************************************************/
343

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