merge-update from trunk; added import preference to add media as media specimen
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / agent / Team.java
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.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.IndexColumn;
34 import org.hibernate.envers.Audited;
35 import org.hibernate.search.annotations.Indexed;
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 * @version 1.0
59 * @created 08-Nov-2007 13:06:58
60 */
61 @XmlAccessorType(XmlAccessType.FIELD)
62 @XmlType(name = "Team", propOrder = {
63 "protectedNomenclaturalTitleCache",
64 "teamMembers"
65 })
66 @XmlRootElement(name = "Team")
67 @Entity
68 @Indexed(index = "eu.etaxonomy.cdm.model.agent.AgentBase")
69 @Audited
70 @Configurable
71 public class Team extends TeamOrPersonBase<Team> {
72 private static final long serialVersionUID = 97640416905934622L;
73 public static final Logger logger = Logger.getLogger(Team.class);
74
75 @XmlElement(name = "ProtectedNomenclaturalTitleCache")
76 private boolean protectedNomenclaturalTitleCache = false;
77
78 //An abbreviated name for the team (e. g. in case of nomenclatural authorteams). A non abbreviated name for the team (e. g.
79 //in case of some bibliographical references)
80 @XmlElementWrapper(name = "TeamMembers", nillable = true)
81 @XmlElement(name = "TeamMember")
82 @XmlIDREF
83 @XmlSchemaType(name = "IDREF")
84 @IndexColumn(name="sortIndex", base = 0)
85 @ManyToMany(fetch = FetchType.LAZY)
86 @Cascade(CascadeType.SAVE_UPDATE)
87 @Match(MatchMode.MATCH)
88 private List<Person> teamMembers;
89
90
91 /**
92 * Creates a new team instance without any concrete {@link Person members}.
93 */
94 static public Team NewInstance(){
95 return new Team();
96 }
97
98 /**
99 * Creates a new team instance with a bibliographic and nomenclatural title
100 * but without any {@link Person members}. The caches are set to protected.
101 */
102 static public Team NewTitledInstance(String title, String nomTitle){
103 Team result = new Team();
104 result.setTitleCache(title, true);
105 result.setNomenclaturalTitle(nomTitle, true);
106 return result;
107 }
108
109 /**
110 * Class constructor (including the cache strategy defined in
111 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}).
112 */
113 public Team() {
114 super();
115 this.cacheStrategy = TeamDefaultCacheStrategy.NewInstance();
116 addListenersToMembers();
117 }
118
119 /**
120 * Adds a property change listener to all team members.
121 */
122 private void addListenersToMembers() {
123 List<Person> members = getTeamMembers();
124 for (Person member : members){
125 addListenerForTeamMember(member);
126 }
127 }
128
129 /**
130 * @return
131 */
132 private void addListenerForTeamMember(Person member) {
133 PropertyChangeListener listener = new PropertyChangeListener() {
134 @Override
135 public void propertyChange(PropertyChangeEvent e) {
136
137 // ---- code with no effect below -----
138 // if (! isProtectedTitleCache()){
139 // titleCache = titleCache;
140 // }
141 // if (! isProtectedNomenclaturalTitleCache()){
142 // nomenclaturalTitle = nomenclaturalTitle;
143 // }
144 }
145 };
146 member.addPropertyChangeListener(listener);
147 }
148
149 /**
150 * Returns the list of {@link Person members} belonging to <i>this</i> team.
151 * A person may be a member of several distinct teams.
152 */
153 public List<Person> getTeamMembers(){
154 if(teamMembers == null) {
155 this.teamMembers = new ArrayList<Person>();
156 }
157 return this.teamMembers;
158 }
159
160 protected void setTeamMembers(List<Person> teamMembers) {
161 this.teamMembers = teamMembers;
162 addListenersToMembers();
163 }
164
165 /**
166 * Adds a new {@link Person person} to <i>this</i> team at the end of the members' list.
167 *
168 * @param person the person who should be added to the other team members
169 * @see #getTeamMembers()
170 * @see Person
171 */
172 public void addTeamMember(Person person){
173 if (person != null){
174 getTeamMembers().add(person);
175 firePropertyChange("teamMember", null, person);
176 addListenerForTeamMember(person);
177 }
178 }
179
180 /**
181 * Adds a new {@link Person person} to <i>this</i> team
182 * at the given index place of the members' list. If the person is already
183 * a member of the list he will be moved to the given index place.
184 * The index must be an integer (>=0). If the index is larger than
185 * the present number of members the person will be added at the end of the list.
186 *
187 * @param person the person who should be added to the other team members
188 * @param index the position at which the person should be placed within the members' list (starting with 0)
189 * @see #getTeamMembers()
190 * @see Person
191 */
192 public void addTeamMember(Person person, int index){
193 if (person != null){
194 int oldIndex = getTeamMembers().indexOf(person);
195 if (oldIndex != -1 ){
196 getTeamMembers().remove(person);
197 }
198 if (index >= getTeamMembers().size()){
199 index = getTeamMembers().size();
200 }
201 getTeamMembers().add(index, person);
202 addListenerForTeamMember(person);
203 firePropertyChange("teamMember", null, person);
204 }
205 }
206
207 /**
208 * Removes one person from the list of members of <i>this</i> team.
209 *
210 * @param person the person who should be deleted from <i>this</i> team
211 * @see #getTeamMembers()
212 */
213 public void removeTeamMember(Person person){
214 boolean wasMember = getTeamMembers().remove(person);
215 if (wasMember){
216 firePropertyChange("teamMember", person, null);
217 }
218
219 }
220
221
222 /**
223 * Generates or returns the {@link TeamOrPersonBase#getnomenclaturalTitle() nomenclatural identification} string for <i>this</i> team.
224 * This method overrides {@link TeamOrPersonBase#getNomenclaturalTitle() getNomenclaturalTitle}.
225 * This string is built with the {@link TeamOrPersonBase#getNomenclaturalTitle() abbreviated names}
226 * of all persons belonging to its (ordered) members' list if the flag
227 * {@link #protectedNomenclaturalTitleCache protectedNomenclaturalTitleCache} is not set.
228 * Otherwise this method returns the present nomenclatural abbreviation.
229 * In case the string is generated the cache strategy used is defined in
230 * {@link eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy TeamDefaultCacheStrategy}.
231 * The result might be kept as nomenclatural abbreviation
232 * by using the {@link #setNomenclaturalTitle(String) setNomenclaturalTitle} method.
233 *
234 * @return a string which identifies <i>this</i> team for nomenclature
235 */
236 @Override
237 @Transient
238 public String getNomenclaturalTitle() {
239 if (protectedNomenclaturalTitleCache == PROTECTED){
240 return this.nomenclaturalTitle;
241 }
242 if (nomenclaturalTitle == null){
243 this.nomenclaturalTitle = cacheStrategy.getNomenclaturalTitle(this);
244 }else{
245 //as long as team members to not inform the team about changes the cache must be created new each time
246 nomenclaturalTitle = cacheStrategy.getNomenclaturalTitle(this);
247 }
248 return nomenclaturalTitle;
249 }
250
251 /**
252 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
253 * and protects it from overwriting.
254 * This method overrides {@link TeamOrPersonBase#setNomenclaturalTitle(String) setNomenclaturalTitle}.
255 *
256 * @see #getNomenclaturalTitle()
257 * @see #setNomenclaturalTitle(String, boolean)
258 */
259 @Override
260 public void setNomenclaturalTitle(String nomenclaturalTitle) {
261 this.setNomenclaturalTitle(nomenclaturalTitle, PROTECTED);
262 }
263
264 /**
265 * Assigns a {@link TeamOrPersonBase#nomenclaturalTitle nomenclatural identification} string to <i>this</i> team
266 * and a protection flag status to this string.
267 *
268 * @see #getNomenclaturalTitle()
269 */
270 public void setNomenclaturalTitle(String nomenclaturalTitle, boolean protectedNomenclaturalTitleCache) {
271 firePropertyChange("nomenclaturalTitle", this.nomenclaturalTitle, nomenclaturalTitle);
272 this.nomenclaturalTitle = nomenclaturalTitle;
273 this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
274 }
275
276 /* (non-Javadoc)
277 * @see eu.etaxonomy.cdm.model.agent.TeamOrPersonBase#getTitleCache()
278 */
279 @Override
280 //@Transient //TODO a.kohlbecker remove??
281 public String getTitleCache() {
282 isGeneratingTitleCache = true;
283 String result = "";
284 if (isProtectedTitleCache()){
285 result = this.titleCache;
286 }else{
287 result = generateTitle();
288 result = replaceEmptyTitleByNomTitle(result);
289 result = getTruncatedCache(result);
290 this.titleCache = result;
291 }
292 isGeneratingTitleCache = false;
293 return result;
294 }
295
296 public boolean isProtectedNomenclaturalTitleCache() {
297 return protectedNomenclaturalTitleCache;
298 }
299
300 public void setProtectedNomenclaturalTitleCache(
301 boolean protectedNomenclaturalTitleCache) {
302 this.protectedNomenclaturalTitleCache = protectedNomenclaturalTitleCache;
303 }
304
305 //*********************** CLONE ********************************************************/
306
307 /**
308 * Clones <i>this</i> Team. This is a shortcut that enables to create
309 * a new instance that differs only slightly from <i>this</i> Team.
310 * The corresponding person is cloned.
311 *
312 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
313 * @see java.lang.Object#clone()
314 */
315 @Override
316 public Object clone() {
317 try{
318 Team result = (Team)super.clone();
319 result.teamMembers = new ArrayList<Person>();
320 for (Person teamMember: this.teamMembers){
321 result.addTeamMember(teamMember);
322 }
323 //no changes to protectedNomenclaturalTitleCache
324 return result;
325 } catch (CloneNotSupportedException e){
326 logger.warn("Object does not implement cloneable");
327 e.printStackTrace();
328 return null;
329 }
330
331
332 }
333
334
335 }