/**
* Copyright (C) 2007 EDIT
-* European Distributed Institute of Taxonomy
+* European Distributed Institute of Taxonomy
* http://www.e-taxonomy.eu
-*
+*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* See LICENSE.TXT at the top of this package for the full license terms.
*/
-
package eu.etaxonomy.cdm.model.agent;
-import eu.etaxonomy.cdm.model.common.VersionableEntity;
+import java.io.Serializable;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Embeddable;
+import javax.persistence.FetchType;
+import javax.persistence.OneToMany;
+import javax.persistence.Transient;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
+import org.hibernate.envers.Audited;
-import java.util.*;
-import javax.persistence.*;
+import eu.etaxonomy.cdm.model.location.Country;
+import eu.etaxonomy.cdm.model.location.Point;
+import eu.etaxonomy.cdm.strategy.merge.MergeException;
/**
- * Information on how to approach a {@link Person person} or an {@link Institution institution}.
- * It includes telecommunication data
- * and electronic as well as multiple postal addresses.
- * <p>
- * See also the <a href="http://rs.tdwg.org/ontology/voc/ContactDetails#ContactDetails">TDWG Ontology</a>
- *
+ * The class for information on how to approach a {@link Person person} or an {@link Institution institution}.
+ * It includes telecommunication data and an electronic as well as
+ * multiple postal addresses.
+* <P>
+ * This class corresponds to: <ul>
+ * <li> ContactDetails according to the TDWG ontology
+ * <li> Contact (partially) according to the ABCD schema
+ * </ul>
+ *
* @author m.doering
- * @version 1.0
- * @created 08-Nov-2007 13:06:18
+ * @since 08-Nov-2007 13:06:18
*/
-@Entity
-public class Contact extends VersionableEntity {
- /**
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "Contact", propOrder = {
+ "emailAddresses",
+ "urls",
+ "phoneNumbers",
+ "faxNumbers",
+ "addresses"
+})
+@XmlRootElement(name = "Contact")
+@Embeddable
+@Audited
+public class Contact implements Serializable, Cloneable {
+ private static final long serialVersionUID = -1851305307069277625L;
+ private static final Logger logger = Logger.getLogger(Contact.class);
+
+
+ @XmlElementWrapper(name = "EmailAddresses", nillable = true)
+ @XmlElement(name = "EmailAddress")
+ @ElementCollection(fetch = FetchType.LAZY)
+ @Column(name = "contact_emailaddresses_element")
+ private List<String> emailAddresses = new ArrayList<>();
+
+ @XmlElementWrapper(name = "URLs", nillable = true)
+ @XmlElement(name = "URL")
+ @XmlSchemaType(name = "anyURI")
+ @ElementCollection(fetch = FetchType.LAZY)
+ @Column(name = "contact_urls_element" /*, length=330 */) //length >255 does not work in InnoDB AUD tables for Key length of (REV, id, url) key
+ private final List<String> urls = new ArrayList<>();
+
+ @XmlElementWrapper(name = "PhoneNumbers", nillable = true)
+ @XmlElement(name = "PhoneNumber")
+ @ElementCollection(fetch = FetchType.LAZY)
+ @Column(name = "contact_phonenumbers_element")
+ @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
+ private List<String> phoneNumbers = new ArrayList<>();
+
+ @XmlElementWrapper(name = "FaxNumbers", nillable = true)
+ @XmlElement(name = "FaxNumber")
+ @ElementCollection(fetch = FetchType.LAZY)
+ @Column(name = "contact_faxnumbers_element")
+ private List<String> faxNumbers = new ArrayList<>();
+
+ @XmlElementWrapper(name = "Addresses", nillable = true)
+ @XmlElement(name = "Address")
+ @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
+ @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
+ protected Set<Address> addresses = new HashSet<>();
+
+ public static Contact NewInstance() {
+ return new Contact();
+ }
+
+ /**
+ * Creates a new contact
+ * @param street
+ * @param postcode
+ * @param locality
+ * @param country
+ * @param pobox
+ * @param region
+ * @param email
+ * @param faxNumber
+ * @param phoneNumber
+ * @param url
+ * @param location
+ * @return
+ */
+ public static Contact NewInstance(String street, String postcode, String locality,
+ Country country, String pobox, String region,
+ String email, String faxNumber, String phoneNumber, URI url, Point location) {
+ Contact result = new Contact();
+ if (country != null || StringUtils.isNotBlank(locality) || StringUtils.isNotBlank(pobox) || StringUtils.isNotBlank(postcode) ||
+ StringUtils.isNotBlank(region) || StringUtils.isNotBlank(street) ){
+ Address newAddress = Address.NewInstance(country, locality, pobox, postcode, region, street, location);
+ result.addAddress(newAddress);
+ }
+ if (email != null){
+ result.addEmailAddress(email);
+ }
+ if (faxNumber != null){
+ result.addFaxNumber(faxNumber);
+ }
+ if (phoneNumber != null){
+ result.addPhoneNumber(phoneNumber);
+ }
+ if (url != null){
+ result.addUrl(url);
+ }
+ return result;
+ }
+
+
+ public static Contact NewInstance(Set<Address> addresses, List<String> emailAddresses,
+ List<String> faxNumbers, List<String> phoneNumbers, List<URI> urls) {
+ Contact result = new Contact();
+ if (addresses != null){
+ result.addresses = addresses;
+ }
+ if (emailAddresses != null){
+ result.emailAddresses = emailAddresses;
+ }
+ if (faxNumbers != null){
+ result.faxNumbers = faxNumbers;
+ }
+ if (phoneNumbers != null){
+ result.phoneNumbers = phoneNumbers;
+ }
+ if (urls != null){
+ for (URI uri : urls){
+ result.urls.add(uri.toString());
+ }
+ }
+ return result;
+ }
+
+// ************************ CONSTRUCTOR **************************/
+ /**
* Class constructor.
*/
public Contact() {
- super();
- // TODO Auto-generated constructor stub
- }
-
- static Logger logger = Logger.getLogger(Contact.class);
- private String email;
- private String url;
- private String phone;
- private String fax;
- protected Set<Address> addresses;
-
-
- /**
- * Returns the set of postal {@link Address addresses} belonging to this contact.
+ }
+
+// ************************ MERGE /MATCH ***************************/
+
+ public void merge(Contact contact2) throws MergeException{
+ if (contact2 != null){
+ mergeList(this.getEmailAddresses(), contact2.getEmailAddresses());
+ mergeList(this.getFaxNumbers(), contact2.getFaxNumbers());
+ mergeList(this.getPhoneNumbers(), contact2.getPhoneNumbers());
+ mergeList(this.getUrls(), contact2.getUrls());
+ for (Address address : contact2.getAddresses()){
+ try {
+ if (this.addresses == null){
+ this.addresses = new HashSet<>();
+ }
+ this.addresses.add(address.clone());
+ } catch (CloneNotSupportedException e) {
+ throw new MergeException("Address must implement Cloneable");
+ }
+ }
+ }
+ }
+
+ private void mergeList(List list1, List list2){
+ for (Object obj2 : list2){
+ if (! list1.contains(obj2)){
+ list1.add(obj2);
+ }
+ }
+ }
+
+ /**
+ * True, if no contact data exists in any of the lists (email, phone, ...).
+ */
+ @Transient
+ public boolean isEmpty(){
+ if (isEmpty(emailAddresses) && isEmpty(faxNumbers) && isEmpty(phoneNumbers)
+ && isEmpty(urls) && isEmpty(addresses)){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ private boolean isEmpty(Collection<? extends Object> collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ /**
+ * Returns the set of postal {@link Address addresses} belonging to <i>this</i> contact.
* A {@link Person person} or an {@link Institution institution} cannot have more than one contact,
- * but a contact may include several postal addresses.
+ * but a contact may include several postal addresses.
*
* @return the set of postal addresses
* @see Address
*/
- @OneToMany(mappedBy="contact")
- @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
public Set<Address> getAddresses(){
return this.addresses;
}
- /**
- * @see #getAddresses()
- */
- protected void setAddresses(Set<Address> addresses){
- this.addresses = addresses;
- }
- /**
- * Adds a new postal {@link Address address} to the set of postal addresses of this contact.
+
+ /**
+ * Adds a new postal {@link Address address} to the set of postal addresses of <i>this</i> contact.
*
* @param address the address to be added
* @see #getAddresses()
* @see Address
*/
public void addAddress(Address address){
- address.setContact(this);
+ if (address != null){
+ getAddresses().add(address);
+ }
}
- /**
- * Removes one element from the set of postal addresses of this contact.
+
+ public Address addAddress(String street, String postcode, String locality,
+ Country country, String pobox, String region, Point location){
+ Address newAddress = Address.NewInstance(country, locality, pobox, postcode, region, street, location);
+ getAddresses().add(newAddress);
+ return newAddress;
+ }
+
+ /**
+ * Removes one element from the set of postal addresses of <i>this</i> contact.
*
- * @param address the postal address of this contact which should be deleted
+ * @param address the postal address of <i>this</i> contact which should be deleted
* @see #getAddresses()
*/
public void removeAddress(Address address){
- address.setContact(null);
+ getAddresses().remove(address);
}
-
+
/**
- * Returns the string representing the electronic mail address
- * included in this contact.
+ * Returns the List of strings representing the electronic mail addresses
+ * included in <i>this</i> contact.
*/
- public String getEmail(){
- return this.email;
+ public List<String> getEmailAddresses(){
+ if(this.emailAddresses == null) {
+ this.emailAddresses = new ArrayList<>();
+ }
+ return this.emailAddresses;
}
/**
- * @see #getEmail()
+ * @see #getEmailAddress()
*/
- public void setEmail(String email){
- this.email = email;
+ public void addEmailAddress(String emailAddress){
+ getEmailAddresses().add(emailAddress);
}
/**
- * Returns the string representing the "Uniform Resource Locator" (url)
- * included in this contact.
+ * Removes one element from the list of email addresses of <i>this</i> contact.
+ *
+ * @param emailAddress the email address of <i>this</i> contact which should be deleted
+ * @see #getEmailAddresses()
*/
- public String getUrl(){
- return this.url;
+ public void removeEmailAddress(String emailAddress){
+ getEmailAddresses().remove(emailAddress);
}
+// /**
+// * Returns the list of {@link URI URIs} representing this contact
+// * included in <i>this</i> contact.
+// */
+// @Transient //TODO preliminary workaround as we get LazyInit Exception in JSON #4444
+// public List<URI> getUrls(){
+// List<URI> result = new ArrayList<URI>();
+// if(this.urls != null) {
+// for (String uri : this.urls){
+// result.add(URI.create(uri));
+// }
+// }
+// return result;
+// }
+
/**
- * @see #getUrl()
+ * Returns the list of {@link URI URIs} representing this contact
+ * included in <i>this</i> contact.
*/
- public void setUrl(String url){
- this.url = url;
+ public List<String> getUrls(){
+ return this.urls;
}
/**
- * Returns the string representing the phone number
- * included in this contact.
+ * @see #getUrls()
+ */
+ public void addUrl(URI url){
+ this.urls.add(url.toString());
+ }
+
+ /**
+ * Removes one element from the list of urls of <i>this</i> contact.
+ *
+ * @param url the url of <i>this</i> contact which should be deleted
+ * @see #getUrls()
+ */
+ public void removeUrl(URI url){
+ this.urls.remove(url.toString());
+ }
+
+
+ /**
+ * Returns the list of strings representing the phone numbers
+ * included in <i>this</i> contact.
*/
- public String getPhone(){
- return this.phone;
+ public List<String> getPhoneNumbers(){
+ if(this.phoneNumbers == null) {
+ this.phoneNumbers = new ArrayList<>();
+ }
+ return this.phoneNumbers;
}
/**
* @see #getPhone()
*/
- public void setPhone(String phone){
- this.phone = phone;
+ public void addPhoneNumber(String phoneNumber){
+ getPhoneNumbers().add(phoneNumber);
}
/**
- * Returns the string representing the telefax number
- * included in this contact.
+ * Removes one element from the list of phone numbers of <i>this</i> contact.
+ *
+ * @param phoneNumber the phone number of <i>this</i> contact which should be deleted
+ * @see #getPhoneNumber()
+ */
+ public void removePhoneNumber(String phoneNumber){
+ getPhoneNumbers().remove(phoneNumber);
+ }
+
+ /**
+ * Returns the list of strings representing the telefax numbers
+ * included in <i>this</i> contact.
+ */
+ public List<String> getFaxNumbers(){
+ if(this.faxNumbers == null) {
+ this.faxNumbers = new ArrayList<>();
+ }
+ return this.faxNumbers;
+ }
+
+ /**
+ * @see #getFaxNumbers()
*/
- public String getFax(){
- return this.fax;
+ public void addFaxNumber(String faxNumber){
+ getFaxNumbers().add(faxNumber);
}
/**
- * @see #getFax()
+ * Removes one element from the list of telefax numbers of <i>this</i> contact.
+ *
+ * @param faxNumber the telefax number of <i>this</i> contact which should be deleted
+ * @see #getFaxNumber()
*/
- public void setFax(String fax){
- this.fax = fax;
+ public void removeFaxNumber(String faxNumber){
+ getFaxNumbers().remove(faxNumber);
}
-}
\ No newline at end of file
+//*********************** CLONE ********************************************************/
+
+ /**
+ * Clones <i>this</i> Contact. This is a shortcut that enables to create
+ * a new instance that differs only slightly from <i>this</i> Contact.
+ *
+ *
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Contact clone() {
+ try{
+ Contact result = (Contact) super.clone();
+ result.addresses = new HashSet<>();
+ for (Address adr : this.addresses){
+ result.addAddress(adr.clone());
+ }
+ //no changes to emailAdresses, faxnumbers, phonenumbers, urls
+ return result;
+ }catch (CloneNotSupportedException e){
+ logger.warn("Object does not implement cloneable");
+ e.printStackTrace();
+ return null;
+ }
+ }
+}