/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2003 - 2007 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.syncclient.blackberry.parser;
import java.util.Hashtable;
import java.util.Vector;
import net.rim.blackberry.api.pdap.BlackBerryContact;
//import javax.microedition.pim.Contact;
import net.rim.blackberry.api.pdap.BlackBerryContactList;
//import javax.microedition.pim.ContactList;
import net.rim.device.api.util.StringUtilities;
import javax.microedition.pim.PIMItem;//the corresponding RIM's PIMItem class is deprecated
import com.funambol.syncclient.common.StringTools;
import com.funambol.syncclient.util.StaticDataHelper;
import com.funambol.syncclient.spds.SyncException;
import net.rim.device.api.ui.component.Dialog;
import javax.microedition.pim.PIMList;
import javax.microedition.pim.PIMException;
/**
* This is not, despite the name, only a parser,
* but rather a formatter with added parsing
* functionalities. For more information about
* the Contact fields' values see
* {@link http://www.j2medev.com/api/pim/constant-values.html}
*/
public class XMLContactParser extends XMLParser implements ContactParser {
/*
* the values are the SIF-C elements' tag names
*/
private static final String NOTE = "Body";//BlackBerryContact.NOTE
private static final String BUSINESS_TEL2 = "Business2TelephoneNumber";
private static final String BUSINESS_CITY = "BusinessAddressCity";
private static final String BUSINESS_COUNTRY = "BusinessAddressCountry";
private static final String BUSINESS_PO = "BusinessAddressPostalCode";
private static final String BUSINESS_STATE = "BusinessAddressState";
private static final String BUSINESS_STREET = "BusinessAddressStreet";
private static final String BUSINESS_FAX = "BusinessFaxNumber";//BlackBerryContact.TEL + BlackBerryContact.ATTR_FAX
private static final String BUSINESS_TEL = "BusinessTelephoneNumber";//BlackBerryContact.TEL + BlackBerryContact.ATTR_WORK
private static final String CATEGORIES = "Categories";
private static final String COMPANY = "CompanyName";//BlackBerryContact.ORG
private static final String EMAIL_1 = "Email1Address";//BlackBerryContact.EMAIL
private static final String FILE_AS = "FileAs";
private static final String FIRST_NAME = "FirstName";//BlackBerryContact.NAME_GIVEN
private static final String HOME_CITY = "HomeAddressCity";
private static final String HOME_COUNTRY = "HomeAddressCountry";
private static final String HOME_PO = "HomeAddressPostalCode";
private static final String HOME_STATE = "HomeAddressState";
private static final String HOME_STREET = "HomeAddressStreet";
private static final String HOME_FAX = "HomeFaxNumber";
private static final String HOME_TEL = "HomeTelephoneNumber";//BlackBerryContact.TEL + BlackBerryContact.ATTR_HOME
private static final String INSTANT_MESSENGER = "IstantMessenger";
private static final String TITLE = "JobTitle";//BlackBerryContact.TITLE
private static final String LAST_NAME = "LastName";//BlackBerryContact.NAME_FAMILY
private static final String MOBILE_TEL = "MobileTelephoneNumber";//BlackBerryContact.TEL + BlackBerryContact.ATTR_MOBILE
private static final String PREFIX_TITLE = "Title";//Bug 856 BlackBerryContact.NAME_PREFIX
private static final String OTHER = "OtherTelephoneNumber";//Bug 856 the field ist not supported by RIM
private static final String PAGER_NUMBER = "PagerNumber";//Bug 856 BlackBerryContact.ATTR_PAGER
private static final String WEB_PAGE = "WebPage";//BlackBerryContact.URL
private static final String HIGHER_OS_4_0 = "4.0.2";
private static final String HIGHER_OS_4_1 = "4.1";
/**
* This ordered list is used in the
* method #buildMapFromXML(String)
*/
private String[] tagArray = {
BUSINESS_FAX, //<BusinessFaxNumber> BlackBerryContact.TEL + BlackBerryContact.ATTR_FAX
BUSINESS_TEL, //<BusinessTelephoneNumber> BlackBerryContact.TEL + BlackBerryContact.ATTR_WORK
BUSINESS_TEL2,
BUSINESS_CITY,
BUSINESS_COUNTRY,
BUSINESS_PO,
BUSINESS_STATE,
BUSINESS_STREET,
CATEGORIES, //<Categories>
COMPANY, //<CompanyName> BlackBerryContact.ORG
EMAIL_1, //<Email1Address> BlackBerryContact.EMAIL
FILE_AS, //<FileAs>
FIRST_NAME, //<FirstName> BlackBerryContact.NAME_GIVEN
HOME_CITY,
HOME_COUNTRY,
HOME_FAX,
HOME_PO,
HOME_STATE,
HOME_STREET,
HOME_TEL, //<HomeTelephoneNumber> BlackBerryContact.TEL + BlackBerryContact.ATTR_HOME
INSTANT_MESSENGER,
LAST_NAME, //<LastName> BlackBerryContact.NAME_FAMILY
MOBILE_TEL, //<MobileTelephoneNumber> BlackBerryContact.TEL + BlackBerryContact.ATTR_MOBILE
NOTE, //<Body> BlackBerryContact.NOTE
OTHER, //<OtherTelephoneNumber> NOT SUPPORTED BY RIM
PAGER_NUMBER, //<PagerNumber> BlackBerryContact.TEL + BlackBerryContact.ATTR_PAGER
PREFIX_TITLE, //<Title> BlackBerryContact.NAME_PREFIX
TITLE, //<JobTitle> BlackBerryContact.TITLE
WEB_PAGE //<WebPage> BlackBerryContact.URL
};
private String[] telAttrs = {
BUSINESS_FAX, //<BusinessFaxNumber>
BUSINESS_TEL, //<BusinessTelephoneNumber>
HOME_TEL, //<HomeTelephoneNumber>
MOBILE_TEL, //<MobileTelephoneNumber>
PAGER_NUMBER //<PagerNumber>
};
private BlackBerryContactList list;
private BlackBerryContact contact;
private boolean modify;
/**
* The initialization constructor
*
* @param BlackBerryContactList A BlackBerry contacts list
* @param BlackBerryContact A BlackBerry contact
* @param boolean To modify or not to modify: that is the question...
*/
public XMLContactParser(BlackBerryContactList list,
BlackBerryContact contact,
boolean modify) {
this.list = list;
this.contact = contact;
this.modify = modify;
START_MARKER = "<contact>";
END_MARKER = "</contact>";
}
/**
* <p>This method is invoked by ContactDataStore.setRecord
* (itself invoked by SyncManagerImpl.processModifications)
* to obtain a BlackBerryContact object from a contact String
* in SIF-C format</p>
*
* <p>If the <code>modify</code> flag (passed by creating the
* XMLContactParser within the ParserFactory) is set to true,
* the contact is modified by invoking the {@link #modifyContact(String)}
* method</p>
*
* <p>If the <code>modify</code> flag (passed by creating the
* XMLContactParser within the ParserFactory) is set to false,
* a contact is simply added to the BlackBerry address book by
* invoking the {@link #addContact(String)} method</p>
*
* @param String String containing contact information to be
* parsed
* @return The BlackBerryContact that has to be added/modified
* in the BlackBerry address book
*/
public BlackBerryContact parseContact(String contactString) throws SyncException {
if (modify) {
return modifyContact(contactString);
} else {
return addContact(contactString);
}
}
/**
* Invoked to obtain a BlackBerryContact
* object from a contact String coming from the server
* (in SIF-C XML format), when a contact has to be added
* to the BlackBerry address book. This method adds a new
* contact coming from the server to the data store and
* returns a reference to that object
*
* @param String String containing the contact
* information to be parsed in
* SIF-C format
* @return The BlackBerryContact objec that has
* to be added
*/
public BlackBerryContact addContact(String contactString) throws SyncException {
Hashtable contactMap = buildMapFromXML(contactString);
if (contactMap != null) {
try {
StaticDataHelper sdh = null;
String osVersion = null;
sdh = new StaticDataHelper();
osVersion = sdh.getOS();
/*
* This array is added here below to the contact
* through BlackBerryContact.NAME (so you can see
* BlackBerryContact.NAME as BlackBerryContact.NAME_FAMILY
* + BlackBerryContact.NAME_GIVEN)
*/
String[] arrayField = new String[5];
//the contactMap comes from the buildMapFromXML method
arrayField[BlackBerryContact.NAME_FAMILY] = getValue(contactMap, LAST_NAME);//LastName
arrayField[BlackBerryContact.NAME_GIVEN] = getValue(contactMap, FIRST_NAME);//FirstName
arrayField[BlackBerryContact.NAME_PREFIX] = getValue(contactMap, PREFIX_TITLE);//Title
//'list' is the BlackBerryContactList passed from the ParserFactory
if (list.isSupportedField(BlackBerryContact.NAME)) {
//'contact' is the BlackBerryContact passed to the constructor from the ParserFactory
contact.addStringArray(BlackBerryContact.NAME, PIMItem.ATTR_NONE, arrayField);//see above
}
String field = getValue(contactMap, EMAIL_1);//Email1Address
if (isSupportedField(BlackBerryContact.EMAIL, field)) {
contact.addString(BlackBerryContact.EMAIL, BlackBerryContact.ATTR_NONE, field);
}
field = getValue(contactMap, NOTE);//Body
if (isSupportedField(BlackBerryContact.NOTE, field)) {
contact.addString(BlackBerryContact.NOTE, BlackBerryContact.ATTR_NONE, field);
}
field = getValue(contactMap, TITLE);//JobTitle
if (isSupportedField(BlackBerryContact.TITLE, field)) {
contact.addString(BlackBerryContact.TITLE, BlackBerryContact.ATTR_NONE, field);
}
field = getValue(contactMap, COMPANY);//CompanyName
if (isSupportedField(BlackBerryContact.ORG, field)) {
contact.addString(BlackBerryContact.ORG, BlackBerryContact.ATTR_NONE, field);
}
field = getValue(contactMap, WEB_PAGE);//WebPage
if (isSupportedField(BlackBerryContact.URL, field)) {
contact.addString(BlackBerryContact.URL, BlackBerryContact.ATTR_NONE, field);
}
field = getValue(contactMap, HOME_TEL);//HomeTelephoneNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_HOME, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_HOME, field);
}
field = getValue(contactMap, OTHER);//OtherTelephoneNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_OTHER, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_OTHER, field);
}
field = getValue(contactMap, MOBILE_TEL);//MobileTelephoneNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_MOBILE, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_MOBILE, field);
}
field = getValue(contactMap, BUSINESS_TEL);//BusinessTelephoneNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_WORK, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_WORK, field);
}
field = getValue(contactMap, BUSINESS_FAX);//BusinessFaxNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_FAX, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_FAX, field);
}
field = getValue(contactMap, PAGER_NUMBER);//PagerNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_PAGER, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_PAGER, field);
}
field = getValue(contactMap, CATEGORIES);//Categories
//String[] categories = StringUtilities.stringToWords(field);
int startIndex = 0;
String category = field;
final String sep=";";
do {
int i = category.indexOf(sep, startIndex);
if (i != -1) {
addCategory(category.substring(startIndex, i), contact);
category = category.substring(i + 1);
startIndex = i + 1;
}
} while (category.indexOf(sep, startIndex) != -1);
if (category.trim().length() > 0) {
addCategory(category, contact);
}
//for (int j = 0; j < categories.length; j++)
//{
// contact.addToCategory(categories[j]);
//}
//if the OS version is 4.0.2 upward
if (checkOS(osVersion)) {
//in case field BlackBerryContact.ADDR is supported with work (business) attributes...
if (list.isSupportedField(BlackBerryContact.ADDR)) {
arrayField = new String[7];
arrayField[BlackBerryContact.ADDR_COUNTRY] = getValue(contactMap, BUSINESS_COUNTRY);
arrayField[BlackBerryContact.ADDR_STREET] = getValue(contactMap, BUSINESS_STREET);
arrayField[BlackBerryContact.ADDR_REGION] = getValue(contactMap, BUSINESS_STATE);
arrayField[BlackBerryContact.ADDR_LOCALITY] = getValue(contactMap, BUSINESS_CITY);
arrayField[BlackBerryContact.ADDR_POSTALCODE] = getValue(contactMap, BUSINESS_PO);
StaticDataHelper.log("[DEBUG]In XMLContactParser.addContact() -> setting arrayField with BUSINESS ADDRESS: " + StringTools.dump(arrayField));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_WORK,
arrayField);
}
//in case the field BlackBerryContact.ADDR supports the Home attributes too...
if (list.isSupportedField(BlackBerryContact.ADDR) &&
list.isSupportedAttribute(BlackBerryContact.ADDR, BlackBerryContact.ATTR_HOME)) {
arrayField = new String[7];
arrayField[BlackBerryContact.ADDR_COUNTRY] = getValue(contactMap, HOME_COUNTRY);
arrayField[BlackBerryContact.ADDR_STREET] = getValue(contactMap, HOME_STREET);
arrayField[BlackBerryContact.ADDR_REGION] = getValue(contactMap, HOME_STATE);
arrayField[BlackBerryContact.ADDR_LOCALITY] = getValue(contactMap, HOME_CITY);
arrayField[BlackBerryContact.ADDR_POSTALCODE] = getValue(contactMap, HOME_PO);
StaticDataHelper.log("[DEBUG]In XMLContactParser.addContact() -> arrayField set wit HOME ADDRESS: " + StringTools.dump(arrayField));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_HOME,
arrayField);
}
}
return contact;
} catch (Exception e) {
StaticDataHelper.log("[CARD]Exception in XMLContactParser.addContact(): " + e.toString());
e.printStackTrace();
return null;
}
}
/*
* when contactMap is null (i.e.,
* when wether <contact> nor </contact>
* tags are found in the data section
* of the SyncML message by the
* buildMapFromXML() method)
*/
return null;
}
private void addCategory(String category, PIMItem item) {
try {
PIMList list = item.getPIMList();
if (list.isCategory(category)) {
item.addToCategory(category);
} else {
if ((list.maxCategories() == -1)||(list.maxCategories() > list.getCategories().length)) {
list.addCategory(category);
item.addToCategory(category);
}
}
} catch (PIMException e) {
StaticDataHelper.log("[CARD]Exception in XMLContactParser.addCategory(): " + e.toString());
e.printStackTrace();
}
}
/**
* Invoked to obtain a BlackBerryContact
* object from a contact String coming from
* the server, when a contact has to be modified
* in the BlackBerry address book. This method
* modifies the object and returns a reference
* to that object
*
* @param String The string containing contact
* information to be parsed
* @return The BlackBerryContact contact that
* has been modified on the server
*/
public BlackBerryContact modifyContact(String contactString) throws SyncException {
String[] arrayFieldWork = null;
String[] arrayFieldHome = null;
Hashtable contactMap = buildMapFromXML(contactString);
StaticDataHelper sdh = new StaticDataHelper();
String osVersion = sdh.getOS();
if (contactMap != null) {
try {
String[] arrayField = new String[5];
arrayField[BlackBerryContact.NAME_FAMILY] = getValue(contactMap, LAST_NAME);
arrayField[BlackBerryContact.NAME_GIVEN] = getValue(contactMap, FIRST_NAME);
arrayField[BlackBerryContact.NAME_PREFIX] = getValue(contactMap, PREFIX_TITLE);
/*
* since you can't add data to a field that already contains data,
* invoke countValues() to determine whether the field is empty. If
* the field is not empty, invoke removeValue() to remove the data
* from the field before you add new data. But you can set an
* existing string array value in that field, like here
*/
if (contact.countValues(BlackBerryContact.NAME) > 0) {
contact.setStringArray(BlackBerryContact.NAME, 0, BlackBerryContact.ATTR_NONE, arrayField);
} else {
contact.addStringArray(BlackBerryContact.NAME, BlackBerryContact.ATTR_NONE, arrayField);
}
String field = getValue(contactMap, EMAIL_1);
if (isSupportedField(BlackBerryContact.EMAIL, field)) {
if (contact.countValues(BlackBerryContact.EMAIL) > 0) {
contact.setString(BlackBerryContact.EMAIL, 0, BlackBerryContact.ATTR_NONE, field);
} else {
contact.addString(BlackBerryContact.EMAIL, BlackBerryContact.ATTR_NONE, field);
}
}
field = getValue(contactMap, NOTE);
if (isSupportedField(BlackBerryContact.NOTE, field)) {
if (contact.countValues(BlackBerryContact.NOTE) > 0) {
contact.removeValue(BlackBerryContact.NOTE, 0);
contact.addString(BlackBerryContact.NOTE, BlackBerryContact.ATTR_NONE, field);
} else {
contact.addString(BlackBerryContact.NOTE, BlackBerryContact.ATTR_NONE, field);
}
}
field = getValue(contactMap, COMPANY);
if (isSupportedField(BlackBerryContact.ORG, field)) {
if (contact.countValues(BlackBerryContact.ORG) > 0) {
contact.setString(BlackBerryContact.ORG, 0, BlackBerryContact.ATTR_NONE, field);
} else {
contact.addString(BlackBerryContact.ORG, BlackBerryContact.ATTR_NONE, field);
}
}
field = getValue(contactMap, TITLE);//<JobTitle>
if (isSupportedField(BlackBerryContact.TITLE, field) && contact.countValues(BlackBerryContact.TITLE) > 0) {
contact.setString(BlackBerryContact.TITLE, 0, BlackBerryContact.ATTR_NONE, field);
} else {
contact.addString(BlackBerryContact.TITLE, BlackBerryContact.ATTR_NONE, field);
}
if (isSupportedField(BlackBerryContact.URL, field) && contact.countValues(BlackBerryContact.URL) > 0) {
contact.removeValue(BlackBerryContact.URL, 0);
field = getValue(contactMap, WEB_PAGE);
contact.addString(BlackBerryContact.URL, BlackBerryContact.ATTR_NONE, field);
}
int telValues = contact.countValues(BlackBerryContact.TEL);
for (int i = 0; i < telValues; i++) {
contact.removeValue(BlackBerryContact.TEL, 0);
}
field = getValue(contactMap, HOME_TEL);
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_HOME, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_HOME, field);
}
field = getValue(contactMap, MOBILE_TEL);
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_MOBILE, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_MOBILE, field);
}
field = getValue(contactMap, OTHER);//OtherTelephoneNumber
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_OTHER, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_OTHER, field);
}
field = getValue(contactMap, BUSINESS_TEL);
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_WORK, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_WORK, field);
}
field = getValue(contactMap, BUSINESS_FAX);
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_FAX, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_FAX, field);
}
field = getValue(contactMap, PAGER_NUMBER);//<PagerNumber>
if (isSupportedAttributedField(BlackBerryContact.TEL, BlackBerryContact.ATTR_PAGER, field)) {
contact.addString(BlackBerryContact.TEL, BlackBerryContact.ATTR_PAGER, field);
}
field = getValue(contactMap, CATEGORIES);//<Categories>
String[] categories = StringUtilities.stringToWords(field);
/*
* first wipe all categories already on the device...
*/
String[] alreadyOnDevicePresentCategories = contact.getCategories();
for (int i = 0; i < alreadyOnDevicePresentCategories.length; i++) {
contact.removeFromCategory(alreadyOnDevicePresentCategories[i]);
}
/*
* ... and then add the new ones (they're
* incoming, and represent always the last
* will of the user)
*/
for (int j = 0; j < categories.length; j++) {
contact.addToCategory(categories[j]);
}
//only for OS 4.0.2 upward
//FIXME: every OS version has at least the Business address!
if (checkOS(osVersion)) {
//when the field BlackBerryContact.ADDR is supported with work (business) attributes...
if (list.isSupportedField(BlackBerryContact.ADDR)) {
arrayFieldWork = new String[7];//very important: the length must be 7
arrayFieldWork[BlackBerryContact.ADDR_COUNTRY] = getValue(contactMap, BUSINESS_COUNTRY);
arrayFieldWork[BlackBerryContact.ADDR_STREET] = getValue(contactMap, BUSINESS_STREET);
arrayFieldWork[BlackBerryContact.ADDR_REGION] = getValue(contactMap, BUSINESS_STATE);
arrayFieldWork[BlackBerryContact.ADDR_LOCALITY] = getValue(contactMap, BUSINESS_CITY);
arrayFieldWork[BlackBerryContact.ADDR_POSTALCODE] = getValue(contactMap, BUSINESS_PO);
}
//when the field BlackBerryContact.ADDR supports the Home attributes too...
if (list.isSupportedField(BlackBerryContact.ADDR) &&
list.isSupportedAttribute(BlackBerryContact.ADDR, BlackBerryContact.ATTR_HOME)) {
arrayFieldHome = new String[7];//very important: the length must be 7
arrayFieldHome[BlackBerryContact.ADDR_COUNTRY] = getValue(contactMap, HOME_COUNTRY);
arrayFieldHome[BlackBerryContact.ADDR_STREET] = getValue(contactMap, HOME_STREET);
arrayFieldHome[BlackBerryContact.ADDR_REGION] = getValue(contactMap, HOME_STATE);
arrayFieldHome[BlackBerryContact.ADDR_LOCALITY] = getValue(contactMap, HOME_CITY);
arrayFieldHome[BlackBerryContact.ADDR_POSTALCODE] = getValue(contactMap, HOME_PO);
}
StaticDataHelper.log("[DEBUG]>>> Count Addr Items: " + contact.countValues(BlackBerryContact.ADDR));
int addressFields = contact.countValues(BlackBerryContact.ADDR);
switch (addressFields) {
/*
* the contact hasn't both home and
* work address already set, we just
* need to add the new values with
* the addStringArray() method without
* removing old data
*/
case 0:
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> adding arrayField with BUSINESS ADDRESS: " + StringTools.dump(arrayFieldWork));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_WORK,
arrayFieldWork);
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> adding arrayField with HOME ADDRESS: " + StringTools.dump(arrayFieldHome));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_HOME,
arrayFieldHome);
break;
/*
* only the work address or the
* home address is set, we need
* to remove the address and then
* to add both work and home address,
* even though one of them is empty
*/
case 1:
contact.removeValue(BlackBerryContact.ADDR, 0);
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> adding arrayField with BUSINESS ADDRESS: " + StringTools.dump(arrayFieldWork));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_WORK,
arrayFieldWork);
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> adding arrayField with HOME ADDRESS: " + StringTools.dump(arrayFieldHome));
contact.addStringArray(BlackBerryContact.ADDR,
BlackBerryContact.ATTR_HOME,
arrayFieldHome);
break;
/*
* both work and home address are
* set, we can modify them with the
* setStringArray() method
*/
case 2:
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> setting arrayField with BUSINESS ADDRESS: " + StringTools.dump(arrayFieldWork));
contact.setStringArray(BlackBerryContact.ADDR, //The field in which the value to change exists
0, //The index of the value to change
BlackBerryContact.ATTR_WORK, //A byte array containing information about this field as int
arrayFieldWork); //The new value
StaticDataHelper.log("[DEBUG]In XMLContactParser.modifyContact() -> setting arrayField with HOME ADDRESS: " + StringTools.dump(arrayFieldHome));
contact.setStringArray(BlackBerryContact.ADDR, //The field in which the value to change exists
1, //The index of the value to change
BlackBerryContact.ATTR_HOME, //A byte array containing information about this field as int
arrayFieldHome); //The new value
break;
default:
//
break;
}
return contact;
}
} catch (Exception e) {
StaticDataHelper.log("[CARD]Exception in XMLContactParser.modifyContact(): " + e.toString());
return null;
}
}
return null;
}
/**
* Transforms the contact information from the storage
* of the device (also known as database) into a SIF-C
* XML string to pass to the server in the \<Data\>
* section of a SyncML message
*
* @param BlackBerryContact A native BlackBerry contact
* wrapped in a Java BlackBerryContact
* class
* @return String All contact information in a string
* (XML SIF-C)
*/
public String toString(BlackBerryContact contact) {
StaticDataHelper.log(">>> Entering XMLContactParser.toString()...");
StringBuffer contactBuffer = new StringBuffer();
StaticDataHelper sdh = null;
String osVersion = null;
sdh = new StaticDataHelper();
osVersion = sdh.getOS();
contactBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
contactBuffer.append("<contact>\n");
/*
* to let Outlook display the name
* on the Contacts start page
*/
//appendFileAs(contactBuffer);
appendName(contactBuffer, contact);
appendField(contactBuffer,
contact,
BlackBerryContact.ORG,//the int native code of this contact field on the device
COMPANY);//<CompanyName></CompanyName>
appendField(contactBuffer,
contact,
BlackBerryContact.NOTE,//the int native code of this contact field on the device
NOTE);//<Body></Body>
appendField(contactBuffer,
contact,
BlackBerryContact.TITLE,//the int native code of this contact field on the device
TITLE);//<JobTitle></JobTitle>
appendField(contactBuffer,
contact,
BlackBerryContact.EMAIL,//the int native code of this contact field on the device
EMAIL_1);//<Email1Address></Email1Address>
appendField(contactBuffer,
contact,
BlackBerryContact.URL,//the int native code of this contact field on the device
WEB_PAGE);//<WebPage></WebPage>
//bugfix 877
appendCategories(contactBuffer);
appendTelephoneFields(contactBuffer, contact);
if (checkOS(osVersion)) {
appendAddress(contactBuffer, contact);
}
contactBuffer.append("</contact>");
sdh.log(">>> returned SIF-C content after fixing bug 856:\n" + contactBuffer.toString());
return contactBuffer.toString();
}
/**
* Appends the contact information
* supported by the BlackBerry to
* a string buffer (uses #appendToContact)
*
* @param StringBuffer String buffer containing contact information
* @param BlackBerryContact A BlackBerry contact
* @param int Type of field (native code of the device's contact field)
* @param String An XML tag for the SIF format
*/
private void appendField(StringBuffer contactBuffer,
BlackBerryContact contact,
int field,
String tag) {
String value = null;
if (list.isSupportedField(field)) {
value = "";
try {
value = contact.getString(field, 0);//
} catch (Exception e) {
StaticDataHelper.log(">>> In XMLContactParser.appendField() --> Field not set: " + tag +
". --> " + e.toString());
}
appendToContact(contactBuffer, tag, value);
}
}
/**
* Appends the contact information
* supported by the BlackBerry to
* a string buffer (used by #appendField)
*
* @param StringBuffer String buffer representing the SIF-C content and
* containing contact information
* @param String Name of tag associated with the contact information
* @param String Value corresponding to the XML element
*/
private void appendToContact(StringBuffer contactBuffer, String tag, String value) {
value = (value == null) ? "" : value;
if (value.length() > 0) {
value = StringTools.escapeXml(value);
}
contactBuffer.append("<").append(tag).append(">").append(value);
contactBuffer.append("</").append(tag).append(">\n");
}
/**
* This method appends the data corresponding
* to the contact's name array
*
* @param StringBuffer String buffer to which data is appended
* @param BlackBerryContact The BlackBerry contact object
*/
private void appendName(StringBuffer buffer, BlackBerryContact contact) {
if (contact.countValues(BlackBerryContact.NAME) > 0) {
String[] name = contact.getStringArray(BlackBerryContact.NAME, 0);
appendToContact(buffer, FIRST_NAME, name[BlackBerryContact.NAME_GIVEN]);//e.g. <FirstName>John</FirstName>
appendToContact(buffer, LAST_NAME, name[BlackBerryContact.NAME_FAMILY]);//e.g. <LastName>Doe</LastName>
appendToContact(buffer, PREFIX_TITLE, name[BlackBerryContact.NAME_PREFIX]);//e.g. <Title>Dr</Title>
} else {
appendToContact(buffer, FIRST_NAME, "");//e.g. <FirstName></FirstName>
appendToContact(buffer, LAST_NAME, "");//e.g. <LastName></LastName>
appendToContact(buffer, PREFIX_TITLE, "");//e.g. <Title></Title>
}
}
/**
* This method appends the data corresponding
* to the contact's telephone numbers array
*
* @param StringBuffer BlackBerry contact list
* @param BlackBerryContact BlackBerry contact
*/
private void appendTelephoneFields(StringBuffer buffer, BlackBerryContact contact) {
/**
* The number of values contained in the <code>BlackBerryContact.TEL</code> field
*/
int totalTelFields = contact.countValues(BlackBerryContact.TEL);
Vector addedAttrs = new Vector();
for (int i = 0; i < totalTelFields; i++) {
String value = contact.getString(BlackBerryContact.TEL, i);
int attr = contact.getAttributes(BlackBerryContact.TEL, i);
String tag = "";
switch (attr) {
case BlackBerryContact.ATTR_MOBILE://16
tag = MOBILE_TEL;//<MobileTelephoneNumber>
break;
case BlackBerryContact.ATTR_FAX://4
tag = BUSINESS_FAX;//<BusinessFaxNumber>
break;
case BlackBerryContact.ATTR_WORK://512
tag = BUSINESS_TEL;//<BusinessTelephoneNumber>
break;
case BlackBerryContact.ATTR_HOME://8
tag = HOME_TEL;//<HomeTelephoneNumber>
break;
case BlackBerryContact.ATTR_PAGER://64
tag = PAGER_NUMBER;//<PagerNumber>
break;
case BlackBerryContact.ATTR_OTHER:
tag = OTHER;//<OtherThelephonNumber>
break;
}
if (!tag.equals("")) {
appendToContact(buffer, tag, value);
addedAttrs.addElement(tag);
}
}
//this is just to create an empty element
//for each element that wasn't added here
//above
for (int i = 0; i < telAttrs.length; i++) {
if (!addedAttrs.contains(telAttrs[i]))//telAttrs is a String array defined above
{
appendToContact(buffer, telAttrs[i], "");
}
}
}
/**
* This method only appends the data corresponding to contact's address details.
*
* @param StringBuffer: Blackberry contact list
* @param BlackBerryContact: Blackberry contact
* @return void
*/
private void appendAddress(StringBuffer buffer, BlackBerryContact contact) {
StaticDataHelper sdh = null;
String osVersion = null;
boolean findHome = false;
boolean findBusiness = false;
sdh = new StaticDataHelper();
osVersion = sdh.getOS();
if (checkOS(osVersion)) {
if (list.isSupportedField(BlackBerryContact.ADDR) && contact.countValues(BlackBerryContact.ADDR) > 0) {
for (int i = 0; i < contact.countValues(BlackBerryContact.ADDR); i++) {
String[] addr = contact.getStringArray(BlackBerryContact.ADDR, i);
if (addr != null && addr.length > 0) {
int addrType = contact.getAttributes(BlackBerryContact.ADDR, i);
if (addrType == BlackBerryContact.ATTR_HOME) {
findHome = true;
appendToContact(buffer, HOME_STREET, addr[BlackBerryContact.ADDR_STREET]);
appendToContact(buffer, HOME_STATE, addr[BlackBerryContact.ADDR_REGION]);
appendToContact(buffer, HOME_CITY, addr[BlackBerryContact.ADDR_LOCALITY]);
appendToContact(buffer, HOME_PO, addr[BlackBerryContact.ADDR_POSTALCODE]);
appendToContact(buffer, HOME_COUNTRY, addr[BlackBerryContact.ADDR_COUNTRY]);
} else {
findBusiness = true;
appendToContact(buffer, BUSINESS_STREET, addr[BlackBerryContact.ADDR_STREET]);
appendToContact(buffer, BUSINESS_STATE, addr[BlackBerryContact.ADDR_REGION]);
appendToContact(buffer, BUSINESS_CITY, addr[BlackBerryContact.ADDR_LOCALITY]);
appendToContact(buffer, BUSINESS_PO, addr[BlackBerryContact.ADDR_POSTALCODE]);
appendToContact(buffer, BUSINESS_COUNTRY, addr[BlackBerryContact.ADDR_COUNTRY]);
}
}
}
}
if (!findHome) {
appendToContact(buffer, HOME_STREET, "");
appendToContact(buffer, HOME_STATE, "");
appendToContact(buffer, HOME_CITY, "");
appendToContact(buffer, HOME_PO, "");
appendToContact(buffer, HOME_COUNTRY, "");
}
if (!findBusiness) {
appendToContact(buffer, BUSINESS_STREET, "");
appendToContact(buffer, BUSINESS_STATE, "");
appendToContact(buffer, BUSINESS_CITY, "");
appendToContact(buffer, BUSINESS_PO, "");
appendToContact(buffer, BUSINESS_COUNTRY, "");
}
}
}
/**
* Returns the value associated with a key in a hashtable
* represented by the passed contactMap. The content is
* trimmed before to be returned. If the value associated
* with the given key is a null object, an empty string
* is returned
*
* @param Hashtable The passed contactMap
* @param String The key to which the associated value is to be found
* @return String The value associated with the key
*/
private String getValue(Hashtable contactMap, String key) {
Object val = contactMap.get(key);
String value = val == null ? "" : val.toString();
return value.trim();
}
/**
* This method checks if the field and attibute are supported by the contact list
* of blackberry address book
*
* @param int: field
* @param int: attribute
* @param String: value
* @return boolean:returns true if both the field and attribute
* associated with it r supported
*/
private boolean isSupportedAttributedField(int field, int attribute, String value) {
return isSupportedField(field, value) && list.isSupportedAttribute(field, attribute);
}
/**
* Checks if the type of field is supported by the contact list
* of the BlackBerry address book
*
* @param int type
* @param String value
* @return boolean returns true if the contact list supports this field
*/
private boolean isSupportedField(int type, String value) {
return list.isSupportedField(type);
}
/**
* This method creates a hashtable with keys and values associated with
* the BlackBerry contact list attributes (picked from the great ordered
* list defined at the top of this class, {@code tagArray})
*
* @param String A SIF-C XML string representing a BlackBerryContact
* from the server
* @return Hashtable
* @throws SyncException if no correct contact element in SIF-C comes from
* the server
*/
/*
private Hashtable buildMapFromXML(String contactString) throws SyncException {
int startMarkerIndex = contactString.indexOf(START_MARKER);//<contact>
int endMarkerIndex = contactString.indexOf(END_MARKER);//</contact>
if (startMarkerIndex == -1 || endMarkerIndex == -1) {
throw new SyncException("Improper data from the server");
//Dialog.inform("Improper data from the server");
//return null;
}
//Check if xml is got an utf-8 encoding in header. TODO: Generalize to other encodings.
boolean is_utf8 = (contactString.substring(0, startMarkerIndex).toLowerCase().indexOf("encoding=\"utf-8\"") != -1);
Hashtable contactMap = new Hashtable();
boolean readNothing = true;
boolean readOpenTag = false;
boolean readData = false;
int openTagPos = 0;
int dataPos = 0;
String tagName = "";
String data = "";
int tagLength = START_MARKER.length();
//TODO: Handle CDATA
for(int i=startMarkerIndex + tagLength; i< endMarkerIndex; i++){
char c = contactString.charAt(i);
switch(c){
case '<':
if(readNothing){
openTagPos = i+1;
readOpenTag = true;
readNothing = false;
}
else if(readData){
int p = contactString.indexOf("</" + tagName + '>', i);
if(p != -1){
data = contactString.substring(dataPos, i);
if(is_utf8){
try{
data = new String(data.getBytes(), "UTF-8");
}
catch (java.io.UnsupportedEncodingException e){
Log.debug(e.toString());
}
}
i += tagName.length() + 2;
readData = false;
dataPos = 0;
readNothing = true;
data = unEscapeXML(data);
//Insert into map
contactMap.put(tagName, data);
}
else {
//Tag mismatch
//TODO:Exception
}
}
else {
//Unexpected <
//TODO:Exception
}
break;
case '/':
if(readOpenTag){
//<Tag />
readOpenTag = false;
readNothing = true;
openTagPos = 0;
i++;
}
break;
case ' ':
if(readOpenTag){
tagName = contactString.substring(openTagPos, i);
int p = contactString.indexOf('>', i);
if(p != -1){
if(contactString.substring(i,p).indexOf('/') != -1){
//<Tag />
readOpenTag = false;
readNothing = true;
openTagPos = 0;
i = p;
}
else {
//Finished reading open tag
i = p;
readOpenTag = false;
readData = true;
dataPos = i+1;
}
}
else {
//Tag not closed
//TODO:Exception
}
}
break;
case '>':
if(readOpenTag){
tagName = contactString.substring(openTagPos, i);
readOpenTag = false;
readData = true;
dataPos = i+1;
}
break;
default:
}
}
return contactMap;
}
*/
/**
* check OS version
* @param handled OS
* @return true if OS >= 4.0.2
*/
private boolean checkOS(String os) {
if (os == null || os.length() == 0) {
StaticDataHelper sdh = new StaticDataHelper();
sdh.setOS();
os = sdh.getOS();
}
if (os == null || !(os.length() > 3)) {
return false;
}
int major = Integer.valueOf(os.substring(0, 1)).intValue();
int middle = Integer.valueOf(os.substring(2, 3)).intValue();
int minor = 0;
if (os.length() > 4) {
minor = Integer.valueOf(os.substring(4, 5)).intValue();
}
if ((major > 4) || (major == 4 && ((middle == 0 && minor >= 2) || middle >= 1))) {
return true;
} else {
return false;
}
}
//bugfixing 877
private void appendCategories(StringBuffer contactBuffer) {
String[] categories = contact.getCategories();
StringBuffer categoriesBuffer = new StringBuffer();
String categoriesContents = null;
if (categories.length >= 1) {
for (int k = 0; k < categories.length; k++) {
categoriesBuffer.append(categories[k]);
if (categories.length > 1 && k < (categories.length - 1)) {
categoriesBuffer.append("; ");//the separator (semicolon + blank)
}
}
//e.g. Business; Marketing
categoriesContents = StringTools.escapeXml(categoriesBuffer.toString());
} else {
//no categories at all!
categoriesContents = "";
}
//e.g. <Categories>Business; Marketing</Categories>
contactBuffer.append("<" + CATEGORIES + ">" +
categoriesContents +
"</" + CATEGORIES + ">");
}
/*
* FIXME: we can save the incoming FileAs to a User defined field, and put it back
* when it's present
*/
private void appendFileAs(StringBuffer contactBuffer) {
String name = contact.getStringArray(BlackBerryContact.NAME, 0)[BlackBerryContact.NAME_GIVEN];
String surname = contact.getStringArray(BlackBerryContact.NAME, 0)[BlackBerryContact.NAME_FAMILY];
String comma = ", ";
if (surname == null) {
surname = "";
comma = "";
}
if (name == null) {
name = "";
comma = "";
}
String value = surname + comma + name;//"Reeve, Christopher" | "Reeve" | "Christopher"
if (value.length() > 0) {
value = StringTools.escapeXml(value);
}
contactBuffer.append("<" + FILE_AS + ">" +
value +
"</" + FILE_AS + ">");
}
}