+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects) that may be empty but not null
+ */
+ public List getTwinItems(Calendar c) throws PIMDBAccessException {
+
+ log.info("\n\n\n=> PIMCalendarDAO getTwinItems begin");
+
+ LinkedList twins = new LinkedList();
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ Date dtStart = null;
+ Date dtEnd = null;
+
+ dtStart = getDateFromString(stringFrom(c.getCalendarContent().getDtStart()));
+ dtEnd = getDateFromString(stringFrom(c.getCalendarContent().getDtEnd()));
+
+ StringBuffer sqlGetCalendarTwinList = new StringBuffer(SQL_GET_EXP_PIM_CALENDAR_ID_LIST_BY_USER);
+
+ String subject = stringFrom(c.getCalendarContent().getSummary(), true); // Empty implies null;
+
+ if ("null".equals(subject)) {
+ subject = null;
+ }
+
+ if (subject == null) {
+ sqlGetCalendarTwinList.append(SQL_AND_NO_SUBJECT_IS_SET);
+ } else {
+ sqlGetCalendarTwinList.append(SQL_AND_SUBJECT_EQUALS_QUESTIONMARK);
+ }
+
+ if (dtStart == null) {
+ sqlGetCalendarTwinList.append(SQL_AND_NO_dtstart_IS_SET);
+ } else {
+ sqlGetCalendarTwinList.append(SQL_AND_dtstart_EQUALS_QUESTIONMARK);
+ }
+
+ if (dtEnd == null) {
+ sqlGetCalendarTwinList.append(SQL_AND_NO_DEND_IS_SET);
+ } else {
+ sqlGetCalendarTwinList.append(SQL_AND_DEND_EQUALS_QUESTIONMARK);
+ }
+
+ sqlGetCalendarTwinList.append(SQL_ORDER_BY_ID);
+
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("\nLooking for items having: ");
+
+ if (subject == null || subject.length() == 0) {
+ sb.append("\n> subject: ");
+ } else {
+ sb.append("\n> subject: '").append(subject).append('\'');
+ }
+ if (dtStart == null) {
+ sb.append("\n> start date: ");
+ } else {
+ sb.append("\n> start date: ").append(dtStart);
+ }
+ if (dtEnd == null) {
+ sb.append("\n> end date: ");
+ } else {
+ sb.append("\n> end date: ").append(dtEnd);
+ }
+
+ log.info(sb.toString());
+
+ ps = con.prepareStatement(sqlGetCalendarTwinList.toString());
+
+ int k = 1;
+
+ ps.setString(k++, userId);
+
+ if (subject != null) {
+ ps.setString(k++, subject);
+ }
+ if (dtStart != null) {
+ ps.setLong(k++, new Long(Long.toString(dtStart.getTime()).substring(0, 10)));
+ }
+ if (dtEnd != null) {
+ ps.setLong(k++, new Long(Long.toString(dtEnd.getTime()).substring(0, 10)));
+ }
+
+ rs = ps.executeQuery();
+
+ long twinId;
+
+ while (rs.next()) {
+
+ twinId = rs.getLong(1); // dend is not relevant in this case
+ log.info("\n\n=> Twin event found: " + twinId);
+
+ twins.add(Long.toString(twinId));
+ }
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("\n=> Error retrieving twin. " + e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ log.info("\n\n\n=> PIMCalendarDAO getTwinItems end");
+
+ return twins;
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ /**
+ * Creates a ContactWrapper object of Event type from a ResultSet.
+ *
+ * @param wrapperId
+ * the UID of the wrapper object to be returned
+ * @param rs
+ * the result of the execution of a proper SQL SELECT statement
+ * on the phpgw_cal table, with the cursor before its first row
+ * @return a newly created ContactWrapper initialized with the fields in the
+ * result set
+ * @throws java.sql.SQLException
+ * @throws NotFoundException
+ */
+ protected static CalendarWrapper createCalendar(String wrapperId, String user_session ,ResultSet rs, String category) throws NotFoundException, Exception {
+
+ if (!rs.next()) {
+ throw new NotFoundException("\n=> No calendar found.");
+ }
+
+ ResultSetMetaData rsmd = rs.getMetaData();
+ int columnCount = rsmd.getColumnCount();
+
+ String column = null;
+
+ String uid = null;
+ String user = null;
+
+ Date dstart = null;
+ Date dend = null;
+
+ uid = String.valueOf(rs.getLong(SQL_FIELD_ID));
+ user = rs.getString(SQL_FIELD_USERID);
+
+ Calendar cal = new Calendar();
+ Reminder r = new Reminder();
+ CalendarContent c = new Event();
+
+ c.setReminder(r);
+ cal.setEvent((Event) c);
+
+ CalendarWrapper cw = new CalendarWrapper(wrapperId, user, cal);
+
+ for (int i = 1; i <= columnCount; i++) {
+
+ column = rsmd.getColumnName(i);
+
+ if (SQL_FIELD_ID.equalsIgnoreCase(column)) {
+ // Does nothing: field already set at construction time
+ } else if (SQL_FIELD_LAST_UPDATE.equalsIgnoreCase(column)) {
+ cw.setLastUpdate(new Timestamp(rs.getLong(i)));
+ } else if (SQL_FIELD_USERID.equalsIgnoreCase(column)) {
+ // Does nothing: field already set at construction time
+ } else if (SQL_FIELD_CATEGORY.equalsIgnoreCase(column)) {
+ c.getCategories().setPropertyValue(category);
+ } else if (SQL_FIELD_STATUS.equalsIgnoreCase(column)) {
+ cw.setStatus(rs.getString(i).charAt(0));
+ } else if (SQL_FIELD_LOCATION.equalsIgnoreCase(column)) {
+ c.getLocation().setPropertyValue(rs.getString(i));
+ } else if (SQL_FIELD_SUBJECT.equalsIgnoreCase(column)) {
+ c.getSummary().setPropertyValue(rs.getString(i));
+ } else if (SQL_FIELD_BODY.equalsIgnoreCase(column)) {
+ c.getDescription().setPropertyValue(rs.getString(i));
+ } else if (SQL_FIELD_DATE_START.equalsIgnoreCase(column)) {
+ if (rs.getLong(i) != 0) {
+ dstart = new Date(new Timestamp(rs.getLong(i) * 1000).getTime());
+ }
+ } else if (SQL_FIELD_DATE_END.equalsIgnoreCase(column)) {
+ if (rs.getLong(i) != 0) {
+ dend = new Date(new Timestamp(rs.getLong(i) * 1000).getTime());
+ }
+ } else if (SQL_FIELD_SENSITIVITY.equalsIgnoreCase(column)) {
+ Short sensitivity = rs.getShort(i);
+ if (sensitivity == 0) {
+ c.getAccessClass().setPropertyValue(new Short((short) 2));
+ } else {
+ c.getAccessClass().setPropertyValue(new Short((short) 0));
+ }
+ }
+ // Unhandled columns are just ignored
+ }
+
+ c.setAllDay(new Boolean(false));
+
+ if (dstart != null) {
+ c.getDtStart().setPropertyValue(getStringFromDateUTC(dstart));
+ }
+
+ if (dend != null) {
+ c.getDtEnd().setPropertyValue(getStringFromDateUTC(dend));
+ }
+
+ if(!user_session.equals(user)){
+ c.getCategories().setPropertyValue("Compartilhado");
+ }
+
+ return cw;
+ }
+
+ /**
+ * Converts date in the DB to a date format palm.
+ *
+ * @param date
+ * it should be in the "yyyyMMdd'T'HHmmss'Z'" format for
+ * yyyy-MM-dd HH:MM:SS
+ * @return a Date object
+ */
+
+ private static String getStringFromDateUTC(Date date) throws Exception {
+
+ SimpleDateFormat utcDateFormatter = new SimpleDateFormat();
+
+ utcDateFormatter.applyPattern(TimeUtils.PATTERN_UTC);
+ utcDateFormatter.setTimeZone(TimeUtils.TIMEZONE_UTC);
+
+ return utcDateFormatter.format(date);
+ }
+
+ /**
+ * Converts a String object representing a date into a corresponding Date
+ * object apt to represent a date in the DB.
+ *
+ * @param date
+ * it should be in the "yyyyMMdd'T'HHmmss'Z'"
+ * format, but will be forced into the right format also if it's
+ * in the "yyyyMMdd'T'HHmmss" format (a 'Z' will be appended) or
+ * in the "yyyy-MM-dd" format (in this case, the time will be considered 00:00:00)
+ * @return a Date object
+ */
+ private static Date getDateFromString(String date) throws ParseException {
+
+ if (date == null || date.length() == 0) {
+ return null;
+ }
+
+ String dateOK = null;
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat();
+ dateFormatter.applyPattern(TimeUtils.PATTERN_UTC);
+
+ String format = TimeUtils.getDateFormat(date);
+
+ if (format.equals(TimeUtils.PATTERN_YYYY_MM_DD)) {
+
+ dateOK = TimeUtils.convertDateFromInDayFormat(date, "000000", true);
+
+ } else if (format.equals(TimeUtils.PATTERN_UTC_WOZ)) {
+
+ dateOK = date + 'Z'; // the non-all-day formatter wants a 'Z'
+ dateFormatter.setTimeZone(TimeUtils.TIMEZONE_UTC);
+
+ } else {
+
+ dateOK = date; // then format should be = TimeUtils.PATTERN_UTC
+ dateFormatter.setTimeZone(TimeUtils.TIMEZONE_UTC);
+ }
+
+ return dateFormatter.parse(dateOK);
+ }
+
+ /**
+ * Attaches the repetion to the recurrence rule of a calendar on the basis
+ * of a ResultSet.
+ *
+ * @param cw
+ * the calendar (as a CalendarWrapper) still lacking information
+ * on the exceptions
+ * @param rs
+ * the result of the execution of a proper SQL SELECT statement
+ * on the phpgw_cal_repeats table, with the cursor before its
+ * first row
+ * @return the CalendarWrapper object with address information attached
+ * @throws Exception
+ */
+ private CalendarWrapper addPIMCalendarExceptions(CalendarWrapper cw, ResultSet rs) throws Exception {
+
+ RecurrencePattern rp = null;
+ short recurrenceType = -1;
+ int interval = 0;
+ short monthOfYear = 0;
+ short dayOfMonth = 0;
+ short dayOfWeekMask = 0;
+ short instance = 0;
+ String startDatePattern = null;
+ String endDatePattern = null;
+ long edate = 0;
+ boolean noend = true;
+ String exceptions_date = null;
+ List exceptions = new Vector();
+
+ if (rs.next()) {
+
+ recurrenceType = Short.parseShort(Long.toString(rs.getLong("recur_type")));
+ interval = Integer.parseInt(Long.toString(rs.getLong("recur_interval")));
+ dayOfWeekMask = Short.parseShort(Long.toString(rs.getLong("recur_data")));
+ startDatePattern = getStringFromDateUTC(new Date(new Timestamp(rs .getLong("datetime") * 1000).getTime()));
+ edate = rs.getLong("recur_enddate");
+ exceptions_date = rs.getString("recur_exception");
+
+ if (edate != 0) {
+ endDatePattern = getStringFromDateUTC(new Date(new Timestamp(edate * 1000).getTime()));
+ noend = false;
+ }
+
+ StringTokenizer exception_date = new StringTokenizer(exceptions_date, ",");
+
+ while (exception_date.hasMoreTokens()) {
+ String date = exception_date.nextToken();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+ ExceptionToRecurrenceRule etrr = new ExceptionToRecurrenceRule(false, sdf.format(new Date(new Long(date) * 1000)).concat("Z"));
+ exceptions.add(etrr);
+ }
+
+ switch (recurrenceType) {
+ case 1: {
+ recurrenceType = 0;
+ break;
+ }
+ case 2: {
+ recurrenceType = 1;
+ break;
+ }
+ case 3: {
+ recurrenceType = 2;
+ break;
+ }
+ case 4: {
+ recurrenceType = 3;
+ break;
+ }
+ case 5: {
+ recurrenceType = 5;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Repeticao do evento
+ rp = new RecurrencePattern(recurrenceType, (interval == 0 ? 1 : interval), monthOfYear, dayOfMonth, dayOfWeekMask, instance, startDatePattern, endDatePattern, noend);
+ rp.setExceptions(exceptions);
+ cw.getCalendar().getCalendarContent().setRecurrencePattern(rp);
+ }
+
+ return cw;
+ }
+
+ /**
+ * Attaches the alarm to the recurrence rule of a calendar on the basis of a
+ * ResultSet.
+ *
+ * @param cw
+ * the calendar (as a CalendarWrapper) still lacking information
+ * on the exceptions
+ * @param rs
+ * the result of the execution of a proper SQL SELECT statement
+ * on the phpgw_async table, with the cursor before its first row
+ * @return the CalendarWrapper object with address information attached
+ * @throws Exception
+ */
+ private CalendarWrapper addPIMCalendarAlarm(CalendarWrapper cw, ResultSet rs) throws Exception {
+
+ if (rs.next()) {
+
+ Reminder rt = null;
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs1 = null;
+
+ Date datea = null;
+ Date dateb = null;
+ long difDatas = 0;
+
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement("SELECT datetime from phpgw_cal WHERE cal_id = ?");
+ ps.setLong(1, Long.parseLong(cw.getId()));
+ rs1 = ps.executeQuery();
+
+ if (rs1.next()) {
+
+ datea = new Date(new Long(rs.getLong("next") * 1000));
+ dateb = new Date(new Long(rs1.getLong("datetime") * 1000));
+ difDatas = dateb.getTime() - datea.getTime();
+
+ rt = new Reminder();
+ rt.setActive(true);
+ rt.setMinutes(new Integer(Integer.parseInt(new Long(Math.abs(difDatas / (60 * 1000))).toString())));
+ cw.getCalendar().getCalendarContent().setReminder(rt);
+
+ }
+
+ DBTools.close(con, ps, rs1);
+ }
+
+ return cw;
+ }
+
+ private String addCategory(String category) throws Exception {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ String id_category = null;
+
+ try {
+
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement("SELECT cat_id from phpgw_categories WHERE cat_owner = -1 AND cat_appname = 'calendar' AND cat_name = ?");
+ ps.setString(1, category);
+ rs = ps.executeQuery();
+
+ if(rs.next()){
+ id_category = rs.getString(1);
+ } else {
+
+ ps = con.prepareStatement("SELECT cat_id from phpgw_categories WHERE cat_owner = ? AND cat_appname = 'calendar' AND cat_name = ?");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setString(2, category);
+ rs = ps.executeQuery();
+
+ if(rs.next()){
+ id_category = rs.getString(1);
+ }
+ }
+
+
+ if(id_category == null){
+
+ ps = con.prepareStatement("INSERT INTO phpgw_categories (cat_owner, cat_access, cat_appname, cat_name, cat_description, cat_data, last_mod) VALUES (?, 'public', 'calendar', ?, '', 'N;', ?)");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setString(2, category);
+ ps.setLong(3, new Long(Long.toString(new Timestamp(System.currentTimeMillis()).getTime()).substring(0, 10)));
+ ps.executeUpdate();
+
+ ps = con.prepareStatement("SELECT cat_id from phpgw_categories WHERE cat_owner = ? AND cat_appname = 'calendar' AND cat_name = ?");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setString(2, category);
+ rs = ps.executeQuery();
+
+ rs.next();
+
+ id_category = rs.getString(1);
+
+ }
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("Error search category <" + category + "> " + e.getMessage());
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ return id_category;
+ }
+
+ private void addRecurrencePattern(RecurrencePattern rp, long id_cal, String sd) throws Exception {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ String dayOfWeekMask = null;
+ String endDatePattern = null;
+ String exception_date = "";
+ String exception_time = "";
+
+ int interval = 0;
+ short recurrenceType = -1;
+ short recur_type = 0;
+
+ try {
+
+ con = getDataSource().getConnection();
+
+ interval = rp.getInterval();
+ recurrenceType = rp.getTypeId();
+ dayOfWeekMask = String.valueOf(rp.getDayOfWeekMask());
+ endDatePattern = rp.getEndDatePattern();
+
+ if (rp.isNoEndDate()) {
+ endDatePattern = "0";
+ } else {
+ endDatePattern = (Long.toString(getDateFromString(endDatePattern).getTime()).substring(0, 10));
+ }
+
+ List exceptions = rp.getExceptions();
+
+ if (!exceptions.isEmpty()) {
+
+ ExceptionToRecurrenceRule etrr = null;
+ exception_time = sd.substring(9, 16); // hora inicial do evento
+
+ int i = 0;
+ int size = exceptions.size();
+
+ for (; i < size - 1; i++) {
+
+ etrr = (ExceptionToRecurrenceRule) exceptions.get(i);
+ exception_date = exception_date + (Long.toString(getDateFromString(etrr.getDate().substring(0, 9).concat(exception_time)).getTime()).substring(0, 10)) + ",";
+ }
+
+ etrr = (ExceptionToRecurrenceRule) exceptions.get(i);
+ exception_date = exception_date + (Long.toString(getDateFromString(etrr.getDate().substring(0, 9).concat(exception_time)).getTime()).substring(0, 10));
+ }
+
+ // Seta o tipo de repetição
+ if (recurrenceType != -1) {
+
+ switch (recurrenceType) {
+ case 0: {
+ recur_type = 1;
+ break;
+ }
+ case 1: {
+ recur_type = 2;
+ break;
+ }
+ case 2: {
+ recur_type = 3;
+ break;
+ }
+ case 3: {
+ recur_type = 4;
+ break;
+ }
+ case 5: {
+ recur_type = 5;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Apaga a repeticao do evento se existir
+ deleteRecurrencePattern(id_cal);
+
+ // Insere no banco de dados a repeticao do evento
+ ps = con.prepareStatement(SQL_INSERT_INTO_EXP_PIM_CALENDAR_REPEATS);
+ ps.setLong(1, id_cal);
+ ps.setLong(2, recur_type);
+ ps.setLong(3, 0);
+ ps.setLong(4, new Long(endDatePattern));
+ ps.setLong(5, interval);
+ ps.setLong(6, new Long(dayOfWeekMask));
+ ps.setString(7, exception_date);
+ ps.executeUpdate();
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("Error event repetitive " + e.getMessage());
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+ private void deleteRecurrencePattern(long id_cal) throws Exception {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement("DELETE FROM phpgw_cal_repeats WHERE cal_id = ?");
+ ps.setLong(1, id_cal);
+ ps.executeUpdate();
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("Error delete event repetitive " + e.getMessage());
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+ private void addReminder(Reminder reminder, long id_cal) throws Exception {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+
+ con = getDataSource().getConnection();
+
+ int minutes = 0;
+ long date = 0;
+
+ minutes = (reminder.getMinutes() * 60);
+ date = new Long(Long.toString(getDateFromString(reminder.getTime()).getTime()).substring(0, 10));
+
+ // Apaga o alarme do evento
+ deleteReminder(id_cal);
+
+ // Insere no banco de dados o alarme do evento
+ ps = con.prepareStatement(SQL_INSERT_INTO_EXP_PIM_ALARM);
+ ps.setString(1, "cal:" + id_cal + ":0"); // id
+ ps.setLong(2, date); // next
+ ps.setString(3, "i:" + date + ";"); // times
+ ps.setString(4, "calendar.bocalendar.send_alarm"); // method
+ ps.setString(5, "a:5:{s:4:\"time\";i:" + date
+ + ";s:6:\"offset\";i:" + minutes + ";s:5:\"owner\";i:"
+ + userId + ";s:7:\"enabled\";i:1;s:6:\"cal_id\";s:"
+ + new Long(id_cal).toString().length() + ":\"" + id_cal
+ + "\";}"); // data
+ ps.setLong(6, Long.parseLong(userId)); // account_id
+ ps.executeUpdate();
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("Error event alarm " + e.getMessage());
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+ private void deleteReminder(long id_cal) throws Exception {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement("DELETE FROM phpgw_async WHERE id = ? AND account_id = ?");
+ ps.setString(1, "cal:" + id_cal + ":0");
+ ps.setLong(2, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("Error delete event alarm " + e.getMessage());
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMContactDAO.java (revision 1009)
@@ -0,0 +1,1346 @@
+/**
+ * This class implements methods to access PIM contact data in a data store.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.items.dao;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import br.com.prognus.psync.exception.PIMDBAccessException;
+import br.com.prognus.psync.items.model.ContactWrapper;
+import br.com.prognus.psync.util.Def;
+
+import com.funambol.common.pim.contact.BusinessDetail;
+import com.funambol.common.pim.contact.Contact;
+import com.funambol.common.pim.contact.Email;
+import com.funambol.common.pim.contact.Name;
+import com.funambol.common.pim.contact.PersonalDetail;
+import com.funambol.common.pim.contact.Phone;
+import com.funambol.framework.security.Sync4jPrincipal;
+import com.funambol.framework.server.store.NotFoundException;
+import com.funambol.framework.tools.DBTools;
+
+public class PIMContactDAO extends PIMEntityDAO {
+
+ // --------------------------------------------------------------- Constants
+
+ private static final String SQL_ORDER_BY_ID = "ORDER BY id_contact";
+
+ private static final String SQL_GET_CONTACT_ID_LIST = "SELECT id_contact FROM phpgw_cc_contact ";
+
+ private static final String SQL_GET_CONTACT_ID_LIST_BY_USER = SQL_GET_CONTACT_ID_LIST
+ + "WHERE id_owner = ?";
+
+ private static final String SQL_GET_CONTACT_BY_ID_USER = "SELECT id_contact, id_owner, last_update, last_status, given_names, family_names, birthdate, category FROM phpgw_cc_contact WHERE id_contact = ? AND id_owner = ? LIMIT 1";
+
+ private static final String SQL_GET_CONTACT_ITEM_BY_ID = "SELECT connection_name, connection_value, id_typeof_contact_connection FROM phpgw_cc_connections AS CO LEFT JOIN phpgw_cc_contact_conns AS CC ON CO.id_connection = CC.id_connection WHERE id_contact = ?";
+
+ private static final String SQL_GET_STATUS_BY_ID_USER_TIME = "SELECT last_status FROM phpgw_cc_contact WHERE id_contact = ? AND id_owner = ? AND last_update > ? LIMIT 1 ";
+
+ private static final String SQL_GET_FNBL_PIM_CONTACT_ID_LIST_BY_USER_TIME_STATUS = SQL_GET_CONTACT_ID_LIST
+ + "WHERE id_owner = ? AND last_update > ? AND last_update < ? AND last_status = ? ";
+
+ private static final String SQL_GET_CONTACT_TWIN_ID_LIST = "SELECT id_contact "
+ + "FROM phpgw_cc_contact "
+ + "WHERE (id_owner = ?) "
+ + "AND ((given_names is null AND (?) = '') "
+ + "OR (lower(given_names) = ?)) "
+ + "AND ((family_names is null AND (?) = '') "
+ + "OR (lower(family_names) = ?)) ";
+
+ private static final String SQL_INSERT_INTO_FNBL_PIM_CONTACT = "INSERT INTO phpgw_cc_contact "
+ + "(id_contact, id_owner, id_status, given_names, family_names, names_ordered, birthdate, category, last_update, last_status ) "
+ + "VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ";
+
+ private static final String SQL_INSERT_INTO_CONTACT_ITEM = "INSERT INTO phpgw_cc_connections "
+ + "(id_connection, connection_name, connection_value, connection_is_default) "
+ + "VALUES (?, ?, ?, ?) ";
+
+ private static final String SQL_ID_MAX_CONNECTIONS = "select max(id_connection) from phpgw_cc_connections";
+
+ private static final String SQL_INSERT_INTO_FNBL_CONNECTIONS = "INSERT INTO phpgw_cc_contact_conns (id_contact, id_connection, id_typeof_contact_connection) VALUES (?, ?, ?)";
+
+ private static final String SQL_UPDATE_FNBL_PIM_CONTACT_BEGIN = "UPDATE phpgw_cc_contact SET ";
+
+ private static final String SQL_UPDATE_FNBL_PIM_CONTACT_END = " WHERE id_contact = ? AND id_owner = ? ";
+
+ private static final String SQL_EQUALS_QUESTIONMARK = " = ?";
+
+ private static final String SQL_EQUALS_QUESTIONMARK_COMMA = " = ?, ";
+
+ protected static final String SQL_FIELD_ID = "id_contact";
+
+ protected static final String SQL_FIELD_USERID = "id_owner";
+
+ protected static final String SQL_FIELD_LAST_UPDATE = "last_update";
+
+ protected static final String SQL_FIELD_STATUS = "last_status";
+
+ protected static final String SQL_FIELD_FIRST_NAME = "given_names";
+
+ protected static final String SQL_FIELD_LAST_NAME = "family_names";
+
+ protected static final String SQL_FIELD_NAME = "names_ordered";
+
+ protected static final String SQL_FIELD_NICK = "alias";
+
+ protected static final String SQL_FIELD_BIRTHDAY = "birthdate";
+
+ protected static final String SQL_FIELD_CATEGORY = "category";
+
+ protected static final int SQL_FIRSTNAME_DIM = 49;
+
+ protected static final int SQL_LASTNAME_DIM = 50;
+
+ protected static final int SQL_EMAIL_DIM = 50;
+
+ protected static final int SQL_PHONE_DIM = 50;
+
+ protected static final int SQL_CATEGORY_DIM = 20;
+
+ protected static final String TYPE_EMAIL_1_ADDRESS = "Principal";
+
+ protected static final String FIELD_EMAIL_1_ADDRESS = "Email1Address";
+
+ protected static final String TYPE_EMAIL_2_ADDRESS = "Alternativo";
+
+ protected static final String FIELD_EMAIL_2_ADDRESS = "OtherEmail2Address";
+
+ protected static final String TYPE_PRIMARY_TELEPHONE_NUMBER = "Principal";
+
+ protected static final String FIELD_PRIMARY_TELEPHONE_NUMBER = "PrimaryTelephoneNumber";
+
+ protected static final String TYPE_HOME_TELEPHONE_NUMBER = "Residencial";
+
+ protected static final String FIELD_HOME_TELEPHONE_NUMBER = "HomeTelephoneNumber";
+
+ protected static final String TYPE_MOBILE_TELEPHONE_NUMBER = "Celular";
+
+ protected static final String FIELD_MOBILE_TELEPHONE_NUMBER = "MobileTelephoneNumber";
+
+ protected static final String TYPE_BUSINESS_TELEPHONE_NUMBER = "Comercial";
+
+ protected static final String FIELD_BUSINESS_TELEPHONE_NUMBER = "BusinessTelephoneNumber";
+
+ protected static final String TYPE_BUSINESS_FAX_NUMBER = "Fax";
+
+ protected static final String FIELD_BUSINESS_FAX_NUMBER = "BusinessFaxNumber";
+
+ protected static final String TYPE_PAGER_NUMBER = "Pager";
+
+ protected static final String FIELD_PAGER_NUMBER = "PagerNumber";
+
+ protected static final String SQL_FIELD_ITEM_NAME = "connection_name";
+
+ protected static final String SQL_FIELD_ITEM_VALUE = "connection_value";
+
+ protected static final String SQL_FIELD_ITEM_TYPE = "id_typeof_contact_connection";
+
+ private Sync4jPrincipal principal = null;
+
+ // -------------------------------------------------------------
+ // Constructors
+
+ /**
+ * @see PIMEntityDAO#PIMEntityDAO(String, String)
+ */
+ public PIMContactDAO(String jndiDataSourceName, Sync4jPrincipal principal) {
+
+ super(jndiDataSourceName, principal.getUsername());
+
+ log.info("\n\n=> Created new PIMContactDAO for data source "
+ + jndiDataSourceName + " and user ID LDAP " + userId
+ + " user login " + principal.getUsername() + "\n");
+
+ this.principal = principal;
+ }
+
+ // ----------------------------------------------------------- Public
+ // methods
+
+ /**
+ * Adds a contact. If necessary, a new ID is generated and set in the
+ * ContactWrapper.
+ *
+ * @param c
+ * as a ContactWrapper object, usually without an ID set.
+ * @throws PIMDBAccessException
+ * @throws SQLException
+ *
+ * @see ContactWrapper
+ */
+ public void addItem(ContactWrapper cw) throws PIMDBAccessException {
+
+ log.info("\n\n=> PIMContactDAO addItem begin\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ long id_contact = 0;
+ long id_connection = 0;
+
+ PersonalDetail personalDetail = null;
+ BusinessDetail businessDetail = null;
+
+ Name name = null;
+ Phone phone = null;
+ Email email = null;
+
+ List emails = new ArrayList();
+ List phones = new ArrayList();
+
+ String phoneType = null;
+ String firstName = null;
+ String lastName = null;
+ String birthday = null;
+ String type = null;
+ String category = null;
+
+ StringBuffer fullName = null;
+ Date anniversary = null;
+
+ try {
+
+ con = getDataSource().getConnection();
+ con.setAutoCommit(false);
+
+ Contact c = cw.getContact();
+ Timestamp lastUpdate = cw.getLastUpdate();
+
+ personalDetail = c.getPersonalDetail();
+ businessDetail = c.getBusinessDetail();
+ name = c.getName();
+
+ lastUpdate = (lastUpdate == null) ? new Timestamp(System
+ .currentTimeMillis()) : lastUpdate;
+
+ if (personalDetail != null) {
+ emails.addAll(personalDetail.getEmails());
+ phones.addAll(personalDetail.getPhones());
+ birthday = personalDetail.getBirthday();
+ category = stringFrom(c.getCategories());
+ }
+
+ if (businessDetail != null) {
+ emails.addAll(businessDetail.getEmails());
+ phones.addAll(businessDetail.getPhones());
+ }
+
+ if (name != null) {
+ firstName = stringFrom(name.getFirstName());
+ lastName = stringFrom(name.getLastName());
+ }
+
+ // consulta o id maximo da tabela contatos
+ ps = con
+ .prepareStatement("select max(id_contact) from phpgw_cc_contact limit 1");
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ long id = rs.getInt(1);
+ id_contact = (id == 0) ? 1 : ++id;
+ }
+
+ cw.setId(Long.toString(id_contact));
+
+ // Formata o nome completo do contato
+ fullName = new StringBuffer();
+
+ if (firstName != null && firstName.length() > 0)
+ fullName.append(truncate(firstName, SQL_FIRSTNAME_DIM));
+ if (lastName != null && lastName.length() > 0) {
+ if (firstName != null && firstName.length() > 0
+ && lastName != null && lastName.length() > 0)
+ fullName.append(" " + truncate(lastName, SQL_LASTNAME_DIM));
+ else
+ fullName.append(truncate(lastName, SQL_LASTNAME_DIM));
+ }
+
+ // Formata a data de aniversario do contato
+ if ((birthday != null) && (!birthday.equals(""))) {
+ anniversary = new java.sql.Date((new SimpleDateFormat(
+ "yyyy-MM-dd").parse(birthday)).getTime());
+ }
+
+ // Prepara os dados para inserir o contato no banco de dados
+ ps = con.prepareStatement(SQL_INSERT_INTO_FNBL_PIM_CONTACT);
+ ps.setLong(1, id_contact); // id_contact
+ ps.setLong(2, Long.parseLong(userId)); // id_owner
+ ps.setLong(3, 1); // id_status
+ ps.setString(4, truncate(firstName, SQL_FIRSTNAME_DIM)); // given_names
+ ps.setString(5, truncate(lastName, SQL_LASTNAME_DIM)); // family_names
+ ps.setString(6, fullName.toString()); // names_ordered
+ ps.setDate(7, anniversary); // birthdate
+ ps.setString(8, truncate(category, SQL_CATEGORY_DIM)); // category
+ ps.setLong(9, lastUpdate.getTime()); // last_update
+ ps.setString(10, String.valueOf(Def.PIM_STATE_NEW)); // last_status
+ ps.executeUpdate();
+
+ // emails
+ if (!emails.isEmpty()) {
+
+ for (int i = 0, l = emails.size(); i < l; i++) {
+
+ email = emails.get(i);
+ Boolean type_email;
+
+ if ((FIELD_EMAIL_1_ADDRESS).equals(email.getEmailType())) {
+ type = TYPE_EMAIL_1_ADDRESS;
+ type_email = true;
+ } else if ((FIELD_EMAIL_2_ADDRESS).equals(email
+ .getEmailType())) {
+ type = TYPE_EMAIL_2_ADDRESS;
+ type_email = false;
+ } else {
+ continue; // Unknown property: saves nothing
+ }
+
+ String emailValue = email.getPropertyValueAsString();
+
+ if (emailValue != null && emailValue.length() != 0) {
+
+ if (emailValue.length() > SQL_EMAIL_DIM) {
+ emailValue = emailValue.substring(0, SQL_EMAIL_DIM);
+ }
+
+ // Verifica o id maximo da tabela phpgw_cc_connections
+ ps = con.prepareStatement(SQL_ID_MAX_CONNECTIONS);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ long id = rs.getInt(1);
+ id_connection = (id == 0) ? 1 : ++id;
+ }
+
+ // Insere os emails do contato na tabela
+ // phpgw_cc_connections
+ ps = con.prepareStatement(SQL_INSERT_INTO_CONTACT_ITEM);
+ ps.setLong(1, id_connection);
+ ps.setString(2, type);
+ ps.setString(3, emailValue);
+ ps.setBoolean(4, type_email);
+ ps.executeUpdate();
+
+ // Insere as referencias de emails do contato na tabela
+ // phpgw_cc_connections
+ ps = con
+ .prepareStatement(SQL_INSERT_INTO_FNBL_CONNECTIONS);
+ ps.setLong(1, id_contact);
+ ps.setLong(2, id_connection);
+ ps.setLong(3, 1);
+ ps.executeUpdate();
+ }
+
+ }
+ }
+
+ // phones
+ if (!phones.isEmpty()) {
+
+ Boolean type_phone = true;
+
+ for (int i = 0, l = phones.size(); i < l; i++) {
+
+ phone = phones.get(i);
+ phoneType = phone.getPhoneType();
+
+ if ((FIELD_PRIMARY_TELEPHONE_NUMBER).equals(phoneType)) {
+ type = TYPE_PRIMARY_TELEPHONE_NUMBER;
+ } else if ((FIELD_HOME_TELEPHONE_NUMBER).equals(phoneType)) {
+ type = TYPE_HOME_TELEPHONE_NUMBER;
+ } else if ((FIELD_MOBILE_TELEPHONE_NUMBER)
+ .equals(phoneType)) {
+ type = TYPE_MOBILE_TELEPHONE_NUMBER;
+ } else if ((FIELD_BUSINESS_TELEPHONE_NUMBER)
+ .equals(phoneType)) {
+ type = TYPE_BUSINESS_TELEPHONE_NUMBER;
+ } else if ((FIELD_BUSINESS_FAX_NUMBER).equals(phoneType)) {
+ type = TYPE_BUSINESS_FAX_NUMBER;
+ } else if ((FIELD_PAGER_NUMBER).equals(phoneType)) {
+ type = TYPE_PAGER_NUMBER;
+ } else {
+ continue; // Unknown property: saves nothing
+ }
+
+ String phoneValue = phone.getPropertyValueAsString();
+
+ if (phoneValue != null && phoneValue.length() != 0) {
+
+ if (phoneValue.length() > SQL_PHONE_DIM) {
+ phoneValue = phoneValue.substring(0, SQL_PHONE_DIM);
+ }
+
+ ps = con.prepareStatement(SQL_ID_MAX_CONNECTIONS);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ long id = rs.getInt(1);
+ id_connection = (id == 0) ? 1 : ++id;
+ }
+
+ // Insere os telefones do contato na tabela
+ // phpgw_cc_connections
+ ps = con.prepareStatement(SQL_INSERT_INTO_CONTACT_ITEM);
+ ps.setLong(1, id_connection);
+ ps.setString(2, type);
+ ps.setString(3, phoneValue);
+ ps.setBoolean(4, type_phone);
+ ps.executeUpdate();
+
+ type_phone = false; // os demais telefones serao setados
+ // como nao padrao
+
+ // Insere as referencias de telefones do contato na
+ // tabela phpgw_cc_connections
+ ps = con
+ .prepareStatement(SQL_INSERT_INTO_FNBL_CONNECTIONS);
+ ps.setLong(1, id_contact);
+ ps.setLong(2, id_connection);
+ ps.setLong(3, 2);
+ ps.executeUpdate();
+ }
+
+ }
+ }
+
+ con.commit();
+ con.setAutoCommit(true);
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("\n=> Error adding contact.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ log.info("\n\n=> Added item with ID " + id_contact + "\n");
+ log.info("\n\n=> PIMContactDAO addItem end\n");
+ }
+
+ /**
+ * Updates a contact.
+ *
+ * @param c
+ * as a ContactWrapper object. If its last update time is null,
+ * then it's set to the current time.
+ * @return the UID of the contact
+ * @throws PIMDBAccessException
+ *
+ * @see ContactWrapper
+ */
+ public String updateItem(ContactWrapper cw) throws PIMDBAccessException {
+
+ log.info("\n\n=> PIMContactDAO updateItem begin\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ long id_connection = 0;
+ long id_contact = 0;
+
+ PersonalDetail personalDetail = null;
+ BusinessDetail businessDetail = null;
+
+ Name name = null;
+ Phone phone = null;
+ Email email = null;
+ String type = null;
+
+ List emails = new ArrayList();
+ List phones = new ArrayList();
+
+ String phoneType = null;
+ StringBuffer queryUpdateFunPimContact = null;
+
+ String firstName = null;
+ String lastName = null;
+ StringBuffer fullName = null;
+ String birthday = null;
+ String category = null;
+ Date anniversary = null;
+
+ try {
+
+ Timestamp lastUpdate = (cw.getLastUpdate() == null) ? new Timestamp(
+ System.currentTimeMillis())
+ : cw.getLastUpdate();
+
+ con = getDataSource().getConnection();
+ con.setAutoCommit(false);
+
+ Contact c = cw.getContact();
+
+ personalDetail = c.getPersonalDetail();
+ businessDetail = c.getBusinessDetail();
+ name = c.getName();
+ id_contact = Long.parseLong(cw.getId());
+
+ if (personalDetail != null) {
+ emails.addAll(personalDetail.getEmails());
+ phones.addAll(personalDetail.getPhones());
+ birthday = personalDetail.getBirthday();
+ category = stringFrom(c.getCategories());
+ }
+
+ if (businessDetail != null) {
+ emails.addAll(businessDetail.getEmails());
+ phones.addAll(businessDetail.getPhones());
+ }
+
+ if (name != null) {
+ firstName = stringFrom(name.getFirstName());
+ lastName = stringFrom(name.getLastName());
+ }
+
+ if (firstName != null && firstName.length() > SQL_FIRSTNAME_DIM) {
+ firstName = firstName.substring(0, SQL_FIRSTNAME_DIM);
+ }
+ if (lastName != null && lastName.length() > SQL_LASTNAME_DIM) {
+ lastName = lastName.substring(0, SQL_LASTNAME_DIM);
+ }
+ if (category != null && category.length() > SQL_CATEGORY_DIM) {
+ category = category.substring(0, SQL_CATEGORY_DIM);
+ }
+
+ // Formata o nome completo do contato
+ fullName = new StringBuffer();
+
+ if (firstName != null && firstName.length() > 0)
+ fullName.append(truncate(firstName, SQL_FIRSTNAME_DIM));
+ if (lastName != null && lastName.length() > 0) {
+ if (firstName != null && firstName.length() > 0
+ && lastName != null && lastName.length() > 0)
+ fullName.append(" " + truncate(lastName, SQL_LASTNAME_DIM));
+ else
+ fullName.append(truncate(lastName, SQL_LASTNAME_DIM));
+ }
+
+ // Formata a data de aniversario do contato
+ if ((birthday != null) && (!birthday.equals(""))) {
+ anniversary = new java.sql.Date((new SimpleDateFormat(
+ "yyyy-MM-dd").parse(birthday)).getTime());
+ } else {
+ anniversary = null;
+ }
+
+ // Prepara os dados do contato para serem atualizados no banco de
+ // dados
+ queryUpdateFunPimContact = new StringBuffer();
+ queryUpdateFunPimContact.append(SQL_UPDATE_FNBL_PIM_CONTACT_BEGIN
+ + SQL_FIELD_LAST_UPDATE + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact.append(SQL_FIELD_FIRST_NAME
+ + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact.append(SQL_FIELD_LAST_NAME
+ + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact.append(SQL_FIELD_NAME
+ + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact.append(SQL_FIELD_BIRTHDAY
+ + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact.append(SQL_FIELD_CATEGORY
+ + SQL_EQUALS_QUESTIONMARK_COMMA);
+ queryUpdateFunPimContact
+ .append(SQL_FIELD_STATUS + SQL_EQUALS_QUESTIONMARK
+ + SQL_UPDATE_FNBL_PIM_CONTACT_END);
+
+ ps = con.prepareStatement(queryUpdateFunPimContact.toString());
+
+ ps.setLong(1, lastUpdate.getTime()); // last_update
+ ps.setString(2, firstName); // given_names
+ ps.setString(3, lastName); // family_names
+ ps.setString(4, fullName.toString()); // names_ordered
+ ps.setDate(5, anniversary); // birthdate
+ ps.setString(6, category); // category
+ ps.setString(7, String.valueOf(Def.PIM_STATE_UPDATED)); // last_status
+ ps.setLong(8, id_contact); // id_contact
+ ps.setLong(9, Long.parseLong(userId)); // id_owner
+ ps.executeUpdate();
+
+ // emails
+
+ // Apaga os emails do contato na tabela phpgw_cc_connections
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_connections WHERE id_connection IN (SELECT id_connection FROM phpgw_cc_contact_conns WHERE id_contact = ? AND id_typeof_contact_connection = 1)");
+ ps.setLong(1, id_contact);
+ ps.executeUpdate();
+
+ // Apaga as referencias de emails do contato na tabela
+ // phpgw_cc_contact_conns
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_conns WHERE id_contact = ? AND id_typeof_contact_connection = 1");
+ ps.setLong(1, id_contact);
+ ps.executeUpdate();
+
+ if (!emails.isEmpty()) {
+
+ boolean type_mail = true;
+
+ for (int i = 0, l = emails.size(); i < l; i++) {
+
+ email = emails.get(i);
+
+ if ((FIELD_EMAIL_1_ADDRESS).equals(email.getEmailType())) {
+ type = TYPE_EMAIL_1_ADDRESS;
+ type_mail = true;
+ } else if ((FIELD_EMAIL_2_ADDRESS).equals(email
+ .getEmailType())) {
+ type = TYPE_EMAIL_2_ADDRESS;
+ type_mail = false;
+ } else {
+ continue; // no save unknown property
+ }
+
+ String emailValue = email.getPropertyValueAsString();
+ emailValue = truncate(emailValue, SQL_EMAIL_DIM);
+
+ if (emailValue != null && emailValue.length() != 0) {
+
+ // Verifica o id maximo da tabela phpgw_cc_connections
+ ps = con.prepareStatement(SQL_ID_MAX_CONNECTIONS);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ long id = rs.getInt(1);
+ id_connection = (id == 0) ? 1 : ++id;
+ }
+
+ // Insere os emails do contato na tabela
+ // phpgw_cc_connections
+ ps = con.prepareStatement(SQL_INSERT_INTO_CONTACT_ITEM);
+ ps.setLong(1, id_connection);
+ ps.setString(2, type);
+ ps.setString(3, emailValue);
+ ps.setBoolean(4, type_mail);
+ ps.executeUpdate();
+
+ // Insere as referencias de emails do contato na tabela
+ // phpgw_cc_connections
+ ps = con
+ .prepareStatement(SQL_INSERT_INTO_FNBL_CONNECTIONS);
+ ps.setLong(1, id_contact);
+ ps.setLong(2, id_connection);
+ ps.setLong(3, 1);
+ ps.executeUpdate();
+ }
+ }
+ }
+
+ // phones
+
+ // Apaga os telefones do contato na tabela phpgw_cc_connections
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_connections WHERE id_connection IN (SELECT id_connection FROM phpgw_cc_contact_conns WHERE id_contact = ? AND id_typeof_contact_connection = 2)");
+ ps.setLong(1, id_contact);
+ ps.executeUpdate();
+
+ // Apaga as referencias de telefones do contato na tabela
+ // phpgw_cc_contact_conns
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_conns WHERE id_contact = ? AND id_typeof_contact_connection = 2");
+ ps.setLong(1, id_contact);
+ ps.executeUpdate();
+
+ if (!phones.isEmpty()) {
+
+ boolean type_fone = true; // Seta o primeiro telefone como
+ // principal no expresso
+
+ for (int i = 0, l = phones.size(); i < l; i++) {
+
+ phone = phones.get(i);
+ phoneType = phone.getPhoneType();
+
+ if ((FIELD_PRIMARY_TELEPHONE_NUMBER).equals(phoneType)) {
+ type = TYPE_PRIMARY_TELEPHONE_NUMBER;
+ } else if ((FIELD_HOME_TELEPHONE_NUMBER).equals(phoneType)) {
+ type = TYPE_HOME_TELEPHONE_NUMBER;
+ } else if ((FIELD_MOBILE_TELEPHONE_NUMBER)
+ .equals(phoneType)) {
+ type = TYPE_MOBILE_TELEPHONE_NUMBER;
+ } else if ((FIELD_BUSINESS_TELEPHONE_NUMBER)
+ .equals(phoneType)) {
+ type = TYPE_BUSINESS_TELEPHONE_NUMBER;
+ } else if ((FIELD_BUSINESS_FAX_NUMBER).equals(phoneType)) {
+ type = TYPE_BUSINESS_FAX_NUMBER;
+ } else if ((FIELD_PAGER_NUMBER).equals(phoneType)) {
+ type = TYPE_PAGER_NUMBER;
+ } else {
+ continue; // Unknown property: saves nothing
+ }
+
+ String phoneValue = phone.getPropertyValueAsString();
+ phoneValue = truncate(phoneValue, SQL_EMAIL_DIM);
+
+ if (phoneValue != null && phoneValue.length() != 0) {
+
+ // Verifica o id maximo da tabela phpgw_cc_connections
+ ps = con.prepareStatement(SQL_ID_MAX_CONNECTIONS);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ long id = rs.getInt(1);
+ id_connection = (id == 0) ? 1 : ++id;
+ }
+
+ // Insere os telefones do contato na tabela
+ // phpgw_cc_connections
+ ps = con.prepareStatement(SQL_INSERT_INTO_CONTACT_ITEM);
+ ps.setLong(1, id_connection);
+ ps.setString(2, type);
+ ps.setString(3, phoneValue);
+ ps.setBoolean(4, type_fone);
+ ps.executeUpdate();
+
+ type_fone = false; // os demais telefones serao setados
+ // como nao padrao
+
+ // Insere as referencias de telefones do contato na
+ // tabela phpgw_cc_connections
+ ps = con
+ .prepareStatement(SQL_INSERT_INTO_FNBL_CONNECTIONS);
+ ps.setLong(1, id_contact);
+ ps.setLong(2, id_connection);
+ ps.setLong(3, 2);
+ ps.executeUpdate();
+ }
+ }
+ }
+
+ con.commit();
+ con.setAutoCommit(true);
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("\n=> Error updating contact.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ log.info("\n\n=> Update item with ID " + id_contact + "\n");
+ log.info("\n\n=> PIMContactDAO updateItem end\n");
+
+ return Long.toString(id_contact);
+ }
+
+ /**
+ * Removes the contact with given UID and sets its last_update field,
+ * provided it has the same userId as this DAO. The deletion is soft
+ * (reversible).
+ *
+ * @param uid
+ * corresponds to the id field in the phpgw_cc_contact table
+ * @throws PIMDBAccessException
+ */
+ public void removeItem(String uid) throws PIMDBAccessException {
+
+ log.info("\n\n=> DAO start removeItem " + uid + "\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+ con.setAutoCommit(false);
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_addresses WHERE id_address IN (SELECT id_address FROM phpgw_cc_contact_addrs WHERE id_contact = ?)");
+ ps.setLong(1, Long.parseLong(uid));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_addrs WHERE id_contact = ?");
+ ps.setLong(1, Long.parseLong(uid));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_connections WHERE id_connection IN (SELECT id_connection FROM phpgw_cc_contact_conns WHERE id_contact = ?)");
+ ps.setLong(1, Long.parseLong(uid));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_conns WHERE id_contact = ?");
+ ps.setLong(1, Long.parseLong(uid));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact WHERE id_contact = ?");
+ ps.setLong(1, Long.parseLong(uid));
+ ps.executeUpdate();
+
+ con.commit();
+ con.setAutoCommit(true);
+
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ throw new PIMDBAccessException("\nError deleting contact.\n", e);
+
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+ /**
+ * Removes a contact, provided it has the same userId as this DAO. The
+ * deletion is soft (reversible).
+ *
+ * @param contact
+ * whence the UID and the last update Date are extracted
+ * @throws PIMDBAccessException
+ */
+ public void removeItem(ContactWrapper contact) throws PIMDBAccessException {
+
+ removeItem(contact.getId());
+ }
+
+ /**
+ * Removes all contacts, provided it has the same userId as this DAO. The
+ * deletion is soft (reversible).
+ *
+ * @throws PIMDBAccessException
+ */
+ public void removeAllItems() throws PIMDBAccessException {
+
+ log.info("\n\n =>DAO start removeAllItems\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+ con.setAutoCommit(false);
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_addresses WHERE id_address IN (SELECT id_address FROM phpgw_cc_contact_addrs WHERE id_contact IN(SELECT id_contact FROM phpgw_cc_contact WHERE id_owner = ?))");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_addrs WHERE id_contact IN(SELECT id_contact FROM phpgw_cc_contact WHERE id_owner = ?)");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_connections WHERE id_connection IN (SELECT id_connection FROM phpgw_cc_contact_conns WHERE id_contact IN(SELECT id_contact FROM phpgw_cc_contact WHERE id_owner = ?))");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact_conns WHERE id_contact IN(SELECT id_contact FROM phpgw_cc_contact WHERE id_owner = ?)");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ ps = con
+ .prepareStatement("DELETE FROM phpgw_cc_contact WHERE id_owner = ?");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.executeUpdate();
+
+ con.commit();
+ con.setAutoCommit(true);
+
+ } catch (Exception e) {
+
+ throw new PIMDBAccessException("\n=> Error deleting contacts.\n", e);
+
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+ }
+
+ /**
+ * Retrieves the UID list of all contacts belonging to the user.
+ *
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ public List getAllItems() throws PIMDBAccessException {
+
+ log.info("\n\n=> PIMContactDAO getAllItems begin\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ List contacts = new ArrayList();
+ ResultSet rs = null;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement(SQL_GET_CONTACT_ID_LIST_BY_USER
+ + SQL_ORDER_BY_ID);
+ ps.setLong(1, Long.parseLong(userId));
+
+ rs = ps.executeQuery();
+
+ while (rs.next()) {
+ contacts.add(Long.toString(rs.getLong(1))); // It's the first
+ // and only column
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new PIMDBAccessException("\n=> Error listing contacts.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ log.info("\n\n=> PIMContactDAO getAllItems end\n");
+
+ return contacts;
+ }
+
+ /**
+ * Gets the contact with given UID, provided it has the same userId as this
+ * DAO.
+ *
+ * @param uid
+ * corresponds to the id field in the fnbl_pim_contact table
+ * @throws PIMDBAccessException
+ * @return the contact as a ContactWrapper object.
+ */
+ public ContactWrapper getItem(String uid) throws PIMDBAccessException {
+
+ log.info("\n\n=> DAO start getItem " + uid + "\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ ContactWrapper c;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement(SQL_GET_CONTACT_BY_ID_USER);
+ ps.setLong(1, Long.parseLong(uid));
+ ps.setLong(2, Long.parseLong(userId));
+ rs = ps.executeQuery();
+ c = createContact(uid, rs);
+
+ ps = con.prepareStatement(SQL_GET_CONTACT_ITEM_BY_ID);
+ ps.setLong(1, Long.parseLong(uid));
+ rs = ps.executeQuery();
+
+ try {
+ addPIMContactItems(c, rs);
+ } catch (SQLException sqle) {
+ throw new SQLException(
+ "\nError while adding extra PIM contact "
+ + "information.\n" + sqle.getMessage(), sqle
+ .getSQLState());
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new PIMDBAccessException("\n=> Error seeking contact.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ return c;
+ }
+
+ /**
+ * Retrieves the UID list of the contacts considered to be "twins" of a
+ * given contact.
+ *
+ * @param c
+ * the Contact object representing the contact whose twins need
+ * be found. In the present implementation, only the following
+ * data matter:
+ *
+ *
first name
+ *
last name
+ *
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects) that may be empty but not null
+ */
+ public List getTwinItems(Contact c) throws PIMDBAccessException {
+
+ log.info("\n\n=> PIMContactDAO getTwinItems begin\n");
+
+ List twins = new ArrayList();
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+
+ String firstName = c.getName().getFirstName()
+ .getPropertyValueAsString();
+ String lastName = c.getName().getLastName()
+ .getPropertyValueAsString();
+
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ if (firstName == null || ("null".equals(firstName))) {
+ firstName = "";
+ }
+ if (lastName == null || ("null".equals(lastName))) {
+ lastName = "";
+ }
+
+ String fnSearch = (firstName.length() == 0 ? "" : firstName
+ .toLowerCase());
+ String lnSearch = (lastName.length() == 0 ? "" : lastName
+ .toLowerCase());
+ StringBuilder sb = new StringBuilder(100);
+
+ sb.append("\n\nLooking for items having: ").append(
+ "\n> first name : '").append(fnSearch).append('\'')
+ .append("\n> last name : '").append(lnSearch).append(
+ '\'').append("\n");
+
+ log.info(sb.toString());
+
+ ps = con.prepareStatement(SQL_GET_CONTACT_TWIN_ID_LIST
+ + SQL_ORDER_BY_ID);
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setString(2, firstName.toLowerCase());
+ ps.setString(3, firstName.toLowerCase());
+ ps.setString(4, lastName.toLowerCase());
+ ps.setString(5, lastName.toLowerCase());
+ rs = ps.executeQuery();
+
+ long twinId;
+
+ while (rs.next()) {
+
+ twinId = rs.getLong(1); // The id is the first and only column
+ log.info("\n\n=> Twin found: " + twinId + "\n");
+ twins.add(Long.toString(twinId));
+ }
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException("\n=> Error retrieving twin.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ log.info("\n\n=> PIMContactDAO getTwinItems end\n");
+
+ return twins;
+ }
+
+ /**
+ * Retrieves the state of the given item, provided it's been modified after
+ * a certain moment.
+ *
+ * @param uid
+ * the UID of the item to be checked (as a String object)
+ * @param since
+ * the Timestamp that the item's lastUpdate field is checked
+ * against: if the item has been modified before that moment, an
+ * "unchanged" state marker is returned
+ * @throws PIMDBAccessException
+ * @return a char identifying either one of the 3 standard states ("new",
+ * "deleted", "updated") or the special "unchanged" status, all of
+ * them as defined in com.funambol.pim.util.Def
+ */
+ public char getItemState(String uid, Timestamp since)
+ throws PIMDBAccessException {
+
+ log.info("\n\n=> DAO start getItemState\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ char status;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ ps = con.prepareStatement(SQL_GET_STATUS_BY_ID_USER_TIME);
+ ps.setLong(1, Long.parseLong(uid));
+ ps.setLong(2, Long.parseLong(userId));
+ ps.setLong(3, since.getTime());
+ rs = ps.executeQuery();
+
+ if (!rs.next()) {
+
+ status = Def.PIM_STATE_UNCHANGED;
+
+ log.info("\n\n=> Item " + uid + "'s status wasn't retrieved "
+ + "because the item hasn't been modified since "
+ + since + "\n");
+
+ } else {
+
+ status = rs.getString(1).charAt(0);
+
+ log.info("\n\n=> Item " + uid + " has status " + status + "\n");
+
+ }
+
+ } catch (Exception e) {
+ throw new PIMDBAccessException(
+ "\n=> Error retrieving item state.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ return status;
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ /**
+ * Creates a ContactWrapper object from a ResultSet. Only the basic data are
+ * set.
+ *
+ * @param wrapperId
+ * the UID of the wrapper object to be returned
+ * @param rs
+ * the result of the execution of a proper SQL SELECT statement
+ * on the phpgw_cc_contact table, with the cursor before its
+ * first row
+ * @return a newly created ContactWrapper initialized with the fields in the
+ * result set
+ * @throws java.sql.SQLException
+ * @throws NotFoundException
+ */
+ private static ContactWrapper createContact(String wrapperId, ResultSet rs)
+ throws SQLException, NotFoundException {
+
+ if (!rs.next()) {
+ throw new NotFoundException("\n\n=> No contact found.\n");
+ }
+
+ ResultSetMetaData rsmd = rs.getMetaData();
+ ContactWrapper cw = null;
+
+ String column = null;
+ String uid = null;
+ String userId = null;
+
+ uid = String.valueOf(rs.getLong(SQL_FIELD_ID));
+ userId = rs.getString(SQL_FIELD_USERID);
+
+ Contact c = new Contact();
+ cw = new ContactWrapper(wrapperId, userId, c);
+
+ int columnCount = rsmd.getColumnCount();
+
+ for (int i = 1; i <= columnCount; ++i) {
+
+ column = rsmd.getColumnName(i);
+
+ // General
+ if (SQL_FIELD_ID.equalsIgnoreCase(column)) {
+ // Does nothing: field already set at construction time
+ } else if (SQL_FIELD_LAST_UPDATE.equalsIgnoreCase(column)) {
+ cw.setLastUpdate(new Timestamp(rs.getLong(i)));
+ } else if (SQL_FIELD_USERID.equalsIgnoreCase(column)) {
+ // Does nothing: field already set at construction time
+ } else if (SQL_FIELD_STATUS.equalsIgnoreCase(column)) {
+ cw.setStatus(rs.getString(i).charAt(0));
+ } else if (SQL_FIELD_FIRST_NAME.equalsIgnoreCase(column)) {
+ c.getName().getFirstName().setPropertyValue(rs.getString(i));
+ } else if (SQL_FIELD_LAST_NAME.equalsIgnoreCase(column)) {
+ c.getName().getLastName().setPropertyValue(rs.getString(i));
+ } else if (SQL_FIELD_BIRTHDAY.equalsIgnoreCase(column)) {
+ c.getPersonalDetail().setBirthday(rs.getString(i));
+ } else if (SQL_FIELD_CATEGORY.equalsIgnoreCase(column)) {
+ c.getCategories().setPropertyValue(rs.getString(i));
+ } else {
+ throw new SQLException("\n=> Unhandled column: " + column);
+ }
+ }
+
+ return cw;
+ }
+
+ /**
+ * Attaches extra information to a contact on the basis of a ResultSet.
+ *
+ * @param c
+ * the contact still lacking extra information
+ * @param rs
+ * the result of the execution of a proper SQL SELECT statement
+ * on the phpgw_cc_connections and phpgw_cc_contact_conns table,
+ * with the cursor before its first row
+ * @return the ContactWrapper object with extra information attached
+ * @throws java.sql.SQLException
+ */
+ private static ContactWrapper addPIMContactItems(ContactWrapper cw,
+ ResultSet rs) throws SQLException {
+
+ ResultSetMetaData rsmd = rs.getMetaData();
+ Contact c = cw.getContact();
+
+ String type = null;
+ String value = null;
+ String name = null;
+ String column = null;
+
+ int columnCount = 0;
+
+ Phone phone;
+ Email email;
+
+ while (rs.next()) {
+
+ columnCount = rsmd.getColumnCount();
+
+ for (int i = 1; i <= columnCount; ++i) {
+
+ column = rsmd.getColumnName(i);
+
+ if (SQL_FIELD_ITEM_NAME.equalsIgnoreCase(column)) {
+ name = rs.getString(i);
+ } else if (SQL_FIELD_ITEM_VALUE.equalsIgnoreCase(column)) {
+ value = rs.getString(i);
+ } else if (SQL_FIELD_ITEM_TYPE.equalsIgnoreCase(column)) {
+ type = rs.getString(i);
+ } else {
+ throw new SQLException("\n=> Unhandled column: " + column);
+ }
+ }
+
+ if (type.equals("1")) { // email
+
+ if (TYPE_EMAIL_1_ADDRESS.equalsIgnoreCase(name)) {
+ email = new Email();
+ email.setEmailType(FIELD_EMAIL_1_ADDRESS);
+ email.setPropertyValue(value);
+ c.getPersonalDetail().addEmail(email);
+ } else if (TYPE_EMAIL_2_ADDRESS.equalsIgnoreCase(name)) {
+ email = new Email();
+ email.setEmailType(FIELD_EMAIL_2_ADDRESS);
+ email.setPropertyValue(value);
+ c.getPersonalDetail().addEmail(email);
+ }
+ }
+
+ if (type.equals("2")) { // telefone
+
+ if (TYPE_PRIMARY_TELEPHONE_NUMBER.equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_PRIMARY_TELEPHONE_NUMBER);
+ phone.setPropertyValue(value);
+ c.getBusinessDetail().addPhone(phone);
+ } else if (TYPE_HOME_TELEPHONE_NUMBER.equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_HOME_TELEPHONE_NUMBER);
+ phone.setPropertyValue(value);
+ c.getPersonalDetail().addPhone(phone);
+ } else if (TYPE_MOBILE_TELEPHONE_NUMBER.equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_MOBILE_TELEPHONE_NUMBER);
+ phone.setPropertyValue(value);
+ c.getPersonalDetail().addPhone(phone);
+ } else if (TYPE_BUSINESS_TELEPHONE_NUMBER
+ .equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_BUSINESS_TELEPHONE_NUMBER);
+ phone.setPropertyValue(value);
+ c.getBusinessDetail().addPhone(phone);
+ } else if (TYPE_BUSINESS_FAX_NUMBER.equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_BUSINESS_FAX_NUMBER);
+ phone.setPropertyValue(value);
+ c.getBusinessDetail().addPhone(phone);
+ } else if (TYPE_PAGER_NUMBER.equalsIgnoreCase(name)) {
+ phone = new Phone();
+ phone.setPhoneType(FIELD_PAGER_NUMBER);
+ phone.setPropertyValue(value);
+ c.getBusinessDetail().addPhone(phone);
+ }
+ }
+ }
+
+ return cw;
+ }
+
+ /**
+ * Retrieves the UID list of the contacts belonging to the user filtered
+ * according to the given time interval and status.
+ *
+ * @param since
+ * the earliest allowed last-update Timestamp
+ * @param to
+ * the latest allowed last-update Timestamp
+ * @param status
+ * 'D' for deleted items, 'N' for new items, 'U' for updated
+ * items
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ protected List getItemsHavingStatus(Timestamp since, Timestamp to,
+ char status) throws PIMDBAccessException {
+
+ log.info("\n\n=> Seeking '" + status + "' items "
+ + "in time interval (" + since + "; " + to + ")\n");
+
+ Connection con = null;
+ PreparedStatement ps = null;
+ List contacts = new ArrayList();
+ ResultSet rs = null;
+
+ try {
+ // Looks up the data source when the first connection is created
+ con = getDataSource().getConnection();
+
+ if (status == 'D') {
+
+ ps = con
+ .prepareStatement("SELECT guid FROM fnbl_client_mapping WHERE sync_source = 'catalogo' AND guid NOT IN (SELECT id_contact FROM phpgw_cc_contact WHERE id_owner = ?) AND principal = ? ORDER BY guid");
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setLong(2, this.principal.getId());
+ rs = ps.executeQuery();
+
+ } else {
+
+ ps = con
+ .prepareStatement(SQL_GET_FNBL_PIM_CONTACT_ID_LIST_BY_USER_TIME_STATUS
+ + SQL_ORDER_BY_ID);
+ ps.setLong(1, Long.parseLong(userId));
+ ps.setLong(2, since.getTime());
+ ps.setLong(3, to.getTime());
+ ps.setString(4, String.valueOf(status));
+ rs = ps.executeQuery();
+ }
+
+ while (rs.next()) {
+ contacts.add(Long.toString(rs.getLong(1))); // It's the first
+ // and only column
+ log.info("\n\n=> Item found " + rs.getLong(1) + "\n");
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new PIMDBAccessException("\n=> Error listing contacts.\n", e);
+ } finally {
+ DBTools.close(con, ps, rs);
+ }
+
+ return contacts;
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMEntityDAO.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMEntityDAO.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/items/dao/PIMEntityDAO.java (revision 1009)
@@ -0,0 +1,395 @@
+/**
+ * This class implements methods to access PIM data in a data store.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.items.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+import br.com.prognus.psync.exception.PIMDBAccessException;
+import br.com.prognus.psync.util.Def;
+
+import com.funambol.common.pim.common.Property;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.tools.DBTools;
+import com.funambol.framework.tools.DataSourceTools;
+import com.novell.ldap.LDAPAttribute;
+import com.novell.ldap.LDAPAttributeSet;
+import com.novell.ldap.LDAPConnection;
+import com.novell.ldap.LDAPEntry;
+import com.novell.ldap.LDAPException;
+import com.novell.ldap.LDAPSearchResults;
+import com.novell.ldap.util.Base64;
+
+public abstract class PIMEntityDAO {
+
+ // ------------------------------------------------------------ Private data
+
+ protected static final FunambolLogger log = FunambolLoggerFactory
+ .getLogger(Def.LOGGER_NAME);
+
+ protected String jndiDataSourceName;
+
+ protected DataSource dataSource = null;
+
+ // -------------------------------------------------------------- Properties
+
+ protected String userId;
+
+ public String getUserId() {
+ return userId;
+ }
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Creates a new instance of PIMEntityDAO, ready to be linked to a given
+ * DataSource.
+ *
+ * @param jndiDataSourceName
+ * the jndiname of the datasource to use
+ * @param userId
+ * corresponding to the "userid" field of the fun_pim_contact
+ * table
+ * @throws IllegalArgumentException
+ * if there are errors looking up the dataSource with the given
+ * jndiDataSourceName
+ */
+ public PIMEntityDAO(String jndiDataSourceName, String userId) {
+
+ Connection con = null;
+ PreparedStatement ps = null;
+
+ try {
+ this.dataSource = DataSourceTools
+ .lookupDataSource(jndiDataSourceName);
+ } catch (NamingException ex) {
+ throw new IllegalArgumentException("Error looking up datasource: "
+ + jndiDataSourceName, ex);
+ }
+
+ try {
+ con = getDataSource().getConnection();
+
+ PreparedStatement ldap_server = con
+ .prepareStatement("select config_value from phpgw_config WHERE config_name = 'ldap_host' AND config_app = 'phpgwapi'");
+ ResultSet rs1 = ldap_server.executeQuery();
+
+ PreparedStatement ldap_dc = con
+ .prepareStatement("select config_value from phpgw_config WHERE config_name = 'ldap_context' AND config_app = 'phpgwapi'");
+ ResultSet rs2 = ldap_dc.executeQuery();
+
+ rs1.next();
+ rs2.next();
+
+ String server = rs1.getString(1);
+ String dc = rs2.getString(1);
+
+ this.jndiDataSourceName = jndiDataSourceName;
+ this.userId = LdapUID(server, dc, userId);
+
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Error UID ldap", e);
+ } finally {
+ DBTools.close(con, ps, null);
+ }
+
+ }
+
+ // -----------------------------------------------------------Public methods
+
+ protected String LdapUID(String server, String dc, String user)
+ throws Exception {
+
+ LDAPConnection ldap = new LDAPConnection();
+ String searchAttrs[] = { "uidNumber" };
+ String id_owner = null;
+
+ try {
+
+ ldap.connect(server, 389);
+ LDAPSearchResults searchResults = ldap.search(dc,
+ LDAPConnection.SCOPE_SUB, "(uid=" + user + ")",
+ searchAttrs, false);
+
+ while (searchResults.hasMore()) {
+
+ LDAPEntry nextEntry = null;
+ try {
+ nextEntry = searchResults.next();
+ } catch (LDAPException e) {
+ if (e.getResultCode() == LDAPException.LDAP_TIMEOUT
+ || e.getResultCode() == LDAPException.CONNECT_ERROR)
+ break;
+ else
+ continue;
+ }
+ LDAPAttributeSet attributeSet = nextEntry.getAttributeSet();
+ Iterator allAttributes = attributeSet.iterator();
+
+ while (allAttributes.hasNext()) {
+
+ LDAPAttribute attribute = (LDAPAttribute) allAttributes
+ .next();
+ String attributeName = attribute.getName();
+ Enumeration allValues = attribute.getStringValues();
+
+ if (allValues != null) {
+
+ while (allValues.hasMoreElements()) {
+
+ String Value = (String) allValues.nextElement();
+
+ if (Base64.isLDIFSafe(Value)) {
+ } else {
+ Value = Base64.encode(Value.getBytes());
+ }
+
+ if (attributeName.equalsIgnoreCase("uidNumber")) {
+ id_owner = Value;
+ break;
+ }
+
+ }
+ }
+ }
+ }
+
+ ldap.disconnect();
+
+ if (id_owner == null) {
+ throw new LDAPException();
+ }
+
+ } catch (LDAPException e) {
+ log.info("Error while connecting and binding to LDAP: "
+ + e.toString());
+ }
+
+ return id_owner;
+ }
+
+ /**
+ * Looks up the data source, making some guess attempts based on
+ * jndiDataSourceName.
+ *
+ * @throws Exception
+ */
+ protected DataSource getDataSource() throws Exception {
+ return dataSource;
+ }
+
+ public abstract List getAllItems() throws PIMDBAccessException;
+
+ public abstract void removeItem(String uid) throws PIMDBAccessException;
+
+ public abstract void removeAllItems() throws PIMDBAccessException;
+
+ public abstract char getItemState(String uid, Timestamp since)
+ throws PIMDBAccessException;
+
+ /**
+ * Retrieves the UID list of the new items belonging to the user filtered
+ * according to the given time interval.
+ *
+ * @param since
+ * the earliest allowed last-update Timestamp
+ * @param to
+ * the latest allowed last-update Timestamp
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ public List getNewItems(Timestamp since, Timestamp to)
+ throws PIMDBAccessException {
+
+ log.info("\n\n==>DAO start getNewItems");
+ return getItemsHavingStatus(since, to, 'N');
+ }
+
+ /**
+ * Retrieves the UID list of the deleted items belonging to the user
+ * filtered according to the given time interval.
+ *
+ * @param since
+ * the earliest allowed last-update Timestamp
+ * @param to
+ * the latest allowed last-update Timestamp
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ public List getDeletedItems(Timestamp since, Timestamp to)
+ throws PIMDBAccessException {
+
+ log.info("\n\n==>DAO start getDeletedItems");
+ return getItemsHavingStatus(since, to, 'D');
+ }
+
+ /**
+ * Retrieves the UID list of the updated items belonging to the user
+ * filtered according to the given time interval.
+ *
+ * @param since
+ * the earliest allowed last-update Timestamp
+ * @param to
+ * the latest allowed last-update Timestamp
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ public List getUpdatedItems(Timestamp since, Timestamp to)
+ throws PIMDBAccessException {
+
+ log.info("\n\n==>DAO start getUpdatedItems");
+ return getItemsHavingStatus(since, to, 'U');
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ /**
+ * Retrieves the UID list of the items belonging to the user filtered
+ * according to the given time interval and status.
+ *
+ * @param since
+ * the earliest allowed last-update Timestamp
+ * @param to
+ * the latest allowed last-update Timestamp
+ * @param status
+ * 'D' for deleted items, 'N' for new items, 'U' for updated
+ * items
+ * @throws PIMDBAccessException
+ * @return a List of UIDs (as String objects)
+ */
+ protected abstract List getItemsHavingStatus(Timestamp since, Timestamp to,
+ char status) throws PIMDBAccessException;
+
+ /**
+ * Checks (safely) whether the property is unset.
+ *
+ * @param property
+ * may be null
+ * @return false only if the property value is a non-null, but possibly
+ * empty (""), string
+ *
+ * @see PIMEntityDAO#stringFrom(Property)
+ */
+ protected static boolean isNullProperty(Property property) {
+
+ return (property == null || property.getPropertyValueAsString() == null);
+ }
+
+ /**
+ * Checks (safely) whether the property is unset or set to an empty string.
+ *
+ * @param property
+ * may be null
+ * @return false only if the property value is a non-null non-empty string
+ *
+ * @see PIMEntityDAO#stringFrom(Property, boolean)
+ */
+ protected static boolean isEmptyProperty(Property property) {
+
+ if (property == null) {
+ return true;
+ }
+ String string = property.getPropertyValueAsString();
+ if (string == null || string.length() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extract a string from a property in a safe way. An empty string ("") is
+ * considered as an acceptable value for the property: in such a case, an
+ * empty String object will be returned.
+ *
+ * @param property
+ * may be null
+ * @return if existing, the property value will be returned as a String
+ * object
+ */
+ protected static String stringFrom(Property property) {
+
+ if (property == null) {
+ return null;
+ }
+ return property.getPropertyValueAsString();
+ }
+
+ /**
+ * Extract a string from a property in a safe way. This method is not
+ * currently used, but it could be useful in the future for determining the
+ * behaviour of the connector when dealing with empty properties. A field
+ * whose value is extracted with stringFrom(..., true) will not be updated
+ * in case its value is set to ""; a field whose value is extracted with
+ * stringFrom(..., false) will be considered as explicitly kept blank if its
+ * value is "". This means that single field deletions can be made tunable.
+ *
+ * @param property
+ * may be null
+ * @param emptyImpliesNull
+ * if true, an empty string ("") will be treated as if it were
+ * null; otherwise, in such a case an empty String object will be
+ * returned
+ *
+ * @return if existing (and not empty if emptyImpliesNull is true), the
+ * property value will be returned as a String object
+ */
+ protected static String stringFrom(Property property,
+ boolean emptyImpliesNull) {
+
+ if (property == null) {
+ return null;
+ }
+
+ String string = property.getPropertyValueAsString();
+ if (string == null || !emptyImpliesNull) {
+ return string;
+ }
+
+ if (string.length() == 0) {
+ return null;
+ }
+ return string;
+ }
+
+ /**
+ * Gets a substring in a safe way.
+ *
+ * @param string
+ * may be null or longer than maxLength
+ * @param maxLength
+ * @return null if the string is null, a truncated substring of string
+ * otherwise
+ */
+ protected static String truncate(String string, int maxLength) {
+ if (string == null || string.length() <= maxLength) {
+ return string;
+ }
+ return string.substring(0, maxLength);
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMCalendarManager.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMCalendarManager.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMCalendarManager.java (revision 1009)
@@ -0,0 +1,165 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.items.manager;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.exception.PIMDBAccessException;
+import br.com.prognus.psync.items.dao.PIMCalendarDAO;
+import br.com.prognus.psync.items.dao.PIMEntityDAO;
+import br.com.prognus.psync.items.model.CalendarWrapper;
+
+import com.funambol.common.pim.calendar.Calendar;
+import com.funambol.framework.security.Sync4jPrincipal;
+import com.funambol.framework.tools.merge.MergeResult;
+
+public class PIMCalendarManager extends PIMEntityManager {
+
+ // ------------------------------------------------------------- Private
+ // data
+
+ private PIMCalendarDAO dao;
+
+ // -------------------------------------------------------------
+ // Constructors
+
+ /**
+ * @see PIMEntityDAO#PIMEntityDAO(String, String)
+ */
+ public PIMCalendarManager(String jndiDataSourceName,
+ Sync4jPrincipal principal) {
+
+ this.dao = new PIMCalendarDAO(jndiDataSourceName, principal);
+ super.dao = this.dao;
+
+ log.trace("Created new PIMCalendarManager for user ID "
+ + principal.getUsername());
+ }
+
+ // ----------------------------------------------------------- Public
+ // methods
+
+ /**
+ * Updates a calendar.
+ *
+ * @param uid
+ * the UID of the calendar
+ * @param c
+ * a Calendar object
+ * @param t
+ * the last update time that will be forced as the last update
+ * time of the updated calendar
+ * @return the UID of the calendar (it should be the same as the argument)
+ * @throws EntityException
+ */
+ public String updateItem(String uid, Calendar c, Timestamp t)
+ throws EntityException {
+
+ log.trace("Updating calendar with ID " + uid
+ + " on the server, setting " + t
+ + " as its last update timestamp.");
+
+ CalendarWrapper cw = new CalendarWrapper(uid, this.dao.getUserId(), c);
+ cw.setLastUpdate(t);
+
+ try {
+ this.dao.updateItem(cw);
+ return cw.getId();
+ } catch (Exception e) {
+ throw new EntityException("Error updating item.", e);
+ }
+ }
+
+ /**
+ * @see PIMCalendarDAO#addItem(Calendar)
+ */
+ public String addItem(Calendar c, Timestamp t) throws EntityException {
+
+ try {
+ CalendarWrapper cw = createCalendarWrapper(c);
+ cw.setLastUpdate(t);
+ this.dao.addItem(cw);
+ return cw.getId();
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error adding item.", dbae);
+ }
+ }
+
+ /**
+ * @see PIMCalendarDAO#getItem(String)
+ */
+ public CalendarWrapper getItem(String id) throws EntityException {
+ try {
+ return this.dao.getItem(id);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error getting item. " + dbae);
+ }
+ }
+
+ /**
+ * @see PIMCalendarDAO#getTwinItems(Calendar)
+ */
+ public List getTwins(Calendar c) throws EntityException {
+ try {
+ return this.dao.getTwinItems(c);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error getting twins of an item.", dbae);
+ }
+ }
+
+ public boolean mergeItems(String serverCalendarID, Calendar clientCalendar,
+ Timestamp t) throws EntityException {
+
+ log.info("\n\n=> PIMCalendarManager mergeItems begin");
+ log.info("\n\n=> Merging server item " + serverCalendarID
+ + " with its client counterpart.");
+
+ try {
+
+ CalendarWrapper serverCalendarWrapper = this.dao
+ .getItem(serverCalendarID);
+ Calendar serverCalendar = serverCalendarWrapper.getCalendar();
+ MergeResult mergeResult = clientCalendar.merge(serverCalendar);
+
+ if (mergeResult.isSetBRequired()) {
+ updateItem(serverCalendarID, serverCalendar, t);
+ }
+
+ log.info("MergeResult: " + mergeResult);
+
+ return mergeResult.isSetARequired();
+
+ } catch (Exception e) {
+
+ log.error("Error merging items.", e);
+ throw new EntityException("Error merging items. " + e);
+ }
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ private CalendarWrapper createCalendarWrapper(Calendar c) {
+
+ log.info("Created a new CalendarWrapper (UID not yet generated).");
+ return new CalendarWrapper(null, this.dao.getUserId(), c);
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMContactManager.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMContactManager.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMContactManager.java (revision 1009)
@@ -0,0 +1,177 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.items.manager;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.exception.PIMDBAccessException;
+import br.com.prognus.psync.items.dao.PIMContactDAO;
+import br.com.prognus.psync.items.dao.PIMEntityDAO;
+import br.com.prognus.psync.items.model.ContactWrapper;
+
+import com.funambol.common.pim.contact.Contact;
+import com.funambol.framework.security.Sync4jPrincipal;
+import com.funambol.framework.tools.merge.MergeResult;
+
+public class PIMContactManager extends PIMEntityManager {
+
+ // ------------------------------------------------------------- Private
+ // data
+
+ private PIMContactDAO dao;
+
+ // -------------------------------------------------------------
+ // Constructors
+
+ /**
+ * @see PIMEntityDAO#PIMEntityDAO(String, String)
+ */
+ public PIMContactManager(String jndiDataSourceName,
+ Sync4jPrincipal principal) {
+
+ this.dao = new PIMContactDAO(jndiDataSourceName, principal);
+ super.dao = this.dao;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Created new PIMContactManager for user ID "
+ + principal.getUsername() + "\n");
+ }
+
+ }
+
+ // ----------------------------------------------------------- Public
+ // methods
+
+ /**
+ * Updates a contact.
+ *
+ * @param uid
+ * the UID of the contact
+ * @param c
+ * a Contact object
+ * @param t
+ * the last update time that will be forced as the last update
+ * time of the updated contact
+ * @return the UID of the contact (it should be the same as the argument)
+ * @throws EntityException
+ */
+ public String updateItem(String uid, Contact c, Timestamp t)
+ throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Updating contact with ID " + uid
+ + " on the server, setting " + t
+ + " as its last update timestamp.");
+ }
+
+ ContactWrapper cw = new ContactWrapper(uid, this.dao.getUserId(), c);
+ cw.setLastUpdate(t);
+
+ try {
+ return this.dao.updateItem(cw);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error updating item.", dbae);
+ }
+ }
+
+ /**
+ * @see PIMContactDAO#addItem(Contact)
+ */
+ public String addItem(Contact c, Timestamp t) throws EntityException {
+
+ try {
+ ContactWrapper cw = createContactWrapper(c);
+ cw.setLastUpdate(t);
+ this.dao.addItem(cw);
+ return cw.getId();
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error adding item.", dbae);
+ }
+ }
+
+ /**
+ * @see PIMContactDAO#getItem(String)
+ */
+ public ContactWrapper getItem(String id) throws EntityException {
+ try {
+ return this.dao.getItem(id);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error getting item. " + dbae);
+ }
+ }
+
+ /**
+ * @see PIMContactDAO#getTwinItems(Contact)
+ */
+ public List getTwins(Contact c) throws EntityException {
+ try {
+ return this.dao.getTwinItems(c);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error getting twins of an item.", dbae);
+ }
+ }
+
+ public boolean mergeItems(String serverContactID, Contact clientContact,
+ Timestamp t) throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactManager mergeItems begin");
+ log.trace("Merging server item " + serverContactID
+ + " with its client counterpart.");
+ }
+
+ try {
+
+ ContactWrapper serverContactWrapper = this.dao
+ .getItem(serverContactID);
+ Contact serverContact = serverContactWrapper.getContact();
+ MergeResult mergeResult = clientContact.merge(serverContact);
+
+ if (mergeResult.isSetBRequired()) {
+ updateItem(serverContactID, serverContact, t);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("MergeResult: " + mergeResult);
+ }
+
+ return mergeResult.isSetARequired();
+
+ } catch (Exception e) {
+
+ log.error("SyncSource error merging the items", e);
+ throw new EntityException("Error merging items. " + e);
+ }
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ private ContactWrapper createContactWrapper(Contact c) {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Created a new ContactWrapper (UID not yet generated).");
+ }
+
+ return new ContactWrapper(null, this.dao.getUserId(), c);
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMEntityManager.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMEntityManager.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/items/manager/PIMEntityManager.java (revision 1009)
@@ -0,0 +1,109 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.items.manager;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.exception.PIMDBAccessException;
+import br.com.prognus.psync.items.dao.PIMEntityDAO;
+import br.com.prognus.psync.util.Def;
+
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+
+public abstract class PIMEntityManager {
+
+ // ------------------------------------------------------------ Private data
+
+ protected static final FunambolLogger log = FunambolLoggerFactory
+ .getLogger(Def.LOGGER_NAME);
+
+ protected PIMEntityDAO dao;
+
+ // ---------------------------------------------------------- Public methods
+
+ public char getItemState(String id, Timestamp t) throws EntityException {
+ try {
+ return dao.getItemState(id, t);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while getting item state.", dbae);
+ }
+ }
+
+ public List getAllItems() throws EntityException {
+
+ try {
+ return dao.getAllItems();
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while getting all items.", dbae);
+ }
+ }
+
+ public List getNewItems(Timestamp since, Timestamp to)
+ throws EntityException {
+
+ try {
+ return dao.getNewItems(since, to);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while getting new items.", dbae);
+ }
+ }
+
+ public List getUpdatedItems(Timestamp since, Timestamp to)
+ throws EntityException {
+
+ try {
+ return dao.getUpdatedItems(since, to);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while getting updated items.",
+ dbae);
+ }
+ }
+
+ public List getDeletedItems(Timestamp since, Timestamp to)
+ throws EntityException {
+
+ try {
+ return dao.getDeletedItems(since, to);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while getting deleted items.",
+ dbae);
+ }
+ }
+
+ public void removeItem(String uid) throws EntityException {
+
+ try {
+ dao.removeItem(uid);
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while deleting item.", dbae);
+ }
+ }
+
+ public void removeAllItems() throws EntityException {
+
+ try {
+ dao.removeAllItems();
+ } catch (PIMDBAccessException dbae) {
+ throw new EntityException("Error while removing all items.", dbae);
+ }
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/BeanShellSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/BeanShellSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/BeanShellSynclet.java (revision 1009)
@@ -0,0 +1,415 @@
+/**
+ * Copyright (C) 2005-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import java.io.*;
+
+import java.net.URL;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import bsh.BshClassManager;
+import bsh.Interpreter;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import com.funambol.framework.config.ConfigClassLoader;
+import com.funambol.framework.core.Sync4jException;
+import com.funambol.framework.core.SyncML;
+import com.funambol.framework.engine.pipeline.*;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+
+import com.funambol.server.config.Configuration;
+
+/**
+ * Goal of the BeanShell synclet is to provide a way to develop synclets without
+ * the need to follow the build/pack/deploy cycle at any change. This will also
+ * minimize the downtime of a running server and allows to apply quick hot
+ * fixes. To achieve this, BeanShell synclet is based on the use of a scripting
+ * language which is interpreted instead of compiled. The BeanShell scripting
+ * language is the scripting used by this synclet, since it is very similar to
+ * Java.
+ *
+ * @version $Id: BeanShellSynclet.java,v 1.5 2007-04-13 08:51:02 luigiafassina Exp $
+ */
+public class BeanShellSynclet
+implements InputMessageProcessor, OutputMessageProcessor {
+
+ // ------------------------------------------------------------- Static data
+
+ /**
+ * The interpreters hash map
+ */
+ private static HashMap interpreters;
+
+ /**
+ * Logger
+ */
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine");
+
+ // ------------------------------------------------------------ Private data
+
+ /**
+ * The script name
+ */
+ private String script;
+
+ /**
+ * The HTTP user agent pattern to match in order to trigger execution
+ */
+ private String pattern;
+
+ /**
+ * HTTP header the pattern is applied to
+ */
+ private String header;
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Delegates the call to the script.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ if (!isDeviceToProcess(processingContext)) {
+ return;
+ }
+
+ try {
+ InputMessageProcessor imp = getInputSynclet();
+ imp.preProcessMessage(processingContext, message);
+ } catch (StopProcessingException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("Error in processing the input message", e);
+ }
+ }
+
+ /**
+ * Delegates the call to the script.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ if (!isDeviceToProcess(processingContext)) {
+ return;
+ }
+
+ try {
+ OutputMessageProcessor omp = getOutputSynclet();
+ omp.postProcessMessage(processingContext, message);
+ } catch (StopProcessingException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("Error in processing the output message", e);
+ }
+ }
+
+ // --------------------------------------------------------------- Accessors
+
+ /**
+ * Sets script
+ *
+ * @param script the script name
+ */
+ public void setScript(String script) {
+ this.script = script;
+ }
+
+ /**
+ * Returns script
+ *
+ * @return scripts
+ */
+ public String getScript() {
+ return this.script;
+ }
+
+ public String getHeader() {
+ return header;
+ }
+
+ public void setHeader(String header) {
+ this.header = header;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public String toString() {
+ ToStringBuilder sb = new ToStringBuilder(this);
+
+ sb.append("script", script ).
+ append("header", header ).
+ append("pattern", pattern);
+
+ return sb.toString();
+ }
+ // --------------------------------------------------------- Private methods
+
+ /**
+ * Returns the interpreter associated to the instance script name. If an
+ * interpreter instance is not yet created, it is created before being
+ * returned.
+ * It also checks if the last modification timestamp of the script is more
+ * recent that the last read. If so, the new script is loaded in the
+ * interpreter.
+ *
+ * @return the interpreter instance associated to the instance's script
+ */
+ private BeanShellEntry getBeanShellEntry() {
+ assert (script != null && script.trim().length()>0);
+
+ BeanShellEntry bse = null;
+ synchronized (this) {
+ bse = (BeanShellEntry)interpreters.get(script);
+
+ if (bse == null) {
+ bse = new BeanShellEntry();
+ bse.lastModified = -1;
+
+ interpreters.put(script, bse);
+ }
+ }
+
+ return bse;
+ }
+
+ /**
+ * Creates and return an Interpreter. Plus, it sets adds the config class
+ * loader to the interpreter class loader.
+ *
+ * @return the created interpreter
+ */
+ private Interpreter createInterpreter() {
+ Interpreter interpreter = new Interpreter();
+
+ ConfigClassLoader cl =
+ (ConfigClassLoader)Configuration.getConfiguration().getClassLoader();
+
+ BshClassManager cm = interpreter.getClassManager();
+ URL[] urls = cl.getURLs();
+ for (int i=0; i0);
+ return new File(Configuration.getConfiguration().getConfigPath(), script);
+ }
+
+ /**
+ * Searches a match for the configured pattern with the HTTP
+ * header specified by header.
+ * If header or pattern is empty, isDeviceToProcess() returns always true.
+ * If the client does not provide the specified header, isDeviceToProcess()
+ * returns false.
+ *
+ * @param context message processing context
+ *
+ * @return true if the device must be proced by this synclet, false otherwise
+ */
+ private boolean isDeviceToProcess(MessageProcessingContext context) {
+ if ((pattern == null || pattern.trim().length() == 0)
+ || (header == null || header.trim().length() == 0)) {
+ return true;
+ }
+
+ Map headers =
+ (Map)context.getRequestProperty(context.PROPERTY_REQUEST_HEADERS);
+
+ //
+ // Search for the requested header value
+ //
+ String value = null;
+ Iterator i = headers.keySet().iterator();
+ while (i.hasNext()) {
+ String h = (String)i.next();
+ if (header.equalsIgnoreCase(h)) {
+ value = (String)headers.get(h);
+ break;
+ }
+ }
+
+ if ((value == null) || (value.trim().length() == 0)) {
+ //
+ // Header not found or not specified
+ //
+ return false;
+ }
+
+ Pattern p = Pattern.compile(pattern);
+ Matcher m = p.matcher(value);
+
+ return m.find();
+ }
+
+ /**
+ * Evaluates the init() method
+ *
+ * @param interpreter the interpreter into which evaluate the script
+ */
+ private void evalInitMethod(Interpreter interpreter) {
+ try {
+ interpreter.eval("init()");
+ } catch(Exception e) {
+ log.error("Error calling the init method", e);
+ }
+ }
+
+ // --------------------------------------------------- Static initialization
+
+ static {
+ interpreters = new HashMap();
+ }
+
+ // ----------------------------------------------------------- Inner classes
+
+ /**
+ * This class represent the key to use to put the interpreters instances
+ * into the hash map. It redefines hashCode() and equals() to the String
+ * script name's ones.
+ */
+ private class BeanShellEntry {
+ public long lastModified;
+ public InputMessageProcessor inputSynclet;
+ public OutputMessageProcessor outputSynclet;
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/SourceUriPrefixSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/SourceUriPrefixSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/SourceUriPrefixSynclet.java (revision 1009)
@@ -0,0 +1,521 @@
+/**
+ * Copyright (C) 2006-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import com.funambol.framework.core.AbstractCommand;
+import com.funambol.framework.core.Alert;
+import com.funambol.framework.core.Status;
+import com.funambol.framework.core.Results;
+import com.funambol.framework.core.Sync;
+import com.funambol.framework.core.Map;
+import com.funambol.framework.core.Item;
+import com.funambol.framework.core.DataStore;
+import com.funambol.framework.core.Source;
+import com.funambol.framework.core.Sync4jException;
+import com.funambol.framework.core.SyncBody;
+import com.funambol.framework.core.SyncML;
+import com.funambol.framework.core.Target;
+import com.funambol.framework.core.TargetRef;
+import com.funambol.framework.core.SourceRef;
+import com.funambol.framework.engine.pipeline.InputMessageProcessor;
+import com.funambol.framework.engine.pipeline.OutputMessageProcessor;
+import com.funambol.framework.engine.pipeline.MessageProcessingContext;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.protocol.ProtocolUtil;
+import com.funambol.framework.core.DevInfItem;
+
+/**
+ * This class strips the source URIs with a './' if needed.
+ *
+ * This is done in order to allow any client to sync with the source URIs
+ * defined, because some devices have a prefix of './'.
+ *
+ * @version $Id: SourceUriPrefixSynclet.java,v 1.4 2007-02-15 10:23:46 luigiafassina Exp $
+ */
+public class SourceUriPrefixSynclet
+implements InputMessageProcessor, OutputMessageProcessor {
+
+ // --------------------------------------------------------------- Constants
+ private static final String SYNCLET_NAME =
+ SourceUriPrefixSynclet.class.getName();
+
+ private static final String PROPERTY_SOURCENAME_CHANGED =
+ "funambol.foundation.sourceuriprefix.SOURCENAME_CHANGED";
+ // ------------------------------------------------------------ Private data
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Process input message and set MessageProcessingContext property.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message )
+ throws Sync4jException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".preProcessMessage(...)");
+ }
+
+ HashMap sourceReplacedMap =
+ (HashMap)processingContext.getSessionProperty(
+ PROPERTY_SOURCENAME_CHANGED);
+
+ if (sourceReplacedMap == null) {
+ sourceReplacedMap = new HashMap();
+ processingContext.setSessionProperty(PROPERTY_SOURCENAME_CHANGED,
+ sourceReplacedMap );
+ }
+
+ //
+ // Store and change the source uri into commands
+ //
+ manageInputAlert (message, sourceReplacedMap);
+ manageInputStatus(message, sourceReplacedMap);
+ manageInputSync (message, sourceReplacedMap);
+ manageInputMap (message, sourceReplacedMap);
+
+ }
+
+ /**
+ * Process and manipulate the output message.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".postProcessMessage(...)");
+ }
+
+ HashMap sourceReplacedMap =
+ (HashMap)processingContext.getSessionProperty(
+ PROPERTY_SOURCENAME_CHANGED);
+ if (!sourceReplacedMap.isEmpty()) {
+ //
+ // Replace original source uri into commands
+ //
+ manageOutputStatus (message, sourceReplacedMap);
+ manageOutputResults(message, sourceReplacedMap);
+ manageOutputAlert (message, sourceReplacedMap);
+ manageOutputSync (message, sourceReplacedMap);
+ }
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * Store and change Target URI into Alert commands
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap in which store uri to replace
+ */
+ private void manageInputAlert(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Strip TargetURI into input Alert commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allClientCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList alertList =
+ ProtocolUtil.filterCommands(allClientCommands, Alert.class);
+
+ Iterator itAlertList = alertList.iterator();
+
+ Alert alert = null;
+ ArrayList items = null;
+ Iterator itItem = null;
+ Item item = null;
+ Target target = null;
+ String targetUri = null;
+
+ while (itAlertList.hasNext()) {
+ alert = (Alert)itAlertList.next();
+ items = alert.getItems();
+ itItem = items.iterator();
+ while (itItem.hasNext()) {
+
+ item = (Item)itItem.next();
+ target = item.getTarget();
+ if (target != null) {
+ targetUri = target.getLocURI();
+ if (log.isTraceEnabled()) {
+ log.trace("original targetUri: " + targetUri);
+ }
+
+ if (targetUri.startsWith("./")) {
+ String targetNew = targetUri.substring(2);
+ if (log.isTraceEnabled()) {
+ log.trace("new targetUri: " + targetNew);
+ }
+ target.setLocURI(targetNew);
+
+ sourceReplacedMap.put(targetNew, targetUri);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Store and change SourceRef into Status commmads
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap in which store uri to replace
+ */
+ private void manageInputStatus(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Strip SourceRef into input Status commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allClientCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList statusList =
+ ProtocolUtil.filterCommands(allClientCommands, Status.class);
+
+ Iterator itStatusList = statusList.iterator();
+
+ Status status = null;
+ while (itStatusList.hasNext()) {
+ status = (Status)itStatusList.next();
+ SourceRef[] srefs =
+ (SourceRef[])status.getSourceRef().toArray(new SourceRef[0]);
+ int s = srefs.length;
+ for(int i=0;i 0) {
+ if (items[0] instanceof DevInfItem) {
+ DevInfItem item = (DevInfItem) items[0];
+ ArrayList dss = item.getDevInfData().getDevInf().getDataStores();
+ int s = dss.size();
+ for (int i = 0; i < s; i++) {
+ DataStore ds = (DataStore) dss.get(i);
+ String sourceRef = ds.getSourceRef().getValue();
+ if (sourceReplacedMap.containsKey(sourceRef)) {
+ ds.getSourceRef().setValue((String) sourceReplacedMap.
+ get(sourceRef));
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replace Source into Sync commands
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap with the uri to replace
+ */
+ private void manageOutputSync(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Replace Source into output Sync commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allServerCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList syncList =
+ ProtocolUtil.filterCommands(allServerCommands, Sync.class);
+
+ Iterator itSyncList = syncList.iterator();
+
+ Sync sync = null;
+ Source source = null;
+ String sourceUri = null;
+
+ while (itSyncList.hasNext()) {
+ sync = (Sync) itSyncList.next();
+
+ source = sync.getSource();
+
+ if (source != null) {
+ sourceUri = source.getLocURI();
+
+ if (sourceReplacedMap.containsKey(sourceUri)) {
+ //
+ // Here we have to create a new Source object because
+ // the Sync can contain the same Source used in the
+ // engine/sessionHandler to handle the Database.
+ // If here we change the source object of the sync commands we
+ // change also the source of the database because it's
+ // the same object!!.
+ //
+ //
+ Source newSource = new Source(
+ (String) sourceReplacedMap.get(sourceUri),
+ source.getLocName()
+ );
+ sync.setSource(newSource);
+ }
+ }
+ }
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeDeviceIdSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeDeviceIdSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeDeviceIdSynclet.java (revision 1009)
@@ -0,0 +1,261 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import java.util.*;
+
+import com.funambol.framework.core.*;
+import com.funambol.framework.engine.pipeline.*;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.protocol.ProtocolUtil;
+
+/**
+ * This class changes the input message client deviceId with a fixed deviceId.
+ * This is done in order to allow any phone to sync without the need of adding
+ * a new device/user/principal in the SyncAdmin. This is for the sake of easy
+ * of use for beginners. To disable this behaviuor, just remove the synclet
+ * from the pipeline.
+ * The original device id will is replaced in the outgoing message so that the
+ * phone device will not notice the change.
+ *
+ *
The change is done only if the sync sources to sync are in the given
+ * syncSourcesToProcess list and the client device id is not in the
+ * devicesNotToProcess list.
+ *
+ * This synclet uses the following context properties:
+ *
+ *
funambol.foundation.changedevice.DEVICEID_ORIG - to store the original device id
+ *
+ *
+ * Note that because the Alert commands containing the syncsource to sync are
+ * given only in the first message, we have to remember if we are changing the
+ * deviceid or not for all the messages in the session. For this, we just check
+ * if clientDeviceId is already set. If not, we check for the alert; if yes, it
+ * means that we have already determined that the device id needs to be replaced,
+ * therefore we replace it without further checks.
+ *
+ *
If the DEVICEID_ORIG in the processingContext is null, no change is performed.
+ *
+ * @version $Id: ChangeDeviceIdSynclet.java,v 1.3 2007-01-11 19:20:19 nichele Exp $
+ */
+public class ChangeDeviceIdSynclet
+implements InputMessageProcessor, OutputMessageProcessor {
+
+ // --------------------------------------------------------------- Constants
+ private static final String PROPERTY_ORIG_ID =
+ "funambol.foundation.changedevice.DEVICEID_ORIG";
+
+ private static final String CLIENT_DEVICEID = "syncml-phone";
+
+ private static final String SYNCLET_NAME = ChangeDeviceIdSynclet.class.getName();
+
+ // ------------------------------------------------------------ Private data
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ /**
+ * Contain the original client device Id
+ */
+ private String clientDeviceId = null;
+
+ /**
+ * Contains the uri of the sync sources to process.
+ * If in the message to process, there are a syncsource specified in the list,
+ * then the message is processed
+ */
+ private ArrayList syncSourcesToProcess = null;
+
+ /**
+ * Contains the list of the known devices of which the ID doesn't have
+ * to be changed.
+ */
+ private ArrayList devicesNotToProcess = null;
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Process input message and set MessageProcessingContext property.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".preProcessMessage(...)");
+ }
+
+ boolean processMessage = true;
+
+ if (clientDeviceId == null) {
+ processMessage = checkSourceUriToSync(message);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + " - device id replacement " + (processMessage ? "" : "not ") + "required");
+ }
+
+ if (processMessage) {
+ if (clientDeviceId == null) {
+ clientDeviceId = message.getSyncHdr().getSource().getLocURI();
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + " - original clientDeviceId '" + clientDeviceId + "'");
+ }
+
+ if (!devicesNotToProcess.contains(clientDeviceId)) {
+ Source source = message.getSyncHdr().getSource();
+ source.setLocURI(CLIENT_DEVICEID);
+ processingContext.setRequestProperty(PROPERTY_ORIG_ID, clientDeviceId);
+ }
+ }
+ }
+
+
+ /**
+ * Process and manipulate the output message.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".postProcessMessage(...)");
+ }
+
+ if (clientDeviceId == null) {
+ clientDeviceId = (String)processingContext.getRequestProperty(PROPERTY_ORIG_ID);
+ }
+
+ //
+ // If still null, no processing is required
+ //
+ if (clientDeviceId == null) {
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + " - processing not required");
+ }
+ } else {
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + " - setting device id to '" + clientDeviceId + "'");
+ }
+
+ Target target = message.getSyncHdr().getTarget();
+ target.setLocURI(clientDeviceId);
+
+ //
+ // We have also to change the SourceRef in the SyncHdr's status
+ //
+ SyncBody body = message.getSyncBody();
+ AbstractCommand[] allServerCommands =
+ (AbstractCommand[])body.getCommands().toArray(new AbstractCommand[0]);
+
+ ArrayList listStatus =
+ ProtocolUtil.filterCommands(allServerCommands,
+ Status.class);
+
+ if (listStatus != null && listStatus.size() != 0) {
+
+ Status status = (Status) listStatus.get(0);
+ List sourceRefList = status.getSourceRef();
+ if (sourceRefList != null && sourceRefList.size() != 0) {
+ SourceRef sourceRef =
+ (SourceRef)sourceRefList.get(0);
+ sourceRef.setValue(clientDeviceId);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Set the syncSourcesToProcess
+ * @param list the syncSourcesToProcess
+ */
+ public void setSyncSourcesToProcess(ArrayList list) {
+ this.syncSourcesToProcess = list;
+ }
+
+ /**
+ * Set the devicesNotToProcess
+ *
+ * @param list the devicesNotToProcess
+ */
+ public void setDevicesNotToProcess(ArrayList list) {
+ this.devicesNotToProcess = list;
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * Checks if the sync sources that are in the syncml message are present
+ * in the list syncSourcesToProcess
+ *
+ * @param message the message to process
+ * @return boolean
+ */
+ private boolean checkSourceUriToSync(SyncML message) {
+
+ if (syncSourcesToProcess == null) {
+ return false;
+ }
+
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allClientCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList alertList = ProtocolUtil.filterCommands(allClientCommands,
+ Alert.class);
+
+ Iterator itAlertList = alertList.iterator();
+
+ Alert alert = null;
+ ArrayList items = null;
+ Iterator itItem = null;
+ Item item = null;
+ Target target = null;
+ String sourceUri = null;
+
+ while (itAlertList.hasNext()) {
+ alert = (Alert)itAlertList.next();
+ items = alert.getItems();
+ itItem = items.iterator();
+ while (itItem.hasNext()) {
+
+ item = (Item)itItem.next();
+ target = item.getTarget();
+ if (target != null) {
+ sourceUri = target.getLocURI();
+
+ if (syncSourcesToProcess.indexOf(sourceUri) != -1) {
+ // the source uri is present in the list of the
+ // sync source to process
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/StopProcessingSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/StopProcessingSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/StopProcessingSynclet.java (revision 1009)
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import com.funambol.framework.core.SyncML;
+import com.funambol.framework.core.Sync4jException;
+import com.funambol.framework.engine.pipeline.InputMessageProcessor;
+import com.funambol.framework.engine.pipeline.OutputMessageProcessor;
+import com.funambol.framework.engine.pipeline.MessageProcessingContext;
+import com.funambol.framework.engine.pipeline.StopProcessingException;
+
+/**
+ * This synclet simply throws a StopProcessingException at each call. It can be
+ * useful for debugging purposes.
+ *
+ * @version $Id: StopProcessingSynclet.java,v 1.2 2007-01-11 19:20:19 nichele Exp $
+ */
+public class StopProcessingSynclet
+implements InputMessageProcessor, OutputMessageProcessor {
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Throws a StopProcessingException.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ throw new StopProcessingException("Please stop input procesing!");
+ }
+
+ /**
+ * Logs the output message and context
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ throw new StopProcessingException("Please stop output procesing!");
+ }
+
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/PIMItemsHandler.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/PIMItemsHandler.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/PIMItemsHandler.java (revision 1009)
@@ -0,0 +1,387 @@
+/**
+ * Copyright (C) 2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.funambol.framework.core.AbstractCommand;
+import com.funambol.framework.core.Add;
+import com.funambol.framework.core.Item;
+import com.funambol.framework.core.ItemizedCommand;
+import com.funambol.framework.core.Meta;
+import com.funambol.framework.core.Replace;
+import com.funambol.framework.core.Sync;
+import com.funambol.framework.core.Sync4jException;
+import com.funambol.framework.core.SyncML;
+import com.funambol.framework.engine.pipeline.MessageProcessingContext;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+
+/**
+ * This class extracts the PIM Items from the SyncML message.
+ * It creates a Map with three lists that contain the items separated based on
+ * their type. In case of large object, this class is able to recognize the
+ * large object item and is able to handle it using a Map cached in the
+ * MessageProcessingContext.
+ *
+ * @version $Id: PIMItemsHandler.java,v 1.1 2007-02-02 10:33:13 luigiafassina Exp $
+ */
+public class PIMItemsHandler {
+
+ // --------------------------------------------------------------- Constants
+ private final FunambolLogger logger =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ private static final String BEGIN_VCARD = "BEGIN:VCARD" ;
+ private static final String END_VCARD_RN = "END:VCARD\r\n" ;
+ private static final String END_VCARD_N = "END:VCARD\n" ;
+ private static final String BEGIN_VCALENDAR = "BEGIN:VCALENDAR" ;
+ private static final String END_VEVENT_RN =
+ "END:VEVENT\r\nEND:VCALENDAR\r\n";
+ private static final String END_VEVENT_N = "END:VEVENT\nEND:VCALENDAR\n";
+ private static final String END_VTODO_RN =
+ "END:VTODO\r\nEND:VCALENDAR\r\n" ;
+ private static final String END_VTODO_N = "END:VTODO\nEND:VCALENDAR\n" ;
+
+ public static final String KEY_VCARD = "VCARD" ;
+ public static final String KEY_VEVENT = "VEVENT";
+ public static final String KEY_VTODO = "VTODO" ;
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Processes input message to extract the items in order to separate them
+ * into lists based on their type (vcard, vevent, vtodo).
+ *
+ * @param pContext the message processing context
+ * @param message the message to be processed
+ *
+ * @return mapItems the map with the three list of items
+ * @throws Sync4jException in case of errors
+ */
+ public Map extractIncomingPIMItems(MessageProcessingContext pContext,
+ SyncML message)
+ throws Sync4jException {
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Starting creation of incoming items lists...");
+ }
+
+ //
+ // Caches the map with lists of items into request context: in this way,
+ // if more synclet act on the same items, is not need to recycle on
+ // items of the message
+ //
+
+ //
+ // Contains the lists of the items
+ //
+ Map mapItems = null;
+ mapItems = (Map)pContext.getRequestProperty("MAP_INCOMING_ITEMS");
+
+ if (mapItems != null) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Exists a map of items: is not need to cycle on message");
+ }
+
+ //
+ // In this case the first synclet has just created the map and so
+ // the next synclets must act on that without recycle in message.
+ //
+ return mapItems;
+ } else {
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Creates a map to contains the lists of items");
+ }
+ mapItems = new HashMap();
+ initializeMapItems(mapItems);
+ }
+
+ //
+ // Used to handle large object item
+ //
+ Item firstItem = null;
+ Item lastItem = null;
+ //
+ // Cache the large object
+ //
+ Map cache = null;
+ cache = (Map)pContext.getSessionProperty("LARGE_OBJ_INCOMING");
+
+ String syncURI = null;
+ String itemLocURI = null;
+
+ List cmds = message.getSyncBody().getCommands();
+ for (AbstractCommand bodyc : cmds) {
+
+ if (bodyc instanceof Sync) {
+ syncURI = ((Sync)bodyc).getTarget().getLocURI();
+
+ //
+ // Processes incoming commands to identifier and separate the items.
+ //
+ List syncCmds = ((Sync)bodyc).getCommands();
+ for (ItemizedCommand c : syncCmds) {
+
+ //
+ // Skip other commands than Add and Replace
+ //
+ if (!(Replace.COMMAND_NAME.equals(c.getName()) ||
+ Add.COMMAND_NAME.equals(c.getName())) ) {
+ continue;
+ }
+
+ List items = c.getItems();
+ for (Item item: items) {
+
+ itemLocURI = ((item.getSource() != null)
+ ? item.getSource().getLocURI()
+ : item.getTarget().getLocURI());
+
+ if (firstItem == null) {
+ firstItem = item;
+ }
+
+ if (firstItem == item) {
+ if (cache == null || cache.isEmpty()) {
+ if (item.isMoreData()) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("The item " + itemLocURI +
+ " is a large object");
+ }
+ cache = handleCache(cache, item, syncURI);
+ }
+ } else {
+ cache = handleCache(cache, item, syncURI);
+ }
+ } //end if firstItem == item
+
+ lastItem = item;
+
+ if (!item.isMoreData()) {
+ mapItems = fillMapItems(mapItems, item);
+ }
+
+ } //end for items
+ } //end for cmds
+ } //end if Sync
+
+ if (lastItem != firstItem) {
+ if (lastItem.isMoreData()) {
+ cache = handleCache(cache, lastItem, syncURI);
+ }
+ }
+ } //end if cmds
+
+ pContext.setSessionProperty("LARGE_OBJ_INCOMING", cache);
+ pContext.setRequestProperty("MAP_INCOMING_ITEMS", mapItems);
+
+ return mapItems;
+ }
+
+ /**
+ * Processes output message to extract the items in order to separate them
+ * into lists based on their type (vcard, vevent, vtodo).
+ *
+ * @param message the message to be processed
+ *
+ * @return mapItems the map with the three list of items
+ * @throws Sync4jException in case of errors
+ */
+ public Map extractOutgoingPIMItems(SyncML message)
+ throws Sync4jException {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Starting creation of outgoing items lists...");
+ }
+
+ Map mapItems = new HashMap();
+ initializeMapItems(mapItems);
+
+ List cmds = message.getSyncBody().getCommands();
+ for (AbstractCommand bodyc : cmds) {
+
+ if (bodyc instanceof Sync) {
+
+ //
+ // Processes incoming commands to identifier and separate the items.
+ // Note: the large object items will not be considered.
+ //
+ List syncCmds = ((Sync)bodyc).getCommands();
+ for (ItemizedCommand c : syncCmds) {
+
+ //
+ // Skip other commands than Add and Replace
+ //
+ if (!(Replace.COMMAND_NAME.equals(c.getName()) ||
+ Add.COMMAND_NAME.equals(c.getName())) ) {
+ continue;
+ }
+
+ List items = c.getItems();
+ for (Item item: items) {
+ mapItems = fillMapItems(mapItems, item);
+ }//end for i items
+ }//end for c commands
+ }//end if Sync
+ }
+
+ return mapItems;
+ }
+
+ /**
+ * If the item has the size set, probably it is a large object item. In this
+ * case, after the synclet modifications, is need to fix the size property
+ * with the real dimension of the data's item.
+ *
+ * @param i the item to fix the size property
+ */
+ public void fixLargeObjectSize(Item i) {
+
+ Meta m = i.getMeta();
+ if (m == null) {
+ return;
+ }
+ if (m.getSize() != null && m.getSize() != 0) {
+ int size = i.getData().getData().length();
+ if (logger.isTraceEnabled()) {
+ logger.trace("Fixed size of large object item from " +
+ m.getSize() + " to " + size);
+ }
+ i.getMeta().setSize(new Long(size));
+ }
+ }
+
+ // --------------------------------------------------------- Private methods
+
+ /**
+ * Handles large object storing its information into a Map.
+ *
+ * @param cache The map in which store the large object informations
+ * @param item the large object
+ * @param source the source with LocURI of item
+ *
+ * @return the map in which the large object is cached
+ */
+ private Map handleCache(Map cache, Item item, String source) {
+
+ if (cache == null || cache.isEmpty()) {
+ //
+ // It is the first chunk of the large object item
+ //
+ String uri = (item.getSource() != null)
+ ? item.getSource().getLocURI()
+ : item.getTarget().getLocURI()
+ ;
+ cache = new HashMap();
+ cache.put("itemLocURI", uri);
+ cache.put("syncLocURI", source);
+ cache.put("data", item.getData().getData());
+
+ } else {
+
+ String cacheData = (String)cache.get("data");
+ String cacheLocURI = (String)cache.get("syncLocURI")
+ + "/"
+ + (String)cache.get("itemLocURI")
+ ;
+
+ String itemLocURI = source + "/" + ((item.getSource() != null)
+ ? item.getSource().getLocURI()
+ : item.getTarget().getLocURI());
+
+ if (cacheLocURI.equals(itemLocURI)) {
+ cacheData = cacheData + item.getData().getData();
+
+ if (item.isMoreData()) {
+ cache.put("data", cacheData);
+ } else {
+ item.getData().setData(cacheData);
+ if (item.getMeta() == null) {
+ item.setMeta(new Meta());
+ }
+ item.getMeta().setSize(new Long(cacheData.length()));
+ cache.clear();
+ }
+ } else {
+ cache.clear();
+ }
+ }
+
+ return cache;
+ }
+
+ /**
+ * Fills the maps of items separated based on their type (vcard, vevent,
+ * vtodo).
+ *
+ * @param mapItems the map in which add the list of items separated based on
+ * them type
+ * @param item the item to handle
+ *
+ * @return the map with lists of items
+ */
+ private Map fillMapItems(Map mapItems, Item item) {
+
+ List vcardItems = (List)mapItems.get(KEY_VCARD );
+ List veventItems = (List)mapItems.get(KEY_VEVENT);
+ List vtodoItems = (List)mapItems.get(KEY_VTODO );
+
+ String data = item.getData().getData();
+
+ if ( data.startsWith(BEGIN_VCARD) &&
+ (data.endsWith(END_VCARD_RN) ||
+ data.endsWith(END_VCARD_N) )) {
+
+ vcardItems.add(item);
+ } else if (data.startsWith(BEGIN_VCALENDAR)) {
+ if (data.endsWith(END_VEVENT_RN) ||
+ data.endsWith(END_VEVENT_N) ) {
+
+ veventItems.add(item);
+ } else if (data.endsWith(END_VTODO_RN) ||
+ data.endsWith(END_VTODO_N) ) {
+
+ vtodoItems.add(item);
+ }
+ }
+ if (logger.isTraceEnabled()) {
+ logger.trace("List vcard : " + vcardItems.toString() );
+ logger.trace("List vevent: " + veventItems.toString());
+ logger.trace("List vtodo : " + vtodoItems.toString() );
+ }
+ return mapItems;
+ }
+
+ /**
+ * Initializes the Map of items.
+ *
+ * @param mapItems the empty map
+ */
+ private void initializeMapItems(Map mapItems) {
+
+ List vcardItems = new ArrayList();
+ List veventItems = new ArrayList();
+ List vtodoItems = new ArrayList();
+
+ mapItems.put(KEY_VCARD , vcardItems );
+ mapItems.put(KEY_VEVENT, veventItems);
+ mapItems.put(KEY_VTODO , vtodoItems );
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeSourceUriSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeSourceUriSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/ChangeSourceUriSynclet.java (revision 1009)
@@ -0,0 +1,528 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import java.util.*;
+
+import com.funambol.framework.core.*;
+import com.funambol.framework.core.Map;
+import com.funambol.framework.engine.pipeline.*;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.protocol.ProtocolUtil;
+
+/**
+ * This class changes the input message client source URI with a correspondent
+ * server source URI
+ *
+ * This is done in order to allow any client to sync with different source URI
+ * using the same SyncSource. For example into phone is possible to write
+ * ./contact or only contact to synchorized with the same contact SyncSource.
+ *
+ * @version $Id: ChangeSourceUriSynclet.java,v 1.5 2007-02-15 10:23:46 luigiafassina Exp $
+ */
+public class ChangeSourceUriSynclet
+implements InputMessageProcessor, OutputMessageProcessor {
+
+ // --------------------------------------------------------------- Constants
+ private static final String SYNCLET_NAME =
+ ChangeSourceUriSynclet.class.getName();
+ private static final String PROPERTY_SOURCENAME_CHANGED =
+ "funambol.foundation.changesourcename.SOURCENAME_CHANGED";
+
+ // ------------------------------------------------------------ Private data
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ private HashMap sourceNameMapping = null;
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Process input message and set MessageProcessingContext property.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message )
+ throws Sync4jException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".preProcessMessage(...)");
+ }
+
+ HashMap sourceReplacedMap =
+ (HashMap)processingContext.getSessionProperty(
+ PROPERTY_SOURCENAME_CHANGED);
+
+ if (sourceReplacedMap == null) {
+ sourceReplacedMap = new HashMap();
+ processingContext.setSessionProperty(PROPERTY_SOURCENAME_CHANGED,
+ sourceReplacedMap );
+ }
+
+ //
+ // Store and change the source uri into commands
+ //
+ manageInputAlert (message, sourceReplacedMap);
+ manageInputStatus(message, sourceReplacedMap);
+ manageInputSync (message, sourceReplacedMap);
+ manageInputMap (message, sourceReplacedMap);
+
+ }
+
+ /**
+ * Process and manipulate the output message.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+ if (log.isTraceEnabled()) {
+ log.trace(SYNCLET_NAME + ".postProcessMessage(...)");
+ }
+
+ HashMap sourceReplacedMap =
+ (HashMap)processingContext.getSessionProperty(
+ PROPERTY_SOURCENAME_CHANGED);
+ if (!sourceReplacedMap.isEmpty()) {
+ //
+ // Replace original source uri into commands
+ //
+ manageOutputStatus (message, sourceReplacedMap);
+ manageOutputResults(message, sourceReplacedMap);
+ manageOutputAlert (message, sourceReplacedMap);
+ manageOutputSync (message, sourceReplacedMap);
+ }
+ }
+
+ /**
+ * Set the hashmap with sources name's to change
+ *
+ * @param map
+ */
+ public void setSourceNameMapping(HashMap map) {
+ this.sourceNameMapping = map;
+ }
+
+ /**
+ * Get the hashmap with sources name's to change
+ *
+ * @return the map
+ */
+ public HashMap getSourceNameMapping() {
+ return this.sourceNameMapping;
+ }
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * Store and change Target URI into Alert commands
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap in which store uri to replace
+ */
+ private void manageInputAlert(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Change TargetURI into input Alert commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allClientCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList alertList =
+ ProtocolUtil.filterCommands(allClientCommands, Alert.class);
+
+ Iterator itAlertList = alertList.iterator();
+
+ Alert alert = null;
+ ArrayList items = null;
+ Iterator itItem = null;
+ Item item = null;
+ Target target = null;
+ String targetUri = null;
+
+ while (itAlertList.hasNext()) {
+ alert = (Alert)itAlertList.next();
+ items = alert.getItems();
+ itItem = items.iterator();
+ while (itItem.hasNext()) {
+
+ item = (Item)itItem.next();
+ target = item.getTarget();
+ if (target != null) {
+ targetUri = target.getLocURI();
+ if (log.isTraceEnabled()) {
+ log.trace("original targetUri: " + targetUri);
+ }
+
+ if (sourceNameMapping.containsKey(targetUri)) {
+ String targetNew =
+ (String)sourceNameMapping.get(targetUri);
+
+ if (log.isTraceEnabled()) {
+ log.trace("new targetUri: " + targetNew);
+ }
+ target.setLocURI(targetNew);
+
+ sourceReplacedMap.put(targetNew, targetUri);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Store and change SourceRef into Status commmads
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap in which store uri to replace
+ */
+ private void manageInputStatus(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Change SourceRef into input Status commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allClientCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList statusList =
+ ProtocolUtil.filterCommands(allClientCommands, Status.class);
+
+ Iterator itStatusList = statusList.iterator();
+
+ Status status = null;
+ while (itStatusList.hasNext()) {
+ status = (Status)itStatusList.next();
+ SourceRef[] srefs =
+ (SourceRef[])status.getSourceRef().toArray(new SourceRef[0]);
+ int s = srefs.length;
+ for(int i=0;i 0) {
+ if (items[0] instanceof DevInfItem) {
+ DevInfItem item = (DevInfItem) items[0];
+ ArrayList dss = item.getDevInfData().getDevInf().getDataStores();
+ int s = dss.size();
+ for (int i = 0; i < s; i++) {
+ DataStore ds = (DataStore) dss.get(i);
+ String sourceRef = ds.getSourceRef().getValue();
+ if (sourceReplacedMap.containsKey(sourceRef)) {
+ ds.getSourceRef().setValue((String) sourceReplacedMap.
+ get(sourceRef));
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replace Source into Sync commands
+ *
+ * @param message the client message
+ * @param sourceReplacedMap the HashMap with the uri to replace
+ */
+ private void manageOutputSync(SyncML message, HashMap sourceReplacedMap) {
+ if (log.isTraceEnabled()) {
+ log.trace("Replace Source into output Sync commands");
+ }
+ SyncBody syncBody = message.getSyncBody();
+
+ AbstractCommand[] allServerCommands =
+ (AbstractCommand[])syncBody.getCommands().toArray(
+ new AbstractCommand[0]);
+
+ ArrayList syncList =
+ ProtocolUtil.filterCommands(allServerCommands, Sync.class);
+
+ Iterator itSyncList = syncList.iterator();
+
+ Sync sync = null;
+ Source source = null;
+ String sourceUri = null;
+
+ while (itSyncList.hasNext()) {
+ sync = (Sync) itSyncList.next();
+
+ source = sync.getSource();
+
+ if (source != null) {
+ sourceUri = source.getLocURI();
+
+ if (sourceReplacedMap.containsKey(sourceUri)) {
+ //
+ // Here we have to create a new Source object because
+ // the Sync can contain the same Source used in the
+ // engine/sessionHandler to handle the Database.
+ // If here we change the source object of the sync commands we
+ // change also the source of the database because it's
+ // the same object!!.
+ //
+ //
+ Source newSource = new Source(
+ (String) sourceReplacedMap.get(sourceUri),
+ source.getLocName()
+ );
+ sync.setSource(newSource);
+ }
+ }
+ }
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/LoggingSynclet.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/LoggingSynclet.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/synclet/LoggingSynclet.java (revision 1009)
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.synclet;
+
+import com.funambol.framework.core.Sync4jException;
+import com.funambol.framework.core.SyncML;
+import com.funambol.framework.engine.pipeline.*;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.tools.SyncMLUtil;
+
+/**
+ * This class just logs input and output messages. This synclet can then be
+ * inserted in any position in a pipeline in order to trace how a message is
+ * changed during the pipeline processing. It is at the same time an input and
+ * an output pipeline component.
+ * Logging is done at INFO level
+ *
+ * @version $Id: LoggingSynclet.java,v 1.4 2007-01-11 19:20:19 nichele Exp $
+ */
+public class LoggingSynclet implements InputMessageProcessor, OutputMessageProcessor {
+
+ // ------------------------------------------------------------ Private data
+
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Logs the input message and context
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ if (log.isInfoEnabled()) {
+ log.info("--------------------------------------------------------------------------------");
+ log.info("Input message processing context" );
+ log.info(" ");
+ log.info(processingContext.toString() );
+ log.info("--------------------------------------------------------------------------------");
+ log.info("Input message" );
+ log.info(" ");
+ log.info(SyncMLUtil.toXML(message) );
+ log.info("--------------------------------------------------------------------------------");
+ }
+ }
+
+ /**
+ * Logs the output message and context
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ *
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message)
+ throws Sync4jException {
+ if (log.isInfoEnabled()) {
+ log.info("--------------------------------------------------------------------------------");
+ log.info("Output message processing context" );
+ log.info(" ");
+ log.info(processingContext.toString() );
+ log.info("--------------------------------------------------------------------------------");
+ log.info("Output message" );
+ log.info(" ");
+ log.info(SyncMLUtil.toXML(message) );
+ log.info("--------------------------------------------------------------------------------");
+ }
+ }
+
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMContactSyncSource.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMContactSyncSource.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMContactSyncSource.java (revision 1009)
@@ -0,0 +1,621 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.engine.source;
+
+import java.io.ByteArrayInputStream;
+import java.sql.Timestamp;
+import java.util.List;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.items.manager.PIMContactManager;
+
+import com.funambol.common.pim.contact.Contact;
+import com.funambol.common.pim.converter.ContactToSIFC;
+import com.funambol.common.pim.converter.ContactToVcard;
+import com.funambol.common.pim.sif.SIFCParser;
+import com.funambol.common.pim.vcard.VcardParser;
+import com.funambol.framework.engine.SyncItem;
+import com.funambol.framework.engine.SyncItemImpl;
+import com.funambol.framework.engine.SyncItemKey;
+import com.funambol.framework.engine.SyncItemState;
+import com.funambol.framework.engine.source.SyncContext;
+import com.funambol.framework.engine.source.SyncSourceException;
+import com.funambol.framework.tools.beans.BeanInitializationException;
+
+public class PIMContactSyncSource extends PIMSyncSource {
+
+ // --------------------------------------------------------------Private
+ // data
+
+ private transient PIMContactManager manager;
+
+ // ------------------------------------------------------------Public
+ // methods
+
+ @Override
+ public void beginSync(SyncContext context) {
+
+ this.manager = new PIMContactManager(JNDI_DATA_SOURCE_NAME, context
+ .getPrincipal());
+ super.manager = this.manager;
+ super.beginSync(context);
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing the ID(s) of the
+ * twin(s) of a given contact.
+ *
+ * @param syncItem
+ * the SyncItem representing the contact whose twin(s) are looked
+ * for
+ * @throws SyncSourceException
+ * @return possibly, just one or no key should be in the array, but it can't
+ * be ruled out a priori that several keys get returned by this
+ * method
+ */
+ @Override
+ public SyncItemKey[] getSyncItemKeysFromTwin(SyncItem syncItem)
+ throws SyncSourceException {
+
+ try {
+ List idList = this.manager.getTwins(convert(syncItem));
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey(idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving twin item keys.", e);
+ }
+ }
+
+ /**
+ * Adds a SyncItem object (representing a contact).
+ *
+ * @param syncItem
+ * the SyncItem representing the contact
+ *
+ * @return a newly created syncItem based on the input object but with its
+ * status set at SyncItemState.NEW and the GUID retrieved by the
+ * back-end
+ */
+ @Override
+ public SyncItem addSyncItem(SyncItem syncItem) throws SyncSourceException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactSyncSource addSyncItem begin");
+ }
+
+ Contact c = null;
+ String content = null;
+
+ try {
+
+ content = getContentFromSyncItem(syncItem);
+ String contentType = syncItem.getType();
+
+ c = convert(content, contentType);
+ Timestamp ts = syncItem.getTimestamp();
+
+ // Adds the contact, wraps it in sync information and uses it to
+ // create a new SyncItem which is the return value of this method
+ SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
+ this.manager.addItem(c, ts), // key
+ null, // mappedKey
+ SyncItemState.NEW, // state
+ content.getBytes(), // content
+ null, // format
+ contentType, // type
+ ts // timestamp
+ );
+
+ return newSyncItem;
+
+ } catch (Exception e) {
+ log.error("SyncSource error adding a new synchronization item", e);
+ throw new SyncSourceException("Error adding the item " + syncItem,
+ e);
+ }
+ }
+
+ /**
+ * Updates a SyncItem object (representing a contact).
+ *
+ * @param syncItem
+ * the SyncItem representing the contact
+ *
+ * @return a newly created syncItem based on the input object but with its
+ * status set at SyncItemState.UPDATED and the GUID retrieved by the
+ * back-end
+ */
+ @Override
+ public SyncItem updateSyncItem(SyncItem syncItem)
+ throws SyncSourceException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("updateSyncItem from " + this.sourceURI);
+ }
+
+ Contact c = null;
+ String content = null;
+
+ try {
+
+ String id = syncItem.getKey().getKeyAsString();
+ content = getContentFromSyncItem(syncItem);
+ String contentType = syncItem.getType();
+
+ c = convert(content, contentType);
+
+ // Modifies the contact, wraps it in sync information and uses it to
+ // create a new SyncItem which is the return value of this method
+ SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
+ this.manager.updateItem(id, c, syncItem.getTimestamp()), // key
+ null, // mappedKey
+ SyncItemState.UPDATED, // state
+ content.getBytes(), // content
+ null, // format
+ contentType, // type
+ null // timestamp
+ );
+
+ return newSyncItem;
+
+ } catch (Exception e) {
+ log
+ .error(
+ "SyncSource error updating a new synchronization item",
+ e);
+ throw new SyncSourceException(
+ "Error updating the item " + syncItem, e);
+ }
+ }
+
+ /**
+ * Deletes the item with a given syncItemKey.
+ *
+ * @param syncItemKey
+ * @param timestamp
+ * in case of a soft deletion, this will be the registered moment
+ * of deletion; if a hard deletion is used, this field is
+ * irrelevant and may also be null
+ * @param softDelete
+ * it is true if the client requires a soft deletion
+ * @throws SyncSourceException
+ */
+ @Override
+ public void removeSyncItem(SyncItemKey syncItemKey, Timestamp timestamp,
+ boolean softDelete) throws SyncSourceException {
+
+ try {
+
+ if (!softDelete) {
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactSyncSource remove the SyncItem");
+ }
+
+ this.manager.removeItem(syncItemKey.getKeyAsString());
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactSyncSource removed SyncItem");
+ }
+ }
+
+ } catch (EntityException e) {
+ log.error("Sync source error: could not delete item with key"
+ + syncItemKey, e);
+ throw new SyncSourceException("Error deleting item. ", e);
+ }
+
+ }
+
+ @Override
+ public SyncItem getSyncItemFromId(SyncItemKey syncItemKey)
+ throws SyncSourceException {
+
+ String id = null;
+ SyncItem syncItem = null;
+
+ id = syncItemKey.getKeyAsString();
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactSyncSource get SyncItem from id " + id);
+ }
+
+ try {
+
+ // Retrieves the contact, wraps it in sync information and uses it
+ // to create a new SyncItem which is the return value of this method
+ syncItem = createSyncItem(id,
+ this.manager.getItem(id).getContact(), SyncItemState.NEW);
+
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error seeking SyncItem with ID: "
+ + id, e);
+ }
+
+ return syncItem;
+
+ }
+
+ public boolean mergeSyncItems(SyncItemKey syncItemKey, SyncItem syncItem)
+ throws SyncSourceException {
+
+ try {
+
+ Contact contact = convert(getContentFromSyncItem(syncItem),
+ syncItem.getType());
+
+ boolean clientUpdateRequired = this.manager.mergeItems(syncItemKey
+ .getKeyAsString(), contact, syncItem.getTimestamp());
+
+ if (clientUpdateRequired) {
+ syncItem = getSyncItemFromId(syncItemKey);
+ }
+
+ // return clientUpdateRequired;
+ return true;
+
+ } catch (EntityException e) {
+
+ log.error("SyncSource error: a merge did not succeed.", e);
+ throw new SyncSourceException("Error merging SyncItem with key '"
+ + syncItemKey + "' with SyncItem '" + syncItem + "'", e);
+ }
+ }
+
+ public void init() throws BeanInitializationException {
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all new contact IDs,
+ * filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ @Override
+ public SyncItemKey[] getNewSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = this.manager.getNewItems(since, to);
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey(idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving new item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all deleted contact
+ * IDs, filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ public SyncItemKey[] getUpdatedSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = this.manager.getUpdatedItems(since, to);
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey(idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException(
+ "Error retrieving updated item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all deleted contact
+ * IDs, filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ @Override
+ public SyncItemKey[] getDeletedSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = this.manager.getDeletedItems(since, to);
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey(idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException(
+ "Error retrieving deleted item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all contact IDs.
+ *
+ * @return a SyncItemKey array
+ */
+ @Override
+ public SyncItemKey[] getAllSyncItemKeys() throws SyncSourceException {
+
+ try {
+ List idList = this.manager.getAllItems();
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey(idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving all item keys. ", e);
+ }
+
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ private Contact sif2Contact(String sifc) throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(sifc.length() + 60);
+ sb.append("Converting: SIF-C => Contact").append("\nINPUT = {")
+ .append(sifc).append('}');
+ log.trace(sb.toString());
+ }
+
+ ByteArrayInputStream buffer = null;
+ SIFCParser parser = null;
+ Contact contact = null;
+ try {
+ contact = new Contact();
+ buffer = new ByteArrayInputStream(sifc.getBytes());
+ if ((sifc.getBytes()).length > 0) {
+ parser = new SIFCParser(buffer);
+ contact = parser.parse();
+ }
+ } catch (Exception e) {
+ throw new EntityException("Error converting SIF-C to Contact. ", e);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Conversion done.");
+ }
+ return contact;
+ }
+
+ private Contact vcard2Contact(String vcard) throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(vcard.length() + 60);
+ sb.append("Converting: VCARD => Contact").append("\nINPUT = {")
+ .append(vcard).append('}');
+ log.trace(sb.toString());
+ }
+
+ ByteArrayInputStream buffer = null;
+ VcardParser parser = null;
+ Contact contact = null;
+ try {
+ contact = new Contact();
+
+ buffer = new ByteArrayInputStream(vcard.getBytes());
+ if ((vcard.getBytes()).length > 0) {
+ parser = new VcardParser(buffer,
+ this.deviceTimeZoneDescription, this.deviceCharset);
+ contact = parser.vCard();
+ }
+ } catch (Exception e) {
+ throw new EntityException("Error converting VCARD to Contact. ", e);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Conversion done.");
+ }
+ return contact;
+ }
+
+ private String contact2sif(Contact contact) throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Converting: Contact => SIF-C");
+ }
+
+ String xml = null;
+ try {
+ // this.deviceTimeZone, this.deviceCharset
+ ContactToSIFC c2xml = new ContactToSIFC(null, null);
+ xml = c2xml.convert(contact);
+
+ if (log.isTraceEnabled()) {
+ log.trace("OUTPUT = {" + xml + "}. Conversion done.");
+ }
+ } catch (Exception e) {
+ throw new EntityException("Error converting Contact to SIF-C. ", e);
+ }
+ return xml;
+ }
+
+ private String contact2vcard(Contact contact) throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Converting: Contact => VCARD");
+ }
+
+ String vcard = null;
+ try {
+ ContactToVcard c2vc = new ContactToVcard(this.deviceTimeZone,
+ this.deviceCharset);
+ vcard = c2vc.convert(contact);
+
+ if (log.isTraceEnabled()) {
+ log.trace("OUTPUT = {" + vcard + "}. Conversion done.");
+ }
+ } catch (Exception e) {
+ throw new EntityException("Error converting Contact to VCARD. ", e);
+ }
+ return vcard;
+ }
+
+ /**
+ * Create a new SyncItem from a Contact. The target contentType and status
+ * are passed as arguments.
+ *
+ * @param contact
+ * the Contact object representing the input information
+ * @param contentType
+ * chosen among the TYPE array's elements
+ * @param status
+ * @throws EntityException
+ * if the content type is wrong or any problem occurs while
+ * creating a new SyncItem
+ * @return a newly created SyncItem object
+ */
+ private SyncItem createSyncItem(String id, Contact contact, char status)
+ throws EntityException {
+
+ String contentType = getInfo().getPreferredType().getType();
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("PIMCalendarSyncSource - creating item with:").append(
+ "\n> id: ").append(id).append("\n> status: ")
+ .append(status).append("\n> content-type: ").append(
+ contentType);
+ log.trace(sb.toString());
+ }
+
+ SyncItem syncItem = null;
+ String stream = convert(contact, contentType);
+
+ try {
+ syncItem = new SyncItemImpl(this, id, status);
+ } catch (Exception e) {
+ throw new EntityException(e);
+ }
+
+ syncItem.setType(contentType);
+ syncItem.setContent(stream.getBytes());
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMContactSyncSource created SyncItem");
+ }
+
+ return syncItem;
+ }
+
+ /**
+ * Converts a SyncItem to a Contact object, provided it represents a contact
+ * item in VCard or SIF-C format.
+ *
+ * @param syncItem
+ * @throws EntityException
+ * if the contentType is wrong or the conversion attempt doesn't
+ * succeed.
+ * @return a Contact object
+ */
+ private Contact convert(SyncItem syncItem) throws EntityException {
+ return convert(getContentFromSyncItem(syncItem), syncItem.getType());
+ }
+
+ /**
+ * Converts a contact in vCard or SIF-C format to a Contact object.
+ *
+ * @param content
+ * as a String
+ * @param contentType
+ * @throws EntityException
+ * if the contentType is wrong or the conversion attempt doesn't
+ * succeed.
+ * @return a Contact object
+ */
+ private Contact convert(String content, String contentType)
+ throws EntityException {
+ // Finds out which target type is required
+ for (int i = 0; i < TYPE.length; i++) {
+ if (contentType.equals(TYPE[i])) { // Bingo!
+
+ // Uses the proper converter method
+ switch (i) {
+ case VCARD:
+ return vcard2Contact(content);
+ case SIFC:
+ return sif2Contact(content);
+ default:
+ throw new EntityException("Can't make a Contact "
+ + "out of a " + TYPE[i] + "!");
+ }
+ }
+ }
+ throw new EntityException("Content type unknown: " + contentType);
+ }
+
+ /**
+ * Converts a Contact back to a streamable (vCard, SIF-C) format.
+ *
+ * @param contact
+ * @param contentType
+ * @throws EntityException
+ * if the contentType is wrong or the conversion attempt doesn't
+ * succeed.
+ * @return the result in the required format
+ */
+ private String convert(Contact contact, String contentType)
+ throws EntityException {
+
+ // Finds out which target type is required
+ for (int i = 0; i < TYPE.length; i++) {
+ if (contentType.equals(TYPE[i])) { // Bingo!
+
+ // Uses the proper converter method
+ switch (i) {
+ case VCARD:
+ return contact2vcard(contact);
+ case SIFC:
+ return contact2sif(contact);
+ default:
+ throw new EntityException("Can't make a " + TYPE[i]
+ + "out of a Contact!");
+ }
+ }
+ }
+ throw new EntityException("Content type unknown: " + contentType);
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMSyncSource.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMSyncSource.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMSyncSource.java (revision 1009)
@@ -0,0 +1,258 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.engine.source;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.util.TimeZone;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.items.manager.PIMEntityManager;
+import br.com.prognus.psync.util.Def;
+
+import com.funambol.framework.core.AlertCode;
+import com.funambol.framework.engine.SyncItem;
+import com.funambol.framework.engine.SyncItemKey;
+import com.funambol.framework.engine.SyncItemState;
+import com.funambol.framework.engine.source.AbstractSyncSource;
+import com.funambol.framework.engine.source.MergeableSyncSource;
+import com.funambol.framework.engine.source.SyncContext;
+import com.funambol.framework.engine.source.SyncSourceException;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.security.Sync4jPrincipal;
+import com.funambol.framework.server.Sync4jDevice;
+import com.funambol.framework.tools.beans.LazyInitBean;
+
+public abstract class PIMSyncSource extends AbstractSyncSource implements
+ MergeableSyncSource, Serializable, LazyInitBean {
+
+ // ----------------------------------------------------------------
+ // Constants
+
+ protected static final String JNDI_DATA_SOURCE_NAME = "jdbc/fnblds";
+
+ public static final int SIFC = 0; // To be used as index for SIF-Contact
+
+ public static final int SIFE = 1; // To be used as index for SIF-Event
+
+ public static final int SIFN = 2; // To be used as index for SIF-Note
+
+ public static final int SIFT = 3; // To be used as index for SIF-Task
+
+ public static final int VCARD = 4; // To be used as index for VCard
+
+ public static final int VCAL = 5; // To be used as index for VCal
+
+ public static final int ICAL = 6; // To be used as index for ICal
+
+ public static final int VNOTE = 7; // To be used as index for VNote
+
+ public static final String[] TYPE = { "text/x-s4j-sifc", // SIF-Contact
+ "text/x-s4j-sife", // SIF-Event
+ "text/x-s4j-sifn", // SIF-Note
+ "text/x-s4j-sift", // SIF-Task
+ "text/x-vcard", // VCard
+ "text/x-vcalendar", // VCal
+ "text/calendar", // ICal
+ "text/x-vnote", // VNote
+ };
+
+ // ----------------------------------------------------------- Protected
+ // data
+
+ protected static final FunambolLogger log = FunambolLoggerFactory
+ .getLogger(Def.LOGGER_NAME);
+
+ protected PIMEntityManager manager;
+
+ protected Sync4jPrincipal principal;
+
+ protected String userId;
+
+ protected int syncMode;
+
+ protected Timestamp lastSyncTime; // n-th synchronization
+
+ protected Timestamp previousSyncTime; // (n - 1)-th synchronization
+
+ protected TimeZone deviceTimeZone = null;
+
+ protected String deviceTimeZoneDescription = null;
+
+ protected String deviceCharset = null;
+
+ // ----------------------------------------------------------- Public
+ // methods
+
+ public void beginSync(SyncContext context) {
+
+ log.trace("PIMSyncSource beginSync start");
+
+ principal = context.getPrincipal();
+ userId = principal.getUsername();
+ syncMode = context.getSyncMode();
+
+ Sync4jDevice device = context.getPrincipal().getDevice();
+ String timezone = device.getTimeZone();
+ if (device.getConvertDate()) {
+ if (timezone != null && timezone.length() > 0) {
+ deviceTimeZoneDescription = timezone;
+ deviceTimeZone = TimeZone
+ .getTimeZone(deviceTimeZoneDescription);
+ }
+ }
+ deviceCharset = device.getCharset();
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder("Beginning sync with:");
+ sb.append("\n> syncMode : ").append(syncMode);
+ sb.append("\n> principal : ").append(principal);
+ sb.append("\n> deviceTimeZoneDescr.: ").append(
+ deviceTimeZoneDescription);
+ sb.append("\n> deviceTimeZone : ").append(deviceTimeZone);
+ sb.append("\n> charset : ").append(deviceCharset);
+
+ log.trace(sb.toString());
+ }
+
+ if (syncMode == AlertCode.REFRESH_FROM_CLIENT) {
+ if (log.isTraceEnabled()) {
+ log.trace("Performing REFRESH_FROM_CLIENT (203)");
+ }
+ removeAllSyncItems();
+ }
+
+ log.trace("PIMSyncSource beginSync end");
+ }
+
+ public void endSync() {
+ }
+
+ /**
+ * Gets the status of the SyncItem with the given key.
+ *
+ * @param syncItemKey
+ * as a SyncItemKey object
+ * @throws SyncSourceException
+ * @return the status as a char
+ */
+ public char getSyncItemStateFromId(SyncItemKey syncItemKey)
+ throws SyncSourceException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMSyncSource getSyncItemStateFromId begin");
+ }
+
+ String id = "N/A"; // default value for error tracking
+
+ try {
+
+ // Slow sync
+ // @todo Implement, depending on a syncMode check
+
+ // Fast sync
+ id = syncItemKey.getKeyAsString();
+ char itemRawState = manager.getItemState(id, previousSyncTime);
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMSyncSource getSyncItemStateFromId end");
+ }
+
+ if (itemRawState == Def.PIM_STATE_UNCHANGED) {
+ return SyncItemState.SYNCHRONIZED;
+ } else {
+ return itemRawState; // Def uses SyncItemState.* as constant
+ // values for N, D and U states
+ }
+ } catch (EntityException ee) {
+ throw new SyncSourceException(
+ "Error getting the state of SyncItem " + "with ID " + id,
+ ee);
+ }
+
+ }
+
+ /**
+ * Not yet used. It just produces a log message.
+ */
+ public void setOperationStatus(String operation, int statusCode,
+ SyncItemKey[] keys) {
+
+ if (log.isTraceEnabled()) {
+ StringBuilder message = new StringBuilder("Received status code '");
+ message.append(statusCode).append("' for a '").append(operation)
+ .append("' command for the following items: ");
+
+ for (int i = 0; i < keys.length; i++) {
+ message.append("\n> ").append(keys[i].getKeyAsString());
+ }
+
+ log.trace(message.toString());
+ }
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ /**
+ * Extracts the content from a syncItem.
+ *
+ * @param syncItem
+ * @return as a String object
+ */
+ protected String getContentFromSyncItem(SyncItem syncItem) {
+
+ byte[] itemContent = syncItem.getContent();
+
+ // Add content processing here, if needed
+
+ return new String(itemContent == null ? new byte[0] : itemContent);
+ }
+
+ protected void saveSyncTiming(Timestamp since, Timestamp to) {
+
+ if (log.isTraceEnabled()) {
+ if (!since.equals(previousSyncTime)) {
+ log.trace("PIMSyncSource sync timing updated to " + since
+ + " / " + to);
+ }
+ }
+ this.previousSyncTime = since;
+ this.lastSyncTime = to;
+ }
+
+ protected void removeAllSyncItems() {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Perform REFRESH_FROM_CLIENT (203) for user " + userId);
+ }
+
+ try {
+ manager.removeAllItems();
+ } catch (EntityException ee) {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Error while performing REFRESH_FROM_CLIENT (203).",
+ ee);
+ }
+ }
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMCalendarSyncSource.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMCalendarSyncSource.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/engine/source/PIMCalendarSyncSource.java (revision 1009)
@@ -0,0 +1,842 @@
+/**
+ * This class is a PIMSyncSource for ICal and VCal data, which could include
+ * either an Event or a Todo. NB: The Todo part is not currenlty implemented.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.engine.source;
+
+import java.io.ByteArrayInputStream;
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import br.com.prognus.psync.exception.EntityException;
+import br.com.prognus.psync.items.manager.PIMCalendarManager;
+import br.com.prognus.psync.items.model.CalendarWrapper;
+import br.com.prognus.psync.util.Def;
+
+import com.funambol.common.pim.calendar.Calendar;
+import com.funambol.common.pim.converter.BaseConverter;
+import com.funambol.common.pim.converter.CalendarToSIFE;
+import com.funambol.common.pim.converter.TaskToSIFT;
+import com.funambol.common.pim.converter.VCalendarConverter;
+import com.funambol.common.pim.converter.VComponentWriter;
+import com.funambol.common.pim.icalendar.ICalendarParser;
+import com.funambol.common.pim.model.VCalendar;
+import com.funambol.common.pim.sif.SIFCalendarParser;
+import com.funambol.common.pim.xvcalendar.XVCalendarParser;
+import com.funambol.framework.engine.SyncItem;
+import com.funambol.framework.engine.SyncItemImpl;
+import com.funambol.framework.engine.SyncItemKey;
+import com.funambol.framework.engine.SyncItemState;
+import com.funambol.framework.engine.source.SyncContext;
+import com.funambol.framework.engine.source.SyncSourceException;
+import com.funambol.framework.tools.beans.BeanInitializationException;
+import com.sun.org.apache.xerces.internal.impl.xs.SubstitutionGroupHandler;
+
+public class PIMCalendarSyncSource extends PIMSyncSource {
+
+ // ------------------------------------------------------------- Private
+ // data
+
+ private PIMCalendarManager manager;
+
+ // ---------------------------------------------------------------
+ // Properties
+
+ private Class entityType; // CalendarContent or one of its subclasses
+
+ public Class getEntityType() {
+ return entityType;
+ }
+
+ public void setEntityType(Class entityType) {
+ this.entityType = entityType;
+ }
+
+ // ----------------------------------------------------------- Public
+ // methods
+
+ public void beginSync(SyncContext context) {
+
+ this.manager = new PIMCalendarManager(JNDI_DATA_SOURCE_NAME, context
+ .getPrincipal());
+ super.manager = this.manager;
+ super.beginSync(context);
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing the ID(s) of the
+ * twin(s) of a given calendar.
+ *
+ * @param syncItem
+ * the SyncItem representing the calendar whose twin(s) are
+ * looked for
+ * @throws SyncSourceException
+ * @return possibly, just one or no key should be in the array, but it can't
+ * be ruled out a priori that several keys get returned by this
+ * method
+ */
+ public SyncItemKey[] getSyncItemKeysFromTwin(SyncItem syncItem)
+ throws SyncSourceException {
+
+ try {
+
+ Calendar calendar = convert(getContentFromSyncItem(syncItem),
+ syncItem.getType());
+
+ List idList = null;
+ idList = manager.getTwins(calendar);
+
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey((String) idList.get(i));
+ }
+ return keyList;
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving twin item keys.", e);
+ }
+ }
+
+ /**
+ * Adds a SyncItem object (representing a calendar).
+ *
+ * @param syncItem
+ * the SyncItem representing the calendar
+ *
+ * @return a newly created syncItem based on the input object but with its
+ * status set at SyncItemState.NEW and the GUID retrieved by the
+ * back-end
+ */
+ public SyncItem addSyncItem(SyncItem syncItem) throws SyncSourceException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("PIMCalendarSyncSource addSyncItem begin");
+ }
+
+ Calendar c = null;
+ String content = null;
+
+ try {
+
+ content = getContentFromSyncItem(syncItem);
+ String contentType = syncItem.getType();
+
+ c = convert(content, contentType);
+ Timestamp ts = syncItem.getTimestamp();
+
+ // Adds the calendar, wraps it in sync information and uses it to
+ // create a new SyncItem which is the return value of this method
+ SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
+ manager.addItem(c, ts), // key
+ null, // mappedKey
+ SyncItemState.NEW, // state
+ content.getBytes(), // content
+ null, // format
+ contentType, // type
+ ts // timestamp
+ );
+
+ return newSyncItem;
+
+ } catch (Exception e) {
+ log.error("SyncSource error adding a new synchronization item.");
+ throw new SyncSourceException("Error adding the item " + syncItem,
+ e);
+ }
+ }
+
+ /**
+ * Updates a SyncItem object (representing a calendar).
+ *
+ * @param syncItem
+ * the SyncItem representing the calendar
+ *
+ * @return a newly created syncItem based on the input object but with its
+ * status set at SyncItemState.UPDATED and the GUID retrieved by the
+ * back-end
+ */
+ public SyncItem updateSyncItem(SyncItem syncItem)
+ throws SyncSourceException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Updates a SyncItem from " + sourceURI);
+ }
+
+ Calendar c = null;
+ String content = null;
+
+ try {
+
+ String id = syncItem.getKey().getKeyAsString();
+ content = getContentFromSyncItem(syncItem);
+ String contentType = syncItem.getType();
+
+ c = convert(content, contentType);
+
+ // Modifies the calendar, wraps it in sync information and uses it
+ // to
+ // create a new SyncItem which is the return value of this method
+ SyncItemImpl newSyncItem = new SyncItemImpl(this, // syncSource
+ manager.updateItem(id, c, syncItem.getTimestamp()), // key
+ null, // mappedKey
+ SyncItemState.UPDATED, // state
+ content.getBytes(), // content
+ null, // format
+ contentType, // type
+ null // timestamp
+ );
+
+ return newSyncItem;
+
+ } catch (Exception e) {
+ log.error("SyncSource error updating a synchronization item.", e);
+ throw new SyncSourceException(
+ "Error updating the item " + syncItem, e);
+ }
+ }
+
+ /**
+ * Deletes the item with a given syncItemKey.
+ *
+ * @param syncItemKey
+ * @param timestamp
+ * in case of a soft deletion, this will be the registered moment
+ * of deletion; if a hard deletion is used, this field is
+ * irrelevant and may also be null
+ * @param softDelete
+ * it is true if the client requires a soft deletion
+ * @throws SyncSourceException
+ */
+ public void removeSyncItem(SyncItemKey syncItemKey, Timestamp timestamp,
+ boolean softDelete) throws SyncSourceException {
+
+ try {
+
+ if (!softDelete) {
+ if (log.isTraceEnabled()) {
+ log.trace("PIMCalendarSyncSource remove the SyncItem "
+ + syncItemKey.getKeyAsString());
+ }
+
+ manager.removeItem(syncItemKey.getKeyAsString());
+ }
+
+ } catch (EntityException e) {
+ log.error("Sync source error: could not delete item with key"
+ + syncItemKey, e);
+ throw new SyncSourceException("Error deleting item. ", e);
+ }
+ }
+
+ public SyncItem getSyncItemFromId(SyncItemKey syncItemKey)
+ throws SyncSourceException {
+
+ String id = null;
+ SyncItem syncItem = null;
+
+ id = syncItemKey.getKeyAsString();
+ if (log.isTraceEnabled()) {
+ log.trace("PIMCalendarSyncSource get SyncItem from " + id);
+ }
+
+ try {
+ CalendarWrapper cw;
+ try {
+ cw = manager.getItem(id);
+ } catch (Exception e) {
+ return null;
+ }
+ // Retrieves the calendar, wraps it in sync information and uses it
+ // to create a new SyncItem which is the return value of this method
+ syncItem = createSyncItem(id, cw.getCalendar(), SyncItemState.NEW);
+
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error seeking SyncItem with ID: "
+ + id, e);
+ }
+
+ return syncItem;
+ }
+
+ public boolean mergeSyncItems(SyncItemKey syncItemKey, SyncItem syncItem)
+ throws SyncSourceException {
+
+ try {
+ Calendar calendar = convert(getContentFromSyncItem(syncItem),
+ syncItem.getType());
+
+ boolean clientUpdateRequired = manager.mergeItems(syncItemKey
+ .getKeyAsString(), calendar, syncItem.getTimestamp());
+
+ if (clientUpdateRequired) {
+ syncItem = getSyncItemFromId(syncItemKey);
+ }
+
+ return true; //clientUpdateRequired 418
+
+ } catch (EntityException e) {
+
+ log.error("SyncSource error: a merge did not succeed.", e);
+ throw new SyncSourceException("Error merging SyncItem with ID "
+ + syncItemKey.getKeyAsString() + "with SyncItem "
+ + syncItem, e);
+ }
+ }
+
+ public void init() throws BeanInitializationException {
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all new calendar IDs,
+ * filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ public SyncItemKey[] getNewSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = manager.getNewItems(since, to);
+ return extractKeyArrayFromIdList(idList);
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving new item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all deleted calendar
+ * IDs, filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ public SyncItemKey[] getUpdatedSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = manager.getUpdatedItems(since, to);
+ return extractKeyArrayFromIdList(idList);
+ } catch (EntityException e) {
+ throw new SyncSourceException(
+ "Error retrieving updated item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all deleted calendar
+ * IDs, filtered according to a given time interval.
+ *
+ * @param since
+ * the earlier limit of the time interval
+ * @param to
+ * the later limit of the time interval
+ * @return a SyncItemKey array
+ */
+ public SyncItemKey[] getDeletedSyncItemKeys(Timestamp since, Timestamp to)
+ throws SyncSourceException {
+
+ saveSyncTiming(since, to);
+
+ try {
+ List idList = manager.getDeletedItems(since, to);
+ return extractKeyArrayFromIdList(idList);
+ } catch (EntityException e) {
+ throw new SyncSourceException(
+ "Error retrieving deleted item keys.", e);
+ }
+ }
+
+ /**
+ * Makes an array of SyncItemKey objects representing all calendar IDs.
+ *
+ * @return a SyncItemKey array
+ */
+ public SyncItemKey[] getAllSyncItemKeys() throws SyncSourceException {
+
+ try {
+ List idList = manager.getAllItems();
+ return extractKeyArrayFromIdList(idList);
+
+ } catch (EntityException e) {
+ throw new SyncSourceException("Error retrieving all item keys. ", e);
+ }
+ }
+
+ /**
+ * Gets the status of the SyncItem with the given key.
+ *
+ * @param syncItemKey
+ * as a SyncItemKey object
+ * @throws SyncSourceException
+ * @return the status as a char
+ */
+ public char getSyncItemStateFromId(SyncItemKey syncItemKey)
+ throws SyncSourceException {
+
+ String id = "N/A"; // default value for error tracking
+
+ try {
+
+ // Slow sync
+ // @todo Implement, depending on a syncMode check
+
+ // Fast sync
+ id = syncItemKey.getKeyAsString();
+ if (log.isTraceEnabled()) {
+ log
+ .trace("PIMCalendarSyncSource get SyncItem state from "
+ + id);
+ }
+ char itemRawState = manager.getItemState(id, previousSyncTime);
+
+ if (itemRawState == Def.PIM_STATE_UNCHANGED) {
+ return SyncItemState.SYNCHRONIZED;
+ } else {
+ return itemRawState; // Def uses SyncItemState.* as constant
+ // values for N, D and U states
+ }
+ } catch (EntityException e) {
+ throw new SyncSourceException(
+ "Error getting the state of SyncItem " + "with ID " + id, e);
+ }
+ }
+
+ // ---------------------------------------------------------- Private
+ // methods
+
+ /**
+ * Extracts the content from a syncItem.
+ *
+ * @param syncItem
+ * @return as a String object (same as
+ * PIMSyncSource#getContentFromSyncItem(String), but trimmed)
+ */
+ protected String getContentFromSyncItem(SyncItem syncItem) {
+
+ String raw = super.getContentFromSyncItem(syncItem);
+
+ return raw.trim();
+ }
+
+ private String hackFix(String text) {
+
+ String result, head, title, clas, description, tail, new_title;
+ int p_categories, p_classes, p_location, p_dstart, values1, values2, op1, op2, op3, op4, op5, op6, op7, op8, qt1, qt2;
+
+ qt1 = 0;
+ qt2 = 0;
+
+ // Pega as posicoes das tags
+ p_categories = text.indexOf("CATEGORIES");
+ p_classes = text.indexOf("CLASS");
+
+ p_location = text.indexOf("LOCATION");
+ p_dstart = text.indexOf("DTSTART");
+
+ // Pega as posicoes das varias formas da tag SUMMARY
+ op1 = text.indexOf("SUMMARY:");
+ op2 = text.indexOf("SUMMARY;CHARSET=UTF-8:");
+ op3 = text.indexOf("SUMMARY;ENCODING=QUOTED-PRINTABLE:");
+ op4 = text.indexOf("SUMMARY;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:");
+
+ // Pega as posicoes das varias formas da tag DESCRIPTION
+ op5 = text.indexOf("DESCRIPTION:");
+ op6 = text.indexOf("DESCRIPTION;CHARSET=UTF-8:");
+ op7 = text.indexOf("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:");
+ op8 = text.indexOf("DESCRIPTION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:");
+
+ qt1 += (op1 == -1) ? 0 : op1 + 8;
+ qt1 += (op2 == -1) ? 0 : op2 + 22;
+ qt1 += (op3 == -1) ? 0 : op3 + 34;
+ qt1 += (op4 == -1) ? 0 : op4 + 48;
+
+ qt2 += (op5 == -1) ? 0 : op5 + 12;
+ qt2 += (op6 == -1) ? 0 : op6 + 26;
+ qt2 += (op7 == -1) ? 0 : op7 + 38;
+ qt2 += (op8 == -1) ? 0 : op8 + 52;
+
+ values1 = (p_categories == -1) ? p_classes : p_categories;
+ values2 = (p_location == -1) ? p_dstart : p_location;
+
+ head = text.substring(0, qt1);
+ title = text.substring(qt1, values1);
+ clas = text.substring(values1, qt2);
+ description = text.substring(qt2, values2);
+ tail = text.substring(values2);
+
+ new_title = title.replaceAll("\r\n ", " ");
+
+ // Retira os enters no final do titulo
+ int title_t, title_bl;
+
+ while(true){
+ title_t = new_title.length();
+ title_bl = new_title.lastIndexOf("=0D=0A=\r\n");
+
+ if(title_bl != -1 && title_t == title_bl + 11){
+ new_title = new_title.substring(0, title_bl) + "\r\n";
+ } else {
+ break;
+ }
+ }
+
+ // Retira os enters no final da descricao
+ int description_t, description_bl;
+
+ while(true){
+ description_t = description.length();
+ description_bl = description.lastIndexOf("=0D=0A=\r\n");
+
+ if(description_bl != -1 && description_t == description_bl + 11){
+ description = description.substring(0, description_bl) + "\r\n";
+ } else {
+ break;
+ }
+ }
+
+ result = head + new_title + clas + description + tail;
+
+ return result;
+ }
+
+ private Calendar webCalendar2Calendar(String text, String vCalType)
+ throws EntityException {
+
+ try {
+ // BugFix XVCalendarParser
+ if (vCalType.equals(PIMSyncSource.TYPE[PIMSyncSource.VCAL])) {
+ text = hackFix(text);
+ }
+
+ ByteArrayInputStream buffer = new ByteArrayInputStream(text.getBytes());
+
+ VCalendar vcalendar;
+ String version;
+ String charset;
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(text.length() + 60);
+ sb.append("Converting: ").append(vCalType).append(
+ " => Calendar ").append("\nINPUT = {").append(text)
+ .append('}');
+ log.trace(sb.toString());
+ }
+
+ if (vCalType.equals(PIMSyncSource.TYPE[PIMSyncSource.VCAL])) {
+ // vCalendar (1.0):
+ XVCalendarParser parser = new XVCalendarParser(buffer);
+ vcalendar = (VCalendar) parser.XVCalendar();
+ version = "1.0";
+ charset = BaseConverter.CHARSET_UTF7; // (versit spec)
+ } else {
+ // iCalendar (2.0):
+ ICalendarParser parser = new ICalendarParser(buffer);
+ vcalendar = (VCalendar) parser.ICalendar();
+ version = "2.0";
+ charset = BaseConverter.CHARSET_UTF8; // (RFC 2445)
+ }
+ if (deviceCharset != null) {
+ charset = deviceCharset; // overrides the default character
+ // set
+ }
+
+ String retrievedVersion = null;
+ if (vcalendar.getProperty("VERSION") != null) {
+ retrievedVersion = vcalendar.getProperty("VERSION").getValue();
+ }
+ vcalendar.addProperty("VERSION", version);
+ if (retrievedVersion == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("No version property was found in the vCal/iCal "
+ + "data: version set to " + version);
+ }
+ } else if (!retrievedVersion.equals(version)) {
+ if (log.isTraceEnabled()) {
+ log.trace("The version in the data was " + retrievedVersion
+ + " but it's been changed to " + version);
+ }
+ }
+
+ VCalendarConverter vcf = new VCalendarConverter(deviceTimeZone,
+ charset);
+
+ Calendar c = null;
+ c = vcf.vcalendar2calendar(vcalendar);
+
+ if (log.isTraceEnabled()) {
+ log.trace("Conversion done.");
+ }
+
+ return c;
+
+ } catch (Exception e) {
+ throw new EntityException("Error converting " + vCalType
+ + " to Calendar. ", e);
+ }
+ }
+
+ private String calendar2webCalendar(Calendar calendar, String vCalType)
+ throws EntityException {
+
+ try {
+
+ String charset;
+ if (vCalType.equals(PIMSyncSource.TYPE[PIMSyncSource.VCAL])) {
+ // vCalendar (1.0):
+ charset = BaseConverter.CHARSET_UTF7; // (versit spec)
+ } else {
+ // iCalendar (2.0):
+ charset = BaseConverter.CHARSET_UTF8; // (RFC 2445)
+ }
+ if (deviceCharset != null) {
+ charset = deviceCharset; // overrides the default character
+ // set
+ }
+
+ VCalendarConverter vcf = new VCalendarConverter(deviceTimeZone,
+ charset);
+
+ VCalendar vcalendar;
+ String vcal;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Converting: Calendar => " + vCalType);
+ }
+
+ if (vCalType.equals(PIMSyncSource.TYPE[VCAL])) { // VCAL
+
+ vcalendar = vcf.calendar2vcalendar(calendar, true); // text/x-vcalendar
+
+ } else { // ICAL
+
+ vcalendar = vcf.calendar2vcalendar(calendar, false); // text/calendar
+ }
+
+ VComponentWriter writer = new VComponentWriter(
+ VComponentWriter.NO_FOLDING);
+ vcal = writer.toString(vcalendar);
+
+ if (log.isTraceEnabled()) {
+ log.trace("OUTPUT = {" + vcal + "}. Conversion done.");
+ }
+
+ return vcal;
+
+ } catch (Exception e) {
+ throw new EntityException("Error converting Calendar to "
+ + vCalType, e);
+ }
+ }
+
+ /**
+ * Create a new SyncItem from a Calendar. The status is passed as an
+ * argument.
+ *
+ * @param calendar
+ * the Calendar object representing the input information
+ * @param status
+ * @throws EntityException
+ * if the content type is wrong or any problem occurs while
+ * creating a new SyncItem
+ * @return a newly created SyncItem object
+ */
+ private SyncItem createSyncItem(String id, Calendar calendar, char status)
+ throws EntityException {
+
+ String contentType = getInfo().getPreferredType().getType();
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("PIMCalendarSyncSource - creating item with:").append(
+ "\n> id: ").append(id).append("\n> status: ")
+ .append(status).append("\n> content-type: ").append(
+ contentType);
+ log.trace(sb.toString());
+ }
+
+ SyncItem syncItem = null;
+ String stream = convert(calendar, contentType);
+
+ try {
+ syncItem = new SyncItemImpl(this, id, status);
+ } catch (Exception e) {
+ throw new EntityException(e);
+ }
+
+ syncItem.setType(contentType);
+ syncItem.setContent(stream.getBytes());
+ if (log.isTraceEnabled()) {
+ log.trace("PIMCalendarSyncSource created SyncItem");
+ }
+ return syncItem;
+ }
+
+ private SyncItemKey[] extractKeyArrayFromIdList(List idList) {
+
+ SyncItemKey[] keyList = new SyncItemKey[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ keyList[i] = new SyncItemKey((String) idList.get(i));
+ }
+ return keyList;
+ }
+
+ /**
+ * Converts a calendar in vCalendar/iCalendar, SIF-E or SIF-T format to a
+ * Calendar object.
+ *
+ * @param content
+ * as a String
+ * @param contentType
+ * @throws EntityException
+ * if the contentType is wrong or the conversion attempt doesn't
+ * succeed.
+ * @return a Calendar object
+ */
+ private Calendar convert(String content, String contentType)
+ throws EntityException {
+ // Finds out which target type is required
+ for (int i = 0; i < TYPE.length; i++) {
+ if (contentType.equals(TYPE[i])) {
+
+ // Uses the proper converter method
+ switch (i) {
+ case VCAL:
+ case ICAL:
+ return webCalendar2Calendar(content, contentType);
+ case SIFE:
+ case SIFT:
+ return sif2Calendar(content, contentType);
+ default:
+ throw new EntityException("Can't make a Contact "
+ + "out of a " + TYPE[i] + "!");
+ }
+ }
+ }
+ throw new EntityException("Content type unknown: " + contentType);
+ }
+
+ /**
+ * Converts a Calendar back to a streamable (vCalendar/iCalendar, SIF-E or
+ * SIF-T) format.
+ *
+ * @param calendar
+ * @param contentType
+ * @throws EntityException
+ * if the contentType is wrong or the conversion attempt doesn't
+ * succeed.
+ * @return the result in the required format
+ */
+ private String convert(Calendar calendar, String contentType)
+ throws EntityException {
+
+ // Finds out which target type is required
+ for (int i = 0; i < TYPE.length; i++) {
+ if (contentType.equals(TYPE[i])) {
+
+ // Uses the proper converter method
+ switch (i) {
+ case VCAL:
+ case ICAL:
+ return calendar2webCalendar(calendar, contentType);
+ case SIFE:
+ case SIFT:
+ return calendar2sif(calendar, contentType);
+ default:
+ throw new EntityException("Can't make a " + TYPE[i]
+ + "out of a Contact!");
+ }
+ }
+ }
+ throw new EntityException("Content type unknown: " + contentType);
+ }
+
+ private Calendar sif2Calendar(String xml, String sifType)
+ throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder(xml.length() + 60);
+ sb.append("Converting: ").append(sifType).append(" => Calendar ")
+ .append("\nINPUT = {").append(xml).append('}');
+ log.trace(sb.toString());
+ }
+
+ ByteArrayInputStream buffer = null;
+ Calendar calendar = null;
+ try {
+ calendar = new Calendar();
+ buffer = new ByteArrayInputStream(xml.getBytes());
+ if ((xml.getBytes()).length > 0) {
+ SIFCalendarParser parser = new SIFCalendarParser(buffer);
+ calendar = parser.parse();
+ }
+ } catch (Exception e) {
+ throw new EntityException("Error converting " + sifType
+ + " to Calendar. ", e);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Conversion done.");
+ }
+ return calendar;
+ }
+
+ private String calendar2sif(Calendar calendar, String sifType)
+ throws EntityException {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Converting: Calendar => " + sifType);
+ }
+
+ String xml = null;
+ BaseConverter c2xml;
+ Object thing;
+
+ try {
+ if (sifType.equals(PIMSyncSource.TYPE[SIFE])) { // SIF-E
+ c2xml = new CalendarToSIFE(deviceTimeZone, deviceCharset);
+ thing = calendar;
+ // NB: A CalendarToSIFE converts a Calendar into a SIF-E
+ } else { // SIF-T
+ c2xml = new TaskToSIFT(deviceTimeZone, deviceCharset);
+ thing = calendar.getTask();
+ // NB: A TaskToSIFT converts just a Task into a SIF-T
+ }
+
+ xml = c2xml.convert(thing);
+
+ if (log.isTraceEnabled()) {
+ log.trace("OUTPUT = {" + xml + "}. Conversion done.");
+ }
+ } catch (Exception e) {
+ throw new EntityException(
+ "Error converting Calendar to " + sifType, e);
+ }
+ return xml;
+ }
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMCalendarSyncSourceConfigPanel.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMCalendarSyncSourceConfigPanel.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMCalendarSyncSourceConfigPanel.java (revision 1009)
@@ -0,0 +1,333 @@
+/**
+ * This class does something.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.admin;
+
+import java.awt.Dimension;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JCheckBox;
+
+import br.com.prognus.psync.engine.source.PIMCalendarSyncSource;
+import br.com.prognus.psync.engine.source.PIMSyncSource;
+
+import com.funambol.admin.AdminException;
+import com.funambol.common.pim.calendar.CalendarContent;
+import com.funambol.common.pim.calendar.Event;
+import com.funambol.common.pim.calendar.Task;
+import com.funambol.framework.engine.source.ContentType;
+import com.funambol.framework.engine.source.SyncSource;
+import com.funambol.framework.engine.source.SyncSourceInfo;
+
+public class PIMCalendarSyncSourceConfigPanel extends PIMSyncSourceConfigPanel
+ implements Serializable {
+
+ // --------------------------------------------------------------- Constants
+ protected static final String PANEL_NAME = "Edit PIM Calendar SyncSource";
+
+ protected static final String TYPE_LABEL_SIFE = "SIF-E";
+
+ protected static final String TYPE_LABEL_SIFT = "SIF-T";
+
+ protected static final String TYPE_LABEL_VCAL = "VCal";
+
+ protected static final String TYPE_LABEL_ICAL = "ICal";
+
+ protected static final String TYPE_SIFE = "text/x-s4j-sife";
+
+ protected static final String TYPE_SIFT = "text/x-s4j-sift";
+
+ protected static final String TYPE_VCAL = "text/x-vcalendar";
+
+ protected static final String TYPE_ICAL = "text/calendar";
+
+ protected static final String VERSION_SIFE = "1.0";
+
+ protected static final String VERSION_SIFT = "1.0";
+
+ protected static final String VERSION_VCAL = "1.0";
+
+ protected static final String VERSION_ICAL = "2.0";
+
+ // ---------------------------------------------------------- Protected data
+ protected JCheckBox eventValue;
+
+ protected JCheckBox taskValue;
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of PIMCalendarSyncSourceConfigPanel. */
+ public PIMCalendarSyncSourceConfigPanel() {
+ super();
+
+ // this.add(eventLabel, null);
+ this.add(eventValue, null);
+ // this.add(taskLabel, null);
+ this.add(taskValue, null);
+
+ typeCombo.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ updateEntityTypeCheckBoxes();
+ }
+ });
+
+ }
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Updates the form
+ */
+ public void updateForm() {
+
+ if (!(getSyncSource() instanceof PIMCalendarSyncSource)) {
+ notifyError(new AdminException(
+ "This is not a PIMCalendarSyncSource! "
+ + "Unable to process SyncSource values."));
+ return;
+ }
+
+ super.updateForm();
+
+ updateEntityTypeCheckBoxes();
+ }
+
+ /**
+ * Set preferredSize of the panel
+ *
+ * @return preferredSize of the panel
+ */
+ public Dimension getPreferredSize() {
+ return new Dimension(525, 310);
+ }
+
+ // ------------------------------------------------------- Protected methods
+
+ /**
+ * Adds extra components just above the confirm button.
+ *
+ * @param x
+ * horizontal position
+ * @param y
+ * vertical position
+ * @param xGap
+ * standard horizontal gap
+ * @param yGap
+ * standard vertical gap
+ * @return the new vertical position
+ */
+ protected int addExtraComponents(int x, int y, int xGap, int yGap) {
+
+ eventValue = new JCheckBox("Events");
+ taskValue = new JCheckBox("Tasks");
+
+ eventValue.setFont(defaultFont);
+ eventValue.setSelected(true);
+ eventValue.setBounds(170, y, 100, 25);
+ eventValue.setEnabled(true);
+
+ x += xGap; // Shift a bit to the right
+
+ taskValue.setFont(defaultFont);
+ taskValue.setSelected(true);
+ taskValue.setBounds(170 + xGap, y, 100, 25);
+ taskValue.setEnabled(true);
+
+ return y + yGap;
+ }
+
+ /**
+ * Updates entities checkboxes according to the selected type
+ */
+ protected void updateEntityTypeCheckBoxes() {
+ if (isSIFSelected()) {
+ eventValue.setSelected(areEventsAllowed());
+ taskValue.setSelected(areTasksAllowed());
+
+ eventValue.setEnabled(false);
+ taskValue.setEnabled(false);
+ } else {
+ PIMCalendarSyncSource syncSource = (PIMCalendarSyncSource) getSyncSource();
+ boolean events = false;
+ boolean tasks = false;
+ if (syncSource.getEntityType() == null
+ || syncSource.getEntityType().isAssignableFrom(Event.class)) {
+ events = true;
+ }
+ if (syncSource.getEntityType() == null
+ || syncSource.getEntityType().isAssignableFrom(Task.class)) {
+ tasks = true;
+ }
+ eventValue.setSelected(events);
+ taskValue.setSelected(tasks);
+
+ eventValue.setEnabled(true);
+ taskValue.setEnabled(true);
+ }
+ }
+
+ /**
+ * Validates the values selected.
+ *
+ * @throws java.lang.IllegalArgumntException
+ * if there is an invalid value
+ */
+ protected void validateValues() throws IllegalArgumentException {
+ super.validateValues();
+
+ if (!eventValue.isSelected() && !taskValue.isSelected()) {
+ throw new IllegalArgumentException(
+ "Please check at least one between 'Events' and 'Tasks'.");
+ }
+ }
+
+ /**
+ * Sets the syncsource with the inserted values
+ */
+ protected void getValues() {
+ super.getValues();
+
+ if ((eventValue == null) || (taskValue == null)) {
+ return;
+ }
+
+ PIMCalendarSyncSource syncSource = (PIMCalendarSyncSource) getSyncSource();
+
+ Class entityType;
+ if (eventValue.isSelected()) {
+ if (taskValue.isSelected()) {
+ entityType = CalendarContent.class;
+ } else {
+ entityType = Event.class;
+ }
+ } else {
+ entityType = Task.class;
+ }
+ syncSource.setEntityType(entityType);
+ }
+
+ /**
+ * Returns the panel name
+ *
+ * @return the panel name
+ */
+ protected String getPanelName() {
+ return PANEL_NAME;
+ }
+
+ /**
+ * Returns the available types
+ *
+ * @return the available types;
+ */
+ protected List getTypes() {
+ List supportedTypes = new ArrayList();
+ supportedTypes.add(TYPE_LABEL_SIFE);
+ supportedTypes.add(TYPE_LABEL_SIFT);
+ supportedTypes.add(TYPE_LABEL_VCAL);
+ supportedTypes.add(TYPE_LABEL_ICAL);
+ return supportedTypes;
+ }
+
+ /**
+ * Returns the type to select based on the given syncsource
+ *
+ * @return the type to select based on the given syncsource
+ */
+ protected String getTypeToSelect(SyncSource syncSource) {
+ String preferredType = null;
+ if (syncSource.getInfo() != null
+ && syncSource.getInfo().getPreferredType() != null) {
+
+ preferredType = syncSource.getInfo().getPreferredType().getType();
+ if (TYPE_ICAL.equals(preferredType)) {
+ return TYPE_LABEL_ICAL;
+ }
+ if (TYPE_VCAL.equals(preferredType)) {
+ return TYPE_LABEL_VCAL;
+ }
+ if (TYPE_SIFE.equals(preferredType)) {
+ return TYPE_LABEL_SIFE;
+ }
+ if (TYPE_SIFT.equals(preferredType)) {
+ return TYPE_LABEL_SIFT;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the source info of the given syncsource based on the given
+ * selectedType
+ *
+ * @param syncSource
+ * the source
+ * @param selectedType
+ * the selected type
+ */
+ public void setSyncSourceInfo(SyncSource syncSource, String selectedType) {
+ PIMSyncSource pimSource = (PIMSyncSource) syncSource;
+ ContentType[] contentTypes = null;
+ if (TYPE_LABEL_ICAL.equals(selectedType)) {
+ contentTypes = new ContentType[2];
+ contentTypes[0] = new ContentType(TYPE_ICAL, VERSION_ICAL);
+ contentTypes[1] = new ContentType(TYPE_VCAL, VERSION_VCAL);
+ } else if (TYPE_LABEL_VCAL.equals(selectedType)) {
+ contentTypes = new ContentType[2];
+ contentTypes[0] = new ContentType(TYPE_VCAL, VERSION_VCAL);
+ contentTypes[1] = new ContentType(TYPE_ICAL, VERSION_ICAL);
+ } else if (TYPE_LABEL_SIFE.equals(selectedType)) {
+ contentTypes = new ContentType[1];
+ contentTypes[0] = new ContentType(TYPE_SIFE, VERSION_SIFE);
+ } else if (TYPE_LABEL_SIFT.equals(selectedType)) {
+ contentTypes = new ContentType[1];
+ contentTypes[0] = new ContentType(TYPE_SIFT, VERSION_SIFT);
+ }
+
+ pimSource.setInfo(new SyncSourceInfo(contentTypes, 0));
+ }
+
+ // --------------------------------------------------------- Private methods
+
+ private boolean areEventsAllowed() {
+ if (typeCombo.getSelectedItem() == null) {
+ return false;
+ }
+
+ if (TYPE_LABEL_SIFT.equals((String) typeCombo.getSelectedItem())) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean areTasksAllowed() {
+ if (typeCombo.getSelectedItem() == null) {
+ return false;
+ }
+
+ if (TYPE_LABEL_SIFE.equals((String) typeCombo.getSelectedItem())) {
+ return false;
+ }
+ return true;
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMContactSyncSourceConfigPanel.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMContactSyncSourceConfigPanel.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMContactSyncSourceConfigPanel.java (revision 1009)
@@ -0,0 +1,143 @@
+/**
+ * This class does something.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.admin;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import br.com.prognus.psync.engine.source.PIMContactSyncSource;
+import br.com.prognus.psync.engine.source.PIMSyncSource;
+
+import com.funambol.admin.AdminException;
+import com.funambol.framework.engine.source.ContentType;
+import com.funambol.framework.engine.source.SyncSource;
+import com.funambol.framework.engine.source.SyncSourceInfo;
+
+public class PIMContactSyncSourceConfigPanel extends PIMSyncSourceConfigPanel
+ implements Serializable {
+
+ // --------------------------------------------------------------- Constants
+ protected static final String PANEL_NAME = "Edit PIM Contact SyncSource";
+
+ protected static final String TYPE_LABEL_SIFC = "SIF-C";
+
+ protected static final String TYPE_LABEL_VCARD = "VCard";
+
+ protected static final String TYPE_SIFC = "text/x-s4j-sifc";
+
+ protected static final String TYPE_VCARD = "text/x-vcard";
+
+ protected static final String VERSION_SIFC = "1.0";
+
+ protected static final String VERSION_VCARD = "2.1";
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of PIMContactSyncSourceConfigPanel. */
+ public PIMContactSyncSourceConfigPanel() {
+ super();
+ }
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Updates the form
+ */
+ @Override
+ public void updateForm() {
+
+ if (!(getSyncSource() instanceof PIMContactSyncSource)) {
+ notifyError(new AdminException(
+ "This is not a PIMContactSyncSources! "
+ + "Unable to process SyncSource values."));
+ return;
+ }
+
+ super.updateForm();
+ }
+
+ // ------------------------------------------------------- Protected methods
+
+ /**
+ * Returns the panel name
+ *
+ * @return the panel name
+ */
+ @Override
+ protected String getPanelName() {
+ return PANEL_NAME;
+ }
+
+ /**
+ * Returns the available types
+ *
+ * @return the available types;
+ */
+ @Override
+ protected List getTypes() {
+ List supportedTypes = new ArrayList();
+ supportedTypes.add(TYPE_LABEL_SIFC);
+ supportedTypes.add(TYPE_LABEL_VCARD);
+ return supportedTypes;
+ }
+
+ /**
+ * Returns the type to select based on the given syncsource
+ *
+ * @return the type to select based on the given syncsource
+ */
+ @Override
+ protected String getTypeToSelect(SyncSource syncSource) {
+ String preferredType = null;
+ if (syncSource.getInfo() != null
+ && syncSource.getInfo().getPreferredType() != null) {
+
+ preferredType = syncSource.getInfo().getPreferredType().getType();
+ if (TYPE_VCARD.equals(preferredType)) {
+ return TYPE_LABEL_VCARD;
+ }
+ if (TYPE_SIFC.equals(preferredType)) {
+ return TYPE_LABEL_SIFC;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the source info of the given syncsource based on the given
+ * selectedType
+ */
+ @Override
+ public void setSyncSourceInfo(SyncSource syncSource, String selectedType) {
+ PIMSyncSource pimSource = (PIMSyncSource) syncSource;
+ ContentType[] contentTypes = null;
+ if (TYPE_LABEL_SIFC.equals(selectedType)) {
+ contentTypes = new ContentType[1];
+ contentTypes[0] = new ContentType(TYPE_SIFC, VERSION_SIFC);
+ } else if (TYPE_LABEL_VCARD.equals(selectedType)) {
+ contentTypes = new ContentType[1];
+ contentTypes[0] = new ContentType(TYPE_VCARD, VERSION_VCARD);
+ }
+
+ pimSource.setInfo(new SyncSourceInfo(contentTypes, 0));
+ }
+ // --------------------------------------------------------- Private methods
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMSyncSourceConfigPanel.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMSyncSourceConfigPanel.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/admin/PIMSyncSourceConfigPanel.java (revision 1009)
@@ -0,0 +1,441 @@
+/**
+ * This class implements the configuration panel for a PIMSyncSource.
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.admin;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.border.TitledBorder;
+
+import org.apache.commons.lang.StringUtils;
+
+import br.com.prognus.psync.engine.source.PIMCalendarSyncSource;
+import br.com.prognus.psync.engine.source.PIMContactSyncSource;
+import br.com.prognus.psync.engine.source.PIMSyncSource;
+
+import com.funambol.admin.AdminException;
+import com.funambol.admin.mo.SyncSourceManagementObject;
+import com.funambol.admin.ui.SourceManagementPanel;
+import com.funambol.framework.engine.source.SyncSource;
+
+public abstract class PIMSyncSourceConfigPanel extends SourceManagementPanel
+ implements Serializable {
+
+ // --------------------------------------------------------------- Constants
+
+ protected static final String PANEL_NAME = "Edit PIM SyncSource";
+
+ public static final String NAME_ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.";
+
+ // All possible PIM SyncSource's
+ public static final Class[] SYNCSOURCE = { PIMContactSyncSource.class,
+ PIMCalendarSyncSource.class, };
+
+ // ------------------------------------------------------------ Private data
+
+ /** Label for the panel's name */
+ protected JLabel panelName = new JLabel();
+
+ /** Border to pose the panel's title in evidence */
+ protected TitledBorder titledBorder;
+
+ protected JLabel nameLabel = new JLabel();
+
+ protected JTextField nameValue = new JTextField();
+
+ protected JLabel typeLabel = new JLabel();
+
+ protected JComboBox typeCombo = new JComboBox();
+
+ protected JLabel sourceUriLabel = new JLabel();
+
+ protected JTextField sourceUriValue = new JTextField();
+
+ protected JCheckBox encryption = new JCheckBox();
+
+ protected JCheckBox encoding = new JCheckBox();
+
+ protected JButton confirmButton = new JButton();
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Creates a new instance of PIMSyncSourceConfigPanel.
+ */
+ public PIMSyncSourceConfigPanel() {
+ init();
+ }
+
+ /**
+ * Updates the panel form with values from the linked SyncSource.
+ */
+ public void updateForm() {
+
+ if (!(getSyncSource() instanceof PIMSyncSource)) {
+ notifyError(new AdminException("This is not a PIMSyncSource! "
+ + "Unable to process SyncSource values."));
+ return;
+ }
+
+ PIMSyncSource syncSource = (PIMSyncSource) getSyncSource();
+
+ if (getState() == STATE_INSERT) {
+ confirmButton.setText("Add");
+ } else if (getState() == STATE_UPDATE) {
+ confirmButton.setText("Save");
+ }
+
+ sourceUriValue.setText(syncSource.getSourceURI());
+ nameValue.setText(syncSource.getName());
+
+ if (syncSource.getSourceURI() != null) {
+ sourceUriValue.setEditable(false);
+ }
+
+ // Preparing to populate the combo box...
+ typeCombo.removeAllItems();
+ List types = getTypes();
+ if (types == null) {
+ types = new ArrayList();
+ }
+ for (int i = 0; i < types.size(); i++) {
+ typeCombo.addItem(types.get(i));
+ }
+ String typeToSelect = getTypeToSelect(syncSource);
+ if (typeToSelect != null) {
+ typeCombo.setSelectedItem(typeToSelect);
+ } else {
+ typeCombo.setSelectedIndex(0);
+ }
+
+ SyncSourceManagementObject mo = (SyncSourceManagementObject) getManagementObject();
+ String transformationsRequired = mo.getTransformationsRequired();
+
+ if (transformationsRequired == null) {
+ encryption.setSelected(false);
+ encoding.setSelected(false);
+ } else {
+ if ("des;b64".equals(transformationsRequired)) {
+ encryption.setSelected(true);
+ encoding.setSelected(true);
+ } else if ("b64".equals(transformationsRequired)) {
+ encryption.setSelected(false);
+ encoding.setSelected(true);
+ } else {
+ encryption.setSelected(false);
+ encoding.setSelected(false);
+ }
+ }
+
+ // In any case, force the encoding for SIF data
+ if (isSIFSelected()) {
+ encoding.setSelected(true);
+ encoding.setEnabled(false);
+ }
+
+ }
+
+ /**
+ * Set preferredSize of the panel
+ *
+ * @return preferredSize of the panel
+ */
+ public Dimension getPreferredSize() {
+ return new Dimension(525, 280);
+ }
+
+ // --------------------------------------------------------- Private methods
+
+ /**
+ * Checks if the values provided by the user are all valid. If they are, the
+ * method ends regularly.
+ *
+ * @throws IllegalArgumentException
+ * if name, uri, type or directory are empty (null or
+ * zero-length)
+ */
+ protected void validateValues() throws IllegalArgumentException {
+ String value = null;
+
+ value = nameValue.getText();
+ if (StringUtils.isEmpty(value)) {
+ throw new IllegalArgumentException("Field 'Name' cannot be empty. "
+ + "Please provide a SyncSource name.");
+ }
+
+ if (!StringUtils.containsOnly(value, NAME_ALLOWED_CHARS.toCharArray())) {
+ throw new IllegalArgumentException(
+ "Only the following characters are allowed for field 'Name':"
+ + "\n" + NAME_ALLOWED_CHARS);
+ }
+
+ value = (String) typeCombo.getSelectedItem();
+ if (StringUtils.isEmpty(value)) {
+ throw new IllegalArgumentException("Field 'Type' cannot be empty. "
+ + "Please provide a SyncSource type.");
+ }
+
+ value = sourceUriValue.getText();
+ if (StringUtils.isEmpty(value)) {
+ throw new IllegalArgumentException(
+ "Field 'Source URI' cannot be empty. "
+ + "Please provide a SyncSource URI.");
+ }
+ }
+
+ /**
+ * Sets the SyncSource's properties with the values provided by the user.
+ */
+ protected void getValues() {
+ PIMSyncSource syncSource = (PIMSyncSource) getSyncSource();
+
+ syncSource.setSourceURI(sourceUriValue.getText().trim());
+ syncSource.setName(nameValue.getText().trim());
+
+ String transformationsRequired = null;
+ if (encryption.isSelected()) {
+ transformationsRequired = "des;b64";
+ } else if (encoding.isSelected()) {
+ transformationsRequired = "b64";
+ }
+ SyncSourceManagementObject mo = (SyncSourceManagementObject) getManagementObject();
+ mo.setTransformationsRequired(transformationsRequired);
+
+ setSyncSourceInfo(syncSource, (String) typeCombo.getSelectedItem());
+ }
+
+ /**
+ * Checks whether a SIF content type is selected.
+ *
+ * @return true if in the combo box the selected string starts with "SIF"
+ */
+ protected boolean isSIFSelected() {
+
+ if (typeCombo.getItemCount() == 0) {
+ return false;
+ }
+ return ((String) typeCombo.getSelectedItem()).startsWith("SIF");
+ }
+
+ /**
+ * Adds extra components just above the confirm button.
+ *
+ * @param x
+ * horizontal position
+ * @param y
+ * vertical position
+ * @param xGap
+ * standard horizontal gap
+ * @param yGap
+ * standard vertical gap
+ * @return the new vertical position
+ */
+ protected int addExtraComponents(int x, int y, int xGap, int yGap) {
+ // Nothing
+
+ return y;
+ }
+
+ /**
+ * Returns the panel name
+ *
+ * @return the panel name
+ */
+ protected String getPanelName() {
+ return PANEL_NAME;
+ }
+
+ /**
+ * Returns the available types
+ *
+ * @return the available types;
+ */
+ protected abstract List getTypes();
+
+ /**
+ * Returns the type to select based on the given syncsource
+ *
+ * @return the type to select based on the given syncsource
+ */
+ protected abstract String getTypeToSelect(SyncSource syncSource);
+
+ /**
+ * Sets the source info of the given syncsource based on the given
+ * selectedType
+ *
+ * @param syncSource
+ * the source
+ * @param selectedType
+ * the selected type
+ */
+ protected abstract void setSyncSourceInfo(SyncSource syncSource,
+ String selectedType);
+
+ // --------------------------------------------------------- Private methods
+
+ /**
+ * Creates the panel's components and layout.
+ *
+ * @todo adjust layout
+ */
+ private void init() {
+ // set layout
+ this.setLayout(null);
+ // set properties of label, position and border
+ // referred to the title of the panel
+ titledBorder = new TitledBorder("");
+ panelName.setFont(titlePanelFont);
+ panelName.setText(getPanelName());
+ panelName.setBounds(new Rectangle(14, 5, 316, 28));
+ panelName.setAlignmentX(SwingConstants.CENTER);
+ panelName.setBorder(titledBorder);
+
+ final int LABEL_X = 14;
+ final int VALUE_X = 170;
+ int y = 60;
+ final int GAP_X = 150;
+ final int GAP_Y = 30;
+
+ sourceUriLabel.setText("Source URI: ");
+ sourceUriLabel.setFont(defaultFont);
+ sourceUriLabel.setBounds(new Rectangle(LABEL_X, y, 150, 18));
+ sourceUriValue.setFont(defaultFont);
+ sourceUriValue.setBounds(new Rectangle(VALUE_X, y, 350, 18));
+
+ y += GAP_Y; // New line
+
+ nameLabel.setText("Name: ");
+ nameLabel.setFont(defaultFont);
+ nameLabel.setBounds(new Rectangle(LABEL_X, y, 150, 18));
+ nameValue.setFont(defaultFont);
+ nameValue.setBounds(new Rectangle(VALUE_X, y, 350, 18));
+ y += GAP_Y; // New line
+
+ typeLabel.setText("Type: ");
+ typeLabel.setFont(defaultFont);
+ typeLabel.setBounds(new Rectangle(LABEL_X, y, 150, 18));
+ typeCombo.setFont(defaultFont);
+ typeCombo.setBounds(new Rectangle(VALUE_X, y, 350, 18));
+
+ // What happens when the Type value is changed?
+ typeCombo.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (isSIFSelected()) {
+ encoding.setSelected(true); // SIFs always encoded
+ encoding.setEnabled(false);
+ } else {
+ encryption.setSelected(false);
+ encoding.setSelected(false);
+ encoding.setEnabled(true);
+ }
+ }
+ });
+
+ y += GAP_Y; // New line
+ int x = LABEL_X;
+
+ y = addExtraComponents(x, y, GAP_X, GAP_Y); // Add other components, if
+ // needed
+
+ encryption.setText("Encrypt data");
+ encryption.setFont(defaultFont);
+ encryption.setSelected(false);
+ encryption.setBounds(x, y, 150, 25);
+
+ // What happens if the encryption is enabled?
+ encryption.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == e.SELECTED) {
+ encoding.setSelected(true); // Encryption implies encoding
+ encoding.setEnabled(false);
+ }
+ if (e.getStateChange() == e.DESELECTED) {
+ if (!isSIFSelected()) {
+ encoding.setEnabled(true);
+ }
+ }
+ }
+ });
+
+ y += GAP_Y; // New line
+
+ encoding.setText("Encode data in Base 64");
+ encoding.setFont(defaultFont);
+ encoding.setSelected(false);
+ encoding.setBounds(x, y, 150, 25);
+
+ y += GAP_Y; // New line
+ y += GAP_Y; // New line
+
+ confirmButton.setFont(defaultFont);
+ confirmButton.setText("Add");
+ confirmButton.setBounds(VALUE_X, y, 70, 25);
+
+ // What happens when the confirmButton is pressed?
+ confirmButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ try {
+ validateValues();
+ getValues();
+ if (getState() == STATE_INSERT) {
+ PIMSyncSourceConfigPanel.this
+ .actionPerformed(new ActionEvent(
+ PIMSyncSourceConfigPanel.this,
+ ACTION_EVENT_INSERT, event
+ .getActionCommand()));
+ } else {
+ PIMSyncSourceConfigPanel.this
+ .actionPerformed(new ActionEvent(
+ PIMSyncSourceConfigPanel.this,
+ ACTION_EVENT_UPDATE, event
+ .getActionCommand()));
+ }
+ } catch (Exception e) {
+ notifyError(new AdminException(e.getMessage(), e));
+ }
+ }
+ });
+
+ // Adds all components to the panel
+ this.add(panelName, null);
+ this.add(nameLabel, null);
+ this.add(sourceUriLabel, null);
+ this.add(sourceUriValue, null);
+ this.add(nameValue, null);
+ this.add(typeLabel, null);
+ this.add(typeCombo, null);
+ this.add(encryption, null);
+ this.add(encoding, null);
+ this.add(confirmButton, null);
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SyncRequestStore.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SyncRequestStore.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SyncRequestStore.java (revision 1009)
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.phones.s55;
+
+import java.util.ArrayList;
+
+import com.funambol.framework.core.*;
+import com.funambol.framework.engine.pipeline.InputMessageProcessor;
+import com.funambol.framework.engine.pipeline.MessageProcessingContext;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+import com.funambol.framework.protocol.ProtocolUtil;
+
+/**
+ * This class store the synchronization alert code into processingContext.
+ *
+ * @version $Id: SyncRequestStore.java,v 1.3 2007-01-11 19:20:19 nichele Exp $
+ */
+public class SyncRequestStore implements InputMessageProcessor {
+
+ // ------------------------------------------------------------ Private data
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Process input message and set MessageProcessingContext property.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void preProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+ if (log.isTraceEnabled()) {
+ log.trace("preProcessMessage: SyncRequestStore of input message");
+ }
+
+ Put clientCapabilities = null;
+
+ try {
+ AbstractCommand[] allCmd =
+ (AbstractCommand[])message.getSyncBody().getCommands().toArray(
+ new AbstractCommand[0]);
+ //
+ //Extract only alert commands
+ //
+ for (int i=0; (allCmd != null) && (i0) {
+ clientCapabilities = (Put)clientCapabilitiesList.get(0);
+
+ if (log.isTraceEnabled()) {
+ log.trace("preProcessMessage: name clientCapabilities "
+ + clientCapabilities.getName());
+ }
+
+ DevInfItem item = (DevInfItem)clientCapabilities.getItems().get(0);
+ DevInf di = item.getDevInfData().getDevInf();
+ String man = di.getMan();
+ processingContext.setRequestProperty(SlowFastSyncManagement.PROPERTY_MANTYPE, man);
+ }
+ } catch(Exception e) {
+ log.error("Errore preprocessing the request", e);
+ }
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SlowFastSyncManagement.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SlowFastSyncManagement.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/phones/s55/SlowFastSyncManagement.java (revision 1009)
@@ -0,0 +1,97 @@
+/**
+ * Copyright (C) 2003-2007 Funambol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the Honest Public License.
+ *
+ * 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
+ * Honest Public License for more details.
+ *
+ * You should have received a copy of the Honest Public License
+ * along with this program; if not, write to Funambol,
+ * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
+ */
+package br.com.prognus.psync.phones.s55;
+
+import com.funambol.framework.core.*;
+import com.funambol.framework.engine.pipeline.MessageProcessingContext;
+import com.funambol.framework.engine.pipeline.OutputMessageProcessor;
+import com.funambol.framework.logging.FunambolLogger;
+import com.funambol.framework.logging.FunambolLoggerFactory;
+
+/**
+ * This class check if the request of slow or fast sync is correct.
+ *
+ * @version $Id: SlowFastSyncManagement.java,v 1.3 2007-01-11 19:20:19 nichele Exp $
+ */
+public class SlowFastSyncManagement implements OutputMessageProcessor {
+
+ // --------------------------------------------------------------- Constants
+
+ public static final String PROPERTY_MANTYPE =
+ "funambol.foundation.slowfastsyncmanagement.MANTYPE";
+ public static final String PROPERTY_CODE =
+ "funambol.foundation.slowfastsyncmanagement.CODE" ;
+
+ // ------------------------------------------------------------ Private data
+ private static final FunambolLogger log =
+ FunambolLoggerFactory.getLogger("engine.pipeline");
+
+ private static final String PHONE_SIEMENS = "siemens";
+
+ // ---------------------------------------------------------- Public methods
+
+ /**
+ * Process and manipulate the output message.
+ *
+ * @param processingContext the message processing context
+ * @param message the message to be processed
+ * @throws Sync4jException
+ */
+ public void postProcessMessage(MessageProcessingContext processingContext,
+ SyncML message) throws Sync4jException {
+ if (log.isTraceEnabled()) {
+ log.trace("postProcessMessage: SlowFastSyncManagement of output message");
+ }
+
+ try {
+
+ //
+ //Only if the device is Siemens55 apply the code
+ //
+ String man = (String)processingContext.getRequestProperty(PROPERTY_MANTYPE);
+ if (log.isTraceEnabled()) {
+ log.trace("postProcessMessage: man: " + man);
+ }
+
+ if (man != null && man.toLowerCase().indexOf(PHONE_SIEMENS) != -1) {
+
+ AbstractCommand[] allCmd = (AbstractCommand[])
+ message.getSyncBody().getCommands().toArray(
+ new AbstractCommand[0]);
+ //
+ //Extract only alert commands
+ //
+ for (int i=0; (allCmd != null) && (i
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.util;
+
+import com.funambol.framework.engine.SyncItemState;
+
+public class Def {
+
+ public static final String LOGGER_NAME = "PSYNC";
+
+ public static final char PIM_STATE_NEW = SyncItemState.NEW;
+
+ public static final char PIM_STATE_UPDATED = SyncItemState.UPDATED;
+
+ public static final char PIM_STATE_DELETED = SyncItemState.DELETED;
+
+ public static final char PIM_STATE_UNCHANGED = SyncItemState.UNKNOWN;
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/EntityException.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/EntityException.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/EntityException.java (revision 1009)
@@ -0,0 +1,66 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.exception;
+
+public class EntityException extends Exception {
+
+ /**
+ * Creates a new instance of EntityException without detail
+ * message.
+ */
+ public EntityException() {
+
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified detail message.
+ *
+ * @param msg
+ * the detail message.
+ */
+ public EntityException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified exception.
+ *
+ * @param cause
+ * Throwable
+ */
+ public EntityException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified detail message.
+ *
+ * @param msg
+ * String
+ * @param cause
+ * Throwable
+ */
+ public EntityException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
Index: tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/PIMDBAccessException.java
===================================================================
--- tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/PIMDBAccessException.java (revision 1009)
+++ tags/psync/1.0/src/main/java/br/com/prognus/psync/exception/PIMDBAccessException.java (revision 1009)
@@ -0,0 +1,66 @@
+/**
+ *
+ * @author Diorgenes Felipe Grzesiuk
+ * @copyright Copyright 2007-2008 Prognus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package br.com.prognus.psync.exception;
+
+public class PIMDBAccessException extends Exception {
+
+ /**
+ * Creates a new instance of EntityException without detail
+ * message.
+ */
+ public PIMDBAccessException() {
+
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified detail message.
+ *
+ * @param msg
+ * the detail message.
+ */
+ public PIMDBAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified exception.
+ *
+ * @param cause
+ * Throwable
+ */
+ public PIMDBAccessException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an instance of EntityException with the
+ * specified detail message.
+ *
+ * @param msg
+ * String
+ * @param cause
+ * Throwable
+ */
+ public PIMDBAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
Index: tags/psync/1.0/src/main/bean/psync/calendar/VEventSource.xml
===================================================================
--- tags/psync/1.0/src/main/bean/psync/calendar/VEventSource.xml (revision 1009)
+++ tags/psync/1.0/src/main/bean/psync/calendar/VEventSource.xml (revision 1009)
@@ -0,0 +1,45 @@
+
+
+
+
Index: tags/psync/1.0/src/main/bean/psync/contact/VCardSource.xml
===================================================================
--- tags/psync/1.0/src/main/bean/psync/contact/VCardSource.xml (revision 1009)
+++ tags/psync/1.0/src/main/bean/psync/contact/VCardSource.xml (revision 1009)
@@ -0,0 +1,32 @@
+
+
+
+
+ catalogo
+
+
+ catalogo
+
+
+
+
+
+
+
+
+ text/x-vcard
+
+
+ 2.1
+
+
+
+
+
+
+ 0
+
+
+
+
+