mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-09-27 18:19:33 +02:00
64d134052d
and fix violations.
776 lines
24 KiB
Java
776 lines
24 KiB
Java
/**
|
|
*
|
|
* Copyright 2003-2007 Jive Software.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.jivesoftware.smackx.vcardtemp.packet;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Modifier;
|
|
import java.net.URL;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
|
import org.jivesoftware.smack.packet.IQ;
|
|
import org.jivesoftware.smack.util.StringUtils;
|
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
|
import org.jivesoftware.smackx.vcardtemp.VCardManager;
|
|
import org.jxmpp.jid.BareJid;
|
|
|
|
/**
|
|
* A VCard class for use with the
|
|
* <a href="http://www.jivesoftware.org/smack/" target="_blank">SMACK jabber library</a>.<p>
|
|
* <p/>
|
|
* You should refer to the
|
|
* <a href="http://www.xmpp.org/extensions/jep-0054.html" target="_blank">XEP-54 documentation</a>.<p>
|
|
* <p/>
|
|
* Please note that this class is incomplete but it does provide the most commonly found
|
|
* information in vCards. Also remember that VCard transfer is not a standard, and the protocol
|
|
* may change or be replaced.<p>
|
|
* <p/>
|
|
* <b>Usage:</b>
|
|
* <pre>
|
|
* <p/>
|
|
* // To save VCard:
|
|
* <p/>
|
|
* VCard vCard = new VCard();
|
|
* vCard.setFirstName("kir");
|
|
* vCard.setLastName("max");
|
|
* vCard.setEmailHome("foo@fee.bar");
|
|
* vCard.setJabberId("jabber@id.org");
|
|
* vCard.setOrganization("Jetbrains, s.r.o");
|
|
* vCard.setNickName("KIR");
|
|
* <p/>
|
|
* vCard.setField("TITLE", "Mr");
|
|
* vCard.setAddressFieldHome("STREET", "Some street");
|
|
* vCard.setAddressFieldWork("CTRY", "US");
|
|
* vCard.setPhoneWork("FAX", "3443233");
|
|
* <p/>
|
|
* vCard.save(connection);
|
|
* <p/>
|
|
* // To load VCard:
|
|
* <p/>
|
|
* VCard vCard = new VCard();
|
|
* vCard.load(conn); // load own VCard
|
|
* vCard.load(conn, "joe@foo.bar"); // load someone's VCard
|
|
* </pre>
|
|
*
|
|
* @author Kirill Maximov (kir@maxkir.com)
|
|
*/
|
|
public class VCard extends IQ {
|
|
public static final String ELEMENT = "vCard";
|
|
public static final String NAMESPACE = "vcard-temp";
|
|
|
|
private static final Logger LOGGER = Logger.getLogger(VCard.class.getName());
|
|
|
|
private static final String DEFAULT_MIME_TYPE = "image/jpeg";
|
|
|
|
/**
|
|
* Phone types:
|
|
* VOICE?, FAX?, PAGER?, MSG?, CELL?, VIDEO?, BBS?, MODEM?, ISDN?, PCS?, PREF?
|
|
*/
|
|
private Map<String, String> homePhones = new HashMap<String, String>();
|
|
private Map<String, String> workPhones = new HashMap<String, String>();
|
|
|
|
/**
|
|
* Address types:
|
|
* POSTAL?, PARCEL?, (DOM | INTL)?, PREF?, POBOX?, EXTADR?, STREET?, LOCALITY?,
|
|
* REGION?, PCODE?, CTRY?
|
|
*/
|
|
private Map<String, String> homeAddr = new HashMap<String, String>();
|
|
private Map<String, String> workAddr = new HashMap<String, String>();
|
|
|
|
private String firstName;
|
|
private String lastName;
|
|
private String middleName;
|
|
|
|
private String emailHome;
|
|
private String emailWork;
|
|
|
|
private String organization;
|
|
private String organizationUnit;
|
|
|
|
private String photoMimeType;
|
|
private String photoBinval;
|
|
|
|
/**
|
|
* Such as DESC ROLE GEO etc.. see XEP-0054
|
|
*/
|
|
private Map<String, String> otherSimpleFields = new HashMap<String, String>();
|
|
|
|
// fields that, as they are should not be escaped before forwarding to the server
|
|
private Map<String, String> otherUnescapableFields = new HashMap<String, String>();
|
|
|
|
public VCard() {
|
|
super(ELEMENT, NAMESPACE);
|
|
}
|
|
|
|
/**
|
|
* Set generic VCard field.
|
|
*
|
|
* @param field value of field. Possible values: NICKNAME, PHOTO, BDAY, JABBERID, MAILER, TZ,
|
|
* GEO, TITLE, ROLE, LOGO, NOTE, PRODID, REV, SORT-STRING, SOUND, UID, URL, DESC.
|
|
*/
|
|
public String getField(String field) {
|
|
return otherSimpleFields.get(field);
|
|
}
|
|
|
|
/**
|
|
* Set generic VCard field.
|
|
*
|
|
* @param value value of field
|
|
* @param field field to set. See {@link #getField(String)}
|
|
* @see #getField(String)
|
|
*/
|
|
public void setField(String field, String value) {
|
|
setField(field, value, false);
|
|
}
|
|
|
|
/**
|
|
* Set generic, unescapable VCard field. If unescabale is set to true, XML maybe a part of the
|
|
* value.
|
|
*
|
|
* @param value value of field
|
|
* @param field field to set. See {@link #getField(String)}
|
|
* @param isUnescapable True if the value should not be escaped, and false if it should.
|
|
*/
|
|
public void setField(String field, String value, boolean isUnescapable) {
|
|
if (!isUnescapable) {
|
|
otherSimpleFields.put(field, value);
|
|
}
|
|
else {
|
|
otherUnescapableFields.put(field, value);
|
|
}
|
|
}
|
|
|
|
public String getFirstName() {
|
|
return firstName;
|
|
}
|
|
|
|
public void setFirstName(String firstName) {
|
|
this.firstName = firstName;
|
|
// Update FN field
|
|
updateFN();
|
|
}
|
|
|
|
public String getLastName() {
|
|
return lastName;
|
|
}
|
|
|
|
public void setLastName(String lastName) {
|
|
this.lastName = lastName;
|
|
// Update FN field
|
|
updateFN();
|
|
}
|
|
|
|
public String getMiddleName() {
|
|
return middleName;
|
|
}
|
|
|
|
public void setMiddleName(String middleName) {
|
|
this.middleName = middleName;
|
|
// Update FN field
|
|
updateFN();
|
|
}
|
|
|
|
public String getNickName() {
|
|
return otherSimpleFields.get("NICKNAME");
|
|
}
|
|
|
|
public void setNickName(String nickName) {
|
|
otherSimpleFields.put("NICKNAME", nickName);
|
|
}
|
|
|
|
public String getEmailHome() {
|
|
return emailHome;
|
|
}
|
|
|
|
public void setEmailHome(String email) {
|
|
this.emailHome = email;
|
|
}
|
|
|
|
public String getEmailWork() {
|
|
return emailWork;
|
|
}
|
|
|
|
public void setEmailWork(String emailWork) {
|
|
this.emailWork = emailWork;
|
|
}
|
|
|
|
public String getJabberId() {
|
|
return otherSimpleFields.get("JABBERID");
|
|
}
|
|
|
|
public void setJabberId(String jabberId) {
|
|
otherSimpleFields.put("JABBERID", jabberId);
|
|
}
|
|
|
|
public String getOrganization() {
|
|
return organization;
|
|
}
|
|
|
|
public void setOrganization(String organization) {
|
|
this.organization = organization;
|
|
}
|
|
|
|
public String getOrganizationUnit() {
|
|
return organizationUnit;
|
|
}
|
|
|
|
public void setOrganizationUnit(String organizationUnit) {
|
|
this.organizationUnit = organizationUnit;
|
|
}
|
|
|
|
/**
|
|
* Get home address field.
|
|
*
|
|
* @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET,
|
|
* LOCALITY, REGION, PCODE, CTRY
|
|
*/
|
|
public String getAddressFieldHome(String addrField) {
|
|
return homeAddr.get(addrField);
|
|
}
|
|
|
|
/**
|
|
* Set home address field.
|
|
*
|
|
* @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET,
|
|
* LOCALITY, REGION, PCODE, CTRY
|
|
*/
|
|
public void setAddressFieldHome(String addrField, String value) {
|
|
homeAddr.put(addrField, value);
|
|
}
|
|
|
|
/**
|
|
* Get work address field.
|
|
*
|
|
* @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET,
|
|
* LOCALITY, REGION, PCODE, CTRY
|
|
*/
|
|
public String getAddressFieldWork(String addrField) {
|
|
return workAddr.get(addrField);
|
|
}
|
|
|
|
/**
|
|
* Set work address field.
|
|
*
|
|
* @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET,
|
|
* LOCALITY, REGION, PCODE, CTRY
|
|
*/
|
|
public void setAddressFieldWork(String addrField, String value) {
|
|
workAddr.put(addrField, value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set home phone number.
|
|
*
|
|
* @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF
|
|
* @param phoneNum phone number
|
|
*/
|
|
public void setPhoneHome(String phoneType, String phoneNum) {
|
|
homePhones.put(phoneType, phoneNum);
|
|
}
|
|
|
|
/**
|
|
* Get home phone number.
|
|
*
|
|
* @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF
|
|
*/
|
|
public String getPhoneHome(String phoneType) {
|
|
return homePhones.get(phoneType);
|
|
}
|
|
|
|
/**
|
|
* Set work phone number.
|
|
*
|
|
* @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF
|
|
* @param phoneNum phone number
|
|
*/
|
|
public void setPhoneWork(String phoneType, String phoneNum) {
|
|
workPhones.put(phoneType, phoneNum);
|
|
}
|
|
|
|
/**
|
|
* Get work phone number.
|
|
*
|
|
* @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF
|
|
*/
|
|
public String getPhoneWork(String phoneType) {
|
|
return workPhones.get(phoneType);
|
|
}
|
|
|
|
/**
|
|
* Set the avatar for the VCard by specifying the url to the image.
|
|
*
|
|
* @param avatarURL the url to the image(png,jpeg,gif,bmp)
|
|
*/
|
|
public void setAvatar(URL avatarURL) {
|
|
byte[] bytes = new byte[0];
|
|
try {
|
|
bytes = getBytes(avatarURL);
|
|
}
|
|
catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Error getting bytes from URL: " + avatarURL, e);
|
|
}
|
|
|
|
setAvatar(bytes);
|
|
}
|
|
|
|
/**
|
|
* Removes the avatar from the vCard.
|
|
*
|
|
* This is done by setting the PHOTO value to the empty string as defined in XEP-0153
|
|
*/
|
|
public void removeAvatar() {
|
|
// Remove avatar (if any)
|
|
photoBinval = null;
|
|
photoMimeType = null;
|
|
}
|
|
|
|
/**
|
|
* Specify the bytes of the JPEG for the avatar to use.
|
|
* If bytes is null, then the avatar will be removed.
|
|
* 'image/jpeg' will be used as MIME type.
|
|
*
|
|
* @param bytes the bytes of the avatar, or null to remove the avatar data
|
|
*/
|
|
public void setAvatar(byte[] bytes) {
|
|
setAvatar(bytes, DEFAULT_MIME_TYPE);
|
|
}
|
|
|
|
/**
|
|
* Specify the bytes for the avatar to use as well as the mime type.
|
|
*
|
|
* @param bytes the bytes of the avatar.
|
|
* @param mimeType the mime type of the avatar.
|
|
*/
|
|
public void setAvatar(byte[] bytes, String mimeType) {
|
|
// If bytes is null, remove the avatar
|
|
if (bytes == null) {
|
|
removeAvatar();
|
|
return;
|
|
}
|
|
|
|
// Otherwise, add to mappings.
|
|
String encodedImage = Base64.encodeToString(bytes);
|
|
|
|
setAvatar(encodedImage, mimeType);
|
|
}
|
|
|
|
/**
|
|
* Specify the Avatar used for this vCard.
|
|
*
|
|
* @param encodedImage the Base64 encoded image as String
|
|
* @param mimeType the MIME type of the image
|
|
*/
|
|
public void setAvatar(String encodedImage, String mimeType) {
|
|
photoBinval = encodedImage;
|
|
photoMimeType = mimeType;
|
|
}
|
|
|
|
/**
|
|
* Set the encoded avatar string. This is used by the provider.
|
|
*
|
|
* @param encodedAvatar the encoded avatar string.
|
|
* @deprecated Use {@link #setAvatar(String, String)} instead.
|
|
*/
|
|
@Deprecated
|
|
public void setEncodedImage(String encodedAvatar) {
|
|
setAvatar(encodedAvatar, DEFAULT_MIME_TYPE);
|
|
}
|
|
|
|
/**
|
|
* Return the byte representation of the avatar(if one exists), otherwise returns null if
|
|
* no avatar could be found.
|
|
* <b>Example 1</b>
|
|
* <pre>
|
|
* // Load Avatar from VCard
|
|
* byte[] avatarBytes = vCard.getAvatar();
|
|
* <p/>
|
|
* // To create an ImageIcon for Swing applications
|
|
* ImageIcon icon = new ImageIcon(avatar);
|
|
* <p/>
|
|
* // To create just an image object from the bytes
|
|
* ByteArrayInputStream bais = new ByteArrayInputStream(avatar);
|
|
* try {
|
|
* Image image = ImageIO.read(bais);
|
|
* }
|
|
* catch (IOException e) {
|
|
* e.printStackTrace();
|
|
* }
|
|
* </pre>
|
|
*
|
|
* @return byte representation of avatar.
|
|
*/
|
|
public byte[] getAvatar() {
|
|
if (photoBinval == null) {
|
|
return null;
|
|
}
|
|
return Base64.decode(photoBinval);
|
|
}
|
|
|
|
/**
|
|
* Returns the MIME Type of the avatar or null if none is set.
|
|
*
|
|
* @return the MIME Type of the avatar or null
|
|
*/
|
|
public String getAvatarMimeType() {
|
|
return photoMimeType;
|
|
}
|
|
|
|
/**
|
|
* Common code for getting the bytes of a url.
|
|
*
|
|
* @param url the url to read.
|
|
*/
|
|
public static byte[] getBytes(URL url) throws IOException {
|
|
final String path = url.getPath();
|
|
final File file = new File(path);
|
|
if (file.exists()) {
|
|
return getFileBytes(file);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static byte[] getFileBytes(File file) throws IOException {
|
|
BufferedInputStream bis = null;
|
|
try {
|
|
bis = new BufferedInputStream(new FileInputStream(file));
|
|
int bytes = (int) file.length();
|
|
byte[] buffer = new byte[bytes];
|
|
int readBytes = bis.read(buffer);
|
|
if (readBytes != buffer.length) {
|
|
throw new IOException("Entire file not read");
|
|
}
|
|
return buffer;
|
|
}
|
|
finally {
|
|
if (bis != null) {
|
|
bis.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the SHA-1 Hash of the Avatar image.
|
|
*
|
|
* @return the SHA-1 Hash of the Avatar image.
|
|
*/
|
|
public String getAvatarHash() {
|
|
byte[] bytes = getAvatar();
|
|
if (bytes == null) {
|
|
return null;
|
|
}
|
|
|
|
MessageDigest digest;
|
|
try {
|
|
digest = MessageDigest.getInstance("SHA-1");
|
|
}
|
|
catch (NoSuchAlgorithmException e) {
|
|
LOGGER.log(Level.SEVERE, "Failed to get message digest", e);
|
|
return null;
|
|
}
|
|
|
|
digest.update(bytes);
|
|
return StringUtils.encodeHex(digest.digest());
|
|
}
|
|
|
|
private void updateFN() {
|
|
StringBuilder sb = new StringBuilder();
|
|
if (firstName != null) {
|
|
sb.append(StringUtils.escapeForXML(firstName)).append(' ');
|
|
}
|
|
if (middleName != null) {
|
|
sb.append(StringUtils.escapeForXML(middleName)).append(' ');
|
|
}
|
|
if (lastName != null) {
|
|
sb.append(StringUtils.escapeForXML(lastName));
|
|
}
|
|
setField("FN", sb.toString());
|
|
}
|
|
|
|
/**
|
|
* Save this vCard for the user connected by 'connection'. XMPPConnection should be authenticated
|
|
* and not anonymous.
|
|
*
|
|
* @param connection the XMPPConnection to use.
|
|
* @throws XMPPErrorException thrown if there was an issue setting the VCard in the server.
|
|
* @throws NoResponseException if there was no response from the server.
|
|
* @throws NotConnectedException
|
|
* @throws InterruptedException
|
|
* @deprecated use {@link VCardManager#saveVCard(VCard)} instead.
|
|
*/
|
|
@Deprecated
|
|
public void save(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
|
VCardManager.getInstanceFor(connection).saveVCard(this);
|
|
}
|
|
|
|
/**
|
|
* Load VCard information for a connected user. XMPPConnection should be authenticated
|
|
* and not anonymous.
|
|
* @throws XMPPErrorException
|
|
* @throws NoResponseException
|
|
* @throws NotConnectedException
|
|
* @throws InterruptedException
|
|
* @deprecated use {@link VCardManager#loadVCard()} instead.
|
|
*/
|
|
@Deprecated
|
|
public void load(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
|
load(connection, null);
|
|
}
|
|
|
|
/**
|
|
* Load VCard information for a given user. XMPPConnection should be authenticated and not anonymous.
|
|
* @throws XMPPErrorException
|
|
* @throws NoResponseException if there was no response from the server.
|
|
* @throws NotConnectedException
|
|
* @throws InterruptedException
|
|
* @deprecated use {@link VCardManager#loadVCard(BareJid)} instead.
|
|
*/
|
|
@Deprecated
|
|
public void load(XMPPConnection connection, BareJid user) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
|
VCard result = VCardManager.getInstanceFor(connection).loadVCard(user);
|
|
copyFieldsFrom(result);
|
|
}
|
|
|
|
@Override
|
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
|
if (!hasContent()) {
|
|
xml.setEmptyElement();
|
|
return xml;
|
|
}
|
|
xml.rightAngleBracket();
|
|
if (hasNameField()) {
|
|
xml.openElement("N");
|
|
xml.optElement("FAMILY", lastName);
|
|
xml.optElement("GIVEN", firstName);
|
|
xml.optElement("MIDDLE", middleName);
|
|
xml.closeElement("N");
|
|
}
|
|
if (hasOrganizationFields()) {
|
|
xml.openElement("ORG");
|
|
xml.optElement("ORGNAME", organization);
|
|
xml.optElement("ORGUNIT", organizationUnit);
|
|
xml.closeElement("ORG");
|
|
}
|
|
for (Entry<String, String> entry : otherSimpleFields.entrySet()) {
|
|
xml.optElement(entry.getKey(), entry.getValue());
|
|
}
|
|
for (Entry<String, String> entry : otherUnescapableFields.entrySet()) {
|
|
final String value = entry.getValue();
|
|
if (value == null) {
|
|
continue;
|
|
}
|
|
xml.openElement(entry.getKey());
|
|
xml.append(value);
|
|
xml.closeElement(entry.getKey());
|
|
}
|
|
if (photoBinval != null) {
|
|
xml.openElement("PHOTO");
|
|
xml.escapedElement("BINVAL", photoBinval);
|
|
xml.element("TYPE", photoMimeType);
|
|
xml.closeElement("PHOTO");
|
|
}
|
|
if (emailWork != null) {
|
|
xml.openElement("EMAIL");
|
|
xml.emptyElement("WORK");
|
|
xml.emptyElement("INTERNET");
|
|
xml.emptyElement("PREF");
|
|
xml.element("USERID", emailWork);
|
|
xml.closeElement("EMAIL");
|
|
}
|
|
if (emailHome != null) {
|
|
xml.openElement("EMAIL");
|
|
xml.emptyElement("HOME");
|
|
xml.emptyElement("INTERNET");
|
|
xml.emptyElement("PREF");
|
|
xml.element("USERID", emailHome);
|
|
xml.closeElement("EMAIL");
|
|
}
|
|
for (Entry<String, String> phone : workPhones.entrySet()) {
|
|
final String number = phone.getValue();
|
|
if (number == null) {
|
|
continue;
|
|
}
|
|
xml.openElement("TEL");
|
|
xml.emptyElement("WORK");
|
|
xml.emptyElement(phone.getKey());
|
|
xml.element("NUMBER", number);
|
|
xml.closeElement("TEL");
|
|
}
|
|
for (Entry<String, String> phone : homePhones.entrySet()) {
|
|
final String number = phone.getValue();
|
|
if (number == null) {
|
|
continue;
|
|
}
|
|
xml.openElement("TEL");
|
|
xml.emptyElement("HOME");
|
|
xml.emptyElement(phone.getKey());
|
|
xml.element("NUMBER", number);
|
|
xml.closeElement("TEL");
|
|
}
|
|
if (!workAddr.isEmpty()) {
|
|
xml.openElement("ADR");
|
|
xml.emptyElement("WORK");
|
|
for (Entry<String, String> entry : workAddr.entrySet()) {
|
|
final String value = entry.getValue();
|
|
if (value == null) {
|
|
continue;
|
|
}
|
|
xml.element(entry.getKey(), value);
|
|
}
|
|
xml.closeElement("ADR");
|
|
}
|
|
if (!homeAddr.isEmpty()) {
|
|
xml.openElement("ADR");
|
|
xml.emptyElement("HOME");
|
|
for (Entry<String, String> entry : homeAddr.entrySet()) {
|
|
final String value = entry.getValue();
|
|
if (value == null) {
|
|
continue;
|
|
}
|
|
xml.element(entry.getKey(), value);
|
|
}
|
|
xml.closeElement("ADR");
|
|
}
|
|
return xml;
|
|
}
|
|
|
|
private void copyFieldsFrom(VCard from) {
|
|
Field[] fields = VCard.class.getDeclaredFields();
|
|
for (Field field : fields) {
|
|
if (field.getDeclaringClass() == VCard.class &&
|
|
!Modifier.isFinal(field.getModifiers())) {
|
|
try {
|
|
field.setAccessible(true);
|
|
field.set(this, field.get(from));
|
|
}
|
|
catch (IllegalAccessException e) {
|
|
throw new RuntimeException("This cannot happen:" + field, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean hasContent() {
|
|
//noinspection OverlyComplexBooleanExpression
|
|
return hasNameField()
|
|
|| hasOrganizationFields()
|
|
|| emailHome != null
|
|
|| emailWork != null
|
|
|| otherSimpleFields.size() > 0
|
|
|| otherUnescapableFields.size() > 0
|
|
|| homeAddr.size() > 0
|
|
|| homePhones.size() > 0
|
|
|| workAddr.size() > 0
|
|
|| workPhones.size() > 0
|
|
|| photoBinval != null
|
|
;
|
|
}
|
|
|
|
private boolean hasNameField() {
|
|
return firstName != null || lastName != null || middleName != null;
|
|
}
|
|
|
|
private boolean hasOrganizationFields() {
|
|
return organization != null || organizationUnit != null;
|
|
}
|
|
|
|
// Used in tests:
|
|
|
|
public boolean equals(Object o) {
|
|
if (this == o) return true;
|
|
if (o == null || getClass() != o.getClass()) return false;
|
|
|
|
final VCard vCard = (VCard) o;
|
|
|
|
if (emailHome != null ? !emailHome.equals(vCard.emailHome) : vCard.emailHome != null) {
|
|
return false;
|
|
}
|
|
if (emailWork != null ? !emailWork.equals(vCard.emailWork) : vCard.emailWork != null) {
|
|
return false;
|
|
}
|
|
if (firstName != null ? !firstName.equals(vCard.firstName) : vCard.firstName != null) {
|
|
return false;
|
|
}
|
|
if (!homeAddr.equals(vCard.homeAddr)) {
|
|
return false;
|
|
}
|
|
if (!homePhones.equals(vCard.homePhones)) {
|
|
return false;
|
|
}
|
|
if (lastName != null ? !lastName.equals(vCard.lastName) : vCard.lastName != null) {
|
|
return false;
|
|
}
|
|
if (middleName != null ? !middleName.equals(vCard.middleName) : vCard.middleName != null) {
|
|
return false;
|
|
}
|
|
if (organization != null ?
|
|
!organization.equals(vCard.organization) : vCard.organization != null) {
|
|
return false;
|
|
}
|
|
if (organizationUnit != null ?
|
|
!organizationUnit.equals(vCard.organizationUnit) : vCard.organizationUnit != null) {
|
|
return false;
|
|
}
|
|
if (!otherSimpleFields.equals(vCard.otherSimpleFields)) {
|
|
return false;
|
|
}
|
|
if (!workAddr.equals(vCard.workAddr)) {
|
|
return false;
|
|
}
|
|
if (photoBinval != null ? !photoBinval.equals(vCard.photoBinval) : vCard.photoBinval != null) {
|
|
return false;
|
|
}
|
|
|
|
return workPhones.equals(vCard.workPhones);
|
|
}
|
|
|
|
public int hashCode() {
|
|
int result;
|
|
result = homePhones.hashCode();
|
|
result = 29 * result + workPhones.hashCode();
|
|
result = 29 * result + homeAddr.hashCode();
|
|
result = 29 * result + workAddr.hashCode();
|
|
result = 29 * result + (firstName != null ? firstName.hashCode() : 0);
|
|
result = 29 * result + (lastName != null ? lastName.hashCode() : 0);
|
|
result = 29 * result + (middleName != null ? middleName.hashCode() : 0);
|
|
result = 29 * result + (emailHome != null ? emailHome.hashCode() : 0);
|
|
result = 29 * result + (emailWork != null ? emailWork.hashCode() : 0);
|
|
result = 29 * result + (organization != null ? organization.hashCode() : 0);
|
|
result = 29 * result + (organizationUnit != null ? organizationUnit.hashCode() : 0);
|
|
result = 29 * result + otherSimpleFields.hashCode();
|
|
result = 29 * result + (photoBinval != null ? photoBinval.hashCode() : 0);
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|