/** * 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 ); } }