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