| /** |
| * $RCSfile$ |
| * $Revision$ |
| * $Date$ |
| * |
| * Copyright 2003-2007 Jive Software. |
| * |
| * All rights reserved. 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.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.Iterator; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.jivesoftware.smack.Connection; |
| import org.jivesoftware.smack.PacketCollector; |
| import org.jivesoftware.smack.SmackConfiguration; |
| import org.jivesoftware.smack.XMPPException; |
| import org.jivesoftware.smack.filter.PacketIDFilter; |
| import org.jivesoftware.smack.packet.IQ; |
| import org.jivesoftware.smack.packet.Packet; |
| import org.jivesoftware.smack.packet.XMPPError; |
| import org.jivesoftware.smack.util.StringUtils; |
| |
| /** |
| * 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.jabber.org/jeps/jep-0054.html" target="_blank">JEP-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 { |
| |
| /** |
| * 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 JEP-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() { |
| } |
| |
| /** |
| * 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) { |
| e.printStackTrace(); |
| } |
| |
| 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, "image/jpeg"); |
| } |
| |
| /** |
| * 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 = StringUtils.encodeBase64(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; |
| } |
| |
| /** |
| * 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 StringUtils.decodeBase64(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) { |
| e.printStackTrace(); |
| 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'. Connection should be authenticated |
| * and not anonymous.<p> |
| * <p/> |
| * NOTE: the method is asynchronous and does not wait for the returned value. |
| * |
| * @param connection the Connection to use. |
| * @throws XMPPException thrown if there was an issue setting the VCard in the server. |
| */ |
| public void save(Connection connection) throws XMPPException { |
| checkAuthenticated(connection, true); |
| |
| setType(IQ.Type.SET); |
| setFrom(connection.getUser()); |
| PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(getPacketID())); |
| connection.sendPacket(this); |
| |
| Packet response = collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); |
| |
| // Cancel the collector. |
| collector.cancel(); |
| if (response == null) { |
| throw new XMPPException("No response from server on status set."); |
| } |
| if (response.getError() != null) { |
| throw new XMPPException(response.getError()); |
| } |
| } |
| |
| /** |
| * Load VCard information for a connected user. Connection should be authenticated |
| * and not anonymous. |
| */ |
| public void load(Connection connection) throws XMPPException { |
| checkAuthenticated(connection, true); |
| |
| setFrom(connection.getUser()); |
| doLoad(connection, connection.getUser()); |
| } |
| |
| /** |
| * Load VCard information for a given user. Connection should be authenticated and not anonymous. |
| */ |
| public void load(Connection connection, String user) throws XMPPException { |
| checkAuthenticated(connection, false); |
| |
| setTo(user); |
| doLoad(connection, user); |
| } |
| |
| private void doLoad(Connection connection, String user) throws XMPPException { |
| setType(Type.GET); |
| PacketCollector collector = connection.createPacketCollector( |
| new PacketIDFilter(getPacketID())); |
| connection.sendPacket(this); |
| |
| VCard result = null; |
| try { |
| result = (VCard) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); |
| |
| if (result == null) { |
| String errorMessage = "Timeout getting VCard information"; |
| throw new XMPPException(errorMessage, new XMPPError( |
| XMPPError.Condition.request_timeout, errorMessage)); |
| } |
| if (result.getError() != null) { |
| throw new XMPPException(result.getError()); |
| } |
| } |
| catch (ClassCastException e) { |
| System.out.println("No VCard for " + user); |
| } |
| |
| copyFieldsFrom(result); |
| } |
| |
| public String getChildElementXML() { |
| StringBuilder sb = new StringBuilder(); |
| new VCardWriter(sb).write(); |
| return sb.toString(); |
| } |
| |
| 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 void checkAuthenticated(Connection connection, boolean checkForAnonymous) { |
| if (connection == null) { |
| throw new IllegalArgumentException("No connection was provided"); |
| } |
| if (!connection.isAuthenticated()) { |
| throw new IllegalArgumentException("Connection is not authenticated"); |
| } |
| if (checkForAnonymous && connection.isAnonymous()) { |
| throw new IllegalArgumentException("Connection cannot be anonymous"); |
| } |
| } |
| |
| 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; |
| } |
| |
| public String toString() { |
| return getChildElementXML(); |
| } |
| |
| //============================================================== |
| |
| private class VCardWriter { |
| |
| private final StringBuilder sb; |
| |
| VCardWriter(StringBuilder sb) { |
| this.sb = sb; |
| } |
| |
| public void write() { |
| appendTag("vCard", "xmlns", "vcard-temp", hasContent(), new ContentBuilder() { |
| public void addTagContent() { |
| buildActualContent(); |
| } |
| }); |
| } |
| |
| private void buildActualContent() { |
| if (hasNameField()) { |
| appendN(); |
| } |
| |
| appendOrganization(); |
| appendGenericFields(); |
| appendPhoto(); |
| |
| appendEmail(emailWork, "WORK"); |
| appendEmail(emailHome, "HOME"); |
| |
| appendPhones(workPhones, "WORK"); |
| appendPhones(homePhones, "HOME"); |
| |
| appendAddress(workAddr, "WORK"); |
| appendAddress(homeAddr, "HOME"); |
| } |
| |
| private void appendPhoto() { |
| if (photoBinval == null) |
| return; |
| |
| appendTag("PHOTO", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendTag("BINVAL", photoBinval); // No need to escape photoBinval, as it's already Base64 encoded |
| appendTag("TYPE", StringUtils.escapeForXML(photoMimeType)); |
| } |
| }); |
| } |
| private void appendEmail(final String email, final String type) { |
| if (email != null) { |
| appendTag("EMAIL", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendEmptyTag(type); |
| appendEmptyTag("INTERNET"); |
| appendEmptyTag("PREF"); |
| appendTag("USERID", StringUtils.escapeForXML(email)); |
| } |
| }); |
| } |
| } |
| |
| private void appendPhones(Map<String, String> phones, final String code) { |
| Iterator<Map.Entry<String, String>> it = phones.entrySet().iterator(); |
| while (it.hasNext()) { |
| final Map.Entry<String,String> entry = it.next(); |
| appendTag("TEL", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendEmptyTag(entry.getKey()); |
| appendEmptyTag(code); |
| appendTag("NUMBER", StringUtils.escapeForXML(entry.getValue())); |
| } |
| }); |
| } |
| } |
| |
| private void appendAddress(final Map<String, String> addr, final String code) { |
| if (addr.size() > 0) { |
| appendTag("ADR", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendEmptyTag(code); |
| |
| Iterator<Map.Entry<String, String>> it = addr.entrySet().iterator(); |
| while (it.hasNext()) { |
| final Entry<String, String> entry = it.next(); |
| appendTag(entry.getKey(), StringUtils.escapeForXML(entry.getValue())); |
| } |
| } |
| }); |
| } |
| } |
| |
| private void appendEmptyTag(Object tag) { |
| sb.append('<').append(tag).append("/>"); |
| } |
| |
| private void appendGenericFields() { |
| Iterator<Map.Entry<String, String>> it = otherSimpleFields.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, String> entry = it.next(); |
| appendTag(entry.getKey().toString(), |
| StringUtils.escapeForXML(entry.getValue())); |
| } |
| |
| it = otherUnescapableFields.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, String> entry = it.next(); |
| appendTag(entry.getKey().toString(),entry.getValue()); |
| } |
| } |
| |
| private void appendOrganization() { |
| if (hasOrganizationFields()) { |
| appendTag("ORG", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendTag("ORGNAME", StringUtils.escapeForXML(organization)); |
| appendTag("ORGUNIT", StringUtils.escapeForXML(organizationUnit)); |
| } |
| }); |
| } |
| } |
| |
| private void appendN() { |
| appendTag("N", true, new ContentBuilder() { |
| public void addTagContent() { |
| appendTag("FAMILY", StringUtils.escapeForXML(lastName)); |
| appendTag("GIVEN", StringUtils.escapeForXML(firstName)); |
| appendTag("MIDDLE", StringUtils.escapeForXML(middleName)); |
| } |
| }); |
| } |
| |
| private void appendTag(String tag, String attr, String attrValue, boolean hasContent, |
| ContentBuilder builder) { |
| sb.append('<').append(tag); |
| if (attr != null) { |
| sb.append(' ').append(attr).append('=').append('\'').append(attrValue).append('\''); |
| } |
| |
| if (hasContent) { |
| sb.append('>'); |
| builder.addTagContent(); |
| sb.append("</").append(tag).append(">\n"); |
| } |
| else { |
| sb.append("/>\n"); |
| } |
| } |
| |
| private void appendTag(String tag, boolean hasContent, ContentBuilder builder) { |
| appendTag(tag, null, null, hasContent, builder); |
| } |
| |
| private void appendTag(String tag, final String tagText) { |
| if (tagText == null) return; |
| final ContentBuilder contentBuilder = new ContentBuilder() { |
| public void addTagContent() { |
| sb.append(tagText.trim()); |
| } |
| }; |
| appendTag(tag, true, contentBuilder); |
| } |
| |
| } |
| |
| //============================================================== |
| |
| private interface ContentBuilder { |
| |
| void addTagContent(); |
| } |
| |
| //============================================================== |
| } |
| |