Merge branch 'develop' into ticket-5066
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / agent / Contact.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.io.Serializable;
13 import java.net.URI;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Set;
18
19 import javax.persistence.Column;
20 import javax.persistence.ElementCollection;
21 import javax.persistence.Embeddable;
22 import javax.persistence.FetchType;
23 import javax.persistence.OneToMany;
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.XmlRootElement;
29 import javax.xml.bind.annotation.XmlSchemaType;
30 import javax.xml.bind.annotation.XmlType;
31
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.log4j.Logger;
34 import org.hibernate.annotations.Cascade;
35 import org.hibernate.annotations.CascadeType;
36 import org.hibernate.envers.Audited;
37
38 import eu.etaxonomy.cdm.model.location.Country;
39 import eu.etaxonomy.cdm.model.location.Point;
40 import eu.etaxonomy.cdm.strategy.merge.MergeException;
41
42 /**
43 * The class for information on how to approach a {@link Person person} or an {@link Institution institution}.
44 * It includes telecommunication data and an electronic as well as
45 * multiple postal addresses.
46 * <P>
47 * This class corresponds to: <ul>
48 * <li> ContactDetails according to the TDWG ontology
49 * <li> Contact (partially) according to the ABCD schema
50 * </ul>
51 *
52 * @author m.doering
53 * @version 1.0
54 * @created 08-Nov-2007 13:06:18
55 */
56 @XmlAccessorType(XmlAccessType.FIELD)
57 @XmlType(name = "Contact", propOrder = {
58 "emailAddresses",
59 "urls",
60 "phoneNumbers",
61 "faxNumbers",
62 "addresses"
63 })
64 @XmlRootElement(name = "Contact")
65 @Embeddable
66 @Audited
67 public class Contact implements Serializable, Cloneable {
68 private static final long serialVersionUID = -1851305307069277625L;
69 private static final Logger logger = Logger.getLogger(Contact.class);
70
71
72 @XmlElementWrapper(name = "EmailAddresses", nillable = true)
73 @XmlElement(name = "EmailAddress")
74 @ElementCollection(fetch = FetchType.LAZY)
75 @Column(name = "contact_emailaddresses_element")
76 private List<String> emailAddresses;
77
78 @XmlElementWrapper(name = "URLs", nillable = true)
79 @XmlElement(name = "URL")
80 @XmlSchemaType(name = "anyURI")
81 @ElementCollection(fetch = FetchType.LAZY)
82 @Column(name = "contact_urls_element" /*, length=330 */) //length >255 does not work in InnoDB AUD tables for Key length of (REV, id, url) key
83 private List<String> urls = new ArrayList<String>();
84
85 @XmlElementWrapper(name = "PhoneNumbers", nillable = true)
86 @XmlElement(name = "PhoneNumber")
87 @ElementCollection(fetch = FetchType.LAZY)
88 @Column(name = "contact_phonenumbers_element")
89 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
90 private List<String> phoneNumbers;
91
92 @XmlElementWrapper(name = "FaxNumbers", nillable = true)
93 @XmlElement(name = "FaxNumber")
94 @ElementCollection(fetch = FetchType.LAZY)
95 @Column(name = "contact_faxnumbers_element")
96 private List<String> faxNumbers;
97
98 @XmlElementWrapper(name = "Addresses", nillable = true)
99 @XmlElement(name = "Address")
100 @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
101 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
102 protected Set<Address> addresses = new HashSet<Address>();
103
104
105 public static Contact NewInstance() {
106 return new Contact();
107 }
108
109 /**
110 * Creates a new contact
111 * @param street
112 * @param postcode
113 * @param locality
114 * @param country
115 * @param pobox
116 * @param region
117 * @param email
118 * @param faxNumber
119 * @param phoneNumber
120 * @param url
121 * @param location
122 * @return
123 */
124 public static Contact NewInstance(String street, String postcode, String locality,
125 Country country, String pobox, String region,
126 String email, String faxNumber, String phoneNumber, URI url, Point location) {
127 Contact result = new Contact();
128 if (country != null || StringUtils.isNotBlank(locality) || StringUtils.isNotBlank(pobox) || StringUtils.isNotBlank(postcode) ||
129 StringUtils.isNotBlank(region) || StringUtils.isNotBlank(street) ){
130 Address newAddress = Address.NewInstance(country, locality, pobox, postcode, region, street, location);
131 result.addAddress(newAddress);
132 }
133 if (email != null){
134 result.addEmailAddress(email);
135 }
136 if (faxNumber != null){
137 result.addFaxNumber(faxNumber);
138 }
139 if (phoneNumber != null){
140 result.addPhoneNumber(phoneNumber);
141 }
142 if (url != null){
143 result.addUrl(url);
144 }
145 return result;
146 }
147
148
149 public static Contact NewInstance(Set<Address> addresses, List<String> emailAdresses,
150 List<String> faxNumbers, List<String> phoneNumbers, List<URI> urls) {
151 Contact result = new Contact();
152 if (addresses != null){
153 result.addresses = addresses;
154 }
155 if (emailAdresses != null){
156 result.emailAddresses = emailAdresses;
157 }
158 if (faxNumbers != null){
159 result.faxNumbers = faxNumbers;
160 }
161 if (phoneNumbers != null){
162 result.phoneNumbers = phoneNumbers;
163 }
164 if (urls != null){
165 for (URI uri : urls){
166 result.urls.add(uri.toString());
167 }
168 }
169 return result;
170 }
171
172
173 /**
174 * Class constructor.
175 */
176 public Contact() {
177 }
178
179
180 public void merge(Contact contact2) throws MergeException{
181 if (contact2 != null){
182 mergeList(this.getEmailAddresses(), contact2.getEmailAddresses());
183 mergeList(this.getFaxNumbers(), contact2.getFaxNumbers());
184 mergeList(this.getPhoneNumbers(), contact2.getPhoneNumbers());
185 mergeList(this.getUrls(), contact2.getUrls());
186 for (Address address : contact2.getAddresses()){
187 try {
188 if (this.addresses == null){
189 this.addresses = new HashSet<Address>();
190 }
191 this.addresses.add((Address)address.clone());
192 } catch (CloneNotSupportedException e) {
193 throw new MergeException("Address must implement Cloneable");
194 }
195 }
196 }
197 }
198
199 private void mergeList(List list1, List list2){
200 for (Object obj2 : list2){
201 if (! list1.contains(obj2)){
202 list1.add(obj2);
203 }
204 }
205 }
206
207
208 /**
209 * Returns the set of postal {@link Address addresses} belonging to <i>this</i> contact.
210 * A {@link Person person} or an {@link Institution institution} cannot have more than one contact,
211 * but a contact may include several postal addresses.
212 *
213 * @return the set of postal addresses
214 * @see Address
215 */
216 public Set<Address> getAddresses(){
217 return this.addresses;
218 }
219
220 /**
221 * Adds a new postal {@link Address address} to the set of postal addresses of <i>this</i> contact.
222 *
223 * @param address the address to be added
224 * @see #getAddresses()
225 * @see Address
226 */
227 public void addAddress(Address address){
228 if (address != null){
229 getAddresses().add(address);
230 }
231 }
232
233 public Address addAddress(String street, String postcode, String locality,
234 Country country, String pobox, String region, Point location){
235 Address newAddress = Address.NewInstance(country, locality, pobox, postcode, region, street, location);
236 getAddresses().add(newAddress);
237 return newAddress;
238 }
239
240 /**
241 * Removes one element from the set of postal addresses of <i>this</i> contact.
242 *
243 * @param address the postal address of <i>this</i> contact which should be deleted
244 * @see #getAddresses()
245 */
246 public void removeAddress(Address address){
247 getAddresses().remove(address);
248 }
249
250
251 /**
252 * Returns the List of strings representing the electronic mail addresses
253 * included in <i>this</i> contact.
254 */
255 public List<String> getEmailAddresses(){
256 if(this.emailAddresses == null) {
257 this.emailAddresses = new ArrayList<String>();
258 }
259 return this.emailAddresses;
260 }
261
262 /**
263 * @see #getEmailAddress()
264 */
265 public void addEmailAddress(String emailAddress){
266 getEmailAddresses().add(emailAddress);
267 }
268
269 /**
270 * Removes one element from the list of email addresses of <i>this</i> contact.
271 *
272 * @param emailAddress the email address of <i>this</i> contact which should be deleted
273 * @see #getEmailAddresses()
274 */
275 public void removeEmailAddress(String emailAddress){
276 getEmailAddresses().remove(emailAddress);
277 }
278
279 // /**
280 // * Returns the list of {@link URI URIs} representing this contact
281 // * included in <i>this</i> contact.
282 // */
283 // @Transient //TODO preliminary workaround as we get LazyInit Exception in JSON #4444
284 // public List<URI> getUrls(){
285 // List<URI> result = new ArrayList<URI>();
286 // if(this.urls != null) {
287 // for (String uri : this.urls){
288 // result.add(URI.create(uri));
289 // }
290 // }
291 // return result;
292 // }
293
294 /**
295 * Returns the list of {@link URI URIs} representing this contact
296 * included in <i>this</i> contact.
297 */
298 public List<String> getUrls(){
299 return this.urls;
300 }
301
302 /**
303 * @see #getUrls()
304 */
305 public void addUrl(URI url){
306 this.urls.add(url.toString());
307 }
308
309 /**
310 * Removes one element from the list of urls of <i>this</i> contact.
311 *
312 * @param url the url of <i>this</i> contact which should be deleted
313 * @see #getUrls()
314 */
315 public void removeUrl(URI url){
316 this.urls.remove(url.toString());
317 }
318
319
320 /**
321 * Returns the list of strings representing the phone numbers
322 * included in <i>this</i> contact.
323 */
324 public List<String> getPhoneNumbers(){
325 if(this.phoneNumbers == null) {
326 this.phoneNumbers = new ArrayList<String>();
327 }
328 return this.phoneNumbers;
329 }
330
331 /**
332 * @see #getPhone()
333 */
334 public void addPhoneNumber(String phoneNumber){
335 getPhoneNumbers().add(phoneNumber);
336 }
337
338 /**
339 * Removes one element from the list of phone numbers of <i>this</i> contact.
340 *
341 * @param phoneNumber the phone number of <i>this</i> contact which should be deleted
342 * @see #getPhoneNumber()
343 */
344 public void removePhoneNumber(String phoneNumber){
345 getPhoneNumbers().remove(phoneNumber);
346 }
347
348 /**
349 * Returns the list of strings representing the telefax numbers
350 * included in <i>this</i> contact.
351 */
352 public List<String> getFaxNumbers(){
353 if(this.faxNumbers == null) {
354 this.faxNumbers = new ArrayList<String>();
355 }
356 return this.faxNumbers;
357 }
358
359 /**
360 * @see #getFaxNumbers()
361 */
362 public void addFaxNumber(String faxNumber){
363 getFaxNumbers().add(faxNumber);
364 }
365
366 /**
367 * Removes one element from the list of telefax numbers of <i>this</i> contact.
368 *
369 * @param faxNumber the telefax number of <i>this</i> contact which should be deleted
370 * @see #getFaxNumber()
371 */
372 public void removeFaxNumber(String faxNumber){
373 getFaxNumbers().remove(faxNumber);
374 }
375
376 //*********************** CLONE ********************************************************/
377
378 /**
379 * Clones <i>this</i> Contact. This is a shortcut that enables to create
380 * a new instance that differs only slightly from <i>this</i> Contact.
381 *
382 *
383 * @see java.lang.Object#clone()
384 */
385 @Override
386 public Object clone() {
387 try{
388 Contact result = (Contact) super.clone();
389 result.addresses = new HashSet<Address>();
390 for (Address adr : this.addresses){
391 result.addAddress((Address)adr.clone());
392 }
393 //no changes to emailAdresses, faxNumbers, phoneNumbers, urls
394 return result;
395 }catch (CloneNotSupportedException e){
396 logger.warn("Object does not implement cloneable");
397 e.printStackTrace();
398 return null;
399 }
400 }
401 }