Project

General

Profile

Download (13.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.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.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
	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
    //under construction #4311
83
    @XmlElement(name = "ProtectedCollectorTitleCache")
84
	private final 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

    
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
		result.setTitleCache(title, true);
119
		result.setNomenclaturalTitle(nomTitle, true);
120
		return result;
121
	}
122

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

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

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

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

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

    
174
	public void setTeamMembers(List<Person> teamMembers) throws SetterAdapterException {
175
	    new EntityCollectionSetterAdapter<Team, Person>(Team.class, Person.class, "teamMembers").setCollection(this, teamMembers);
176
    }
177

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

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

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

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

    
237

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

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

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

    
292

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

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

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

    
326

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

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

    
343

    
344
    @Override
345
    public boolean updateCaches(){
346
        boolean result = super.updateCaches();
347
        if (this.protectedNomenclaturalTitleCache == false){
348
            String oldNomTitleCache = this.nomenclaturalTitle;
349
            this.protectedNomenclaturalTitleCache = false;
350

    
351
            String newAbbrevTitleCache = cacheStrategy.getTitleCache(this);
352

    
353
            if ( oldNomTitleCache == null   || ! oldNomTitleCache.equals(newAbbrevTitleCache) ){
354
                 this.setNomenclaturalTitle(null, false);
355
                 String newCache = this.getNomenclaturalTitle();
356

    
357
                 if (newCache == null){
358
                     logger.warn("New nomTitleCache should never be null");
359
                 }
360
                 if (oldNomTitleCache == null){
361
                     logger.info("Old nomTitleCache should never be null");
362
                 }
363
                 result = true;
364
             }
365
         }
366
        return result;
367
     }
368

    
369
//*********************** CLONE ********************************************************/
370

    
371
	/**
372
	 * Clones <i>this</i> Team. This is a shortcut that enables to create
373
	 * a new instance that differs only slightly from <i>this</i> Team.
374
	 * The corresponding person is cloned.
375
	 *
376
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
377
	 * @see java.lang.Object#clone()
378
	 */
379
	@Override
380
	public Object clone() {
381
		try{
382
			Team result = (Team)super.clone();
383
			result.teamMembers = new ArrayList<>();
384
			for (Person teamMember: this.teamMembers){
385
				result.addTeamMember(teamMember);
386
			}
387
			//no changes to protectedNomenclaturalTitleCache
388
			return result;
389
		} catch (CloneNotSupportedException e){
390
			logger.warn("Object does not implement cloneable");
391
			e.printStackTrace();
392
			return null;
393
		}
394
	}
395
}
(8-8/11)