/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2009 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.common.pim.xvcalendar;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import com.funambol.common.pim.ArrayList;
import com.funambol.util.StringUtil;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMList;
import javax.microedition.pim.EventList;
import javax.microedition.pim.ToDoList;
import javax.microedition.pim.PIMItem;
import javax.microedition.pim.Event;
import javax.microedition.pim.ToDo;
import com.funambol.util.Log;
import com.funambol.util.ConsoleAppender;
import com.funambol.common.pim.vcalendar.CalendarTestUtils;
import com.funambol.common.pim.vcalendar.BasicVCalendar;
import junit.framework.*;
/**
* This is an end-to-end test for the xvCalendar parsing/formatting process.
* The input test vCalendar test cases must have the following supported
* fields, in order to compare them with the formatter output:
*
* Events mandatory fields:
* - SUMMARY
* - LOCATION
* - DTSTART (in UTC)
* - DTEND (in UTC)
* - NOTE
* - UID
* - LAST-MODIFIED (in UTC)
* - CLASS
* Events optional fields:
* - RRULE
*
* Tasks mandatory fields:
* - SUMMARY
* - STATUS
* - NOTE
* - UID
* - LAST-MODIFIED (in UTC)
* - CLASS
* - PRIORITY
* Tasks optional fields:
* - DUE (in UTC)
* - COMPLETED (in UTC)
* - RRULE
*
* Alarm required fields (alarm component is optional):
* - TRIGGER
* - ACTION (only AUDIO is supported)
*
* Encoding note: in all the text fields if encoding is required (e.g. there are
* some special chars), there shall be the ENCODING and CHARSET
* params as: ";ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8"
* Special chars without encoding are not supported.
*/
public class XVCalendarEndToEndTest extends TestCase {
private String taskSuppFields[] = { BasicVCalendar.BEGIN_VCALENDAR,
BasicVCalendar.END_VCALENDAR,
BasicVCalendar.BEGIN_VEVENT,
BasicVCalendar.END_VEVENT,
BasicVCalendar.BEGIN_VTODO,
BasicVCalendar.END_VTODO,
BasicVCalendar.SUMMARY,
BasicVCalendar.LOCATION,
BasicVCalendar.DTEND,
BasicVCalendar.DUE,
BasicVCalendar.DURATION,
BasicVCalendar.DESCRIPTION,
BasicVCalendar.CLASS,
BasicVCalendar.LAST_MODIFIED,
BasicVCalendar.UID,
BasicVCalendar.PRIORITY,
BasicVCalendar.STATUS,
BasicVCalendar.ATTENDEE,
BasicVCalendar.VERSION};
private String eventSuppFields[] = { BasicVCalendar.BEGIN_VCALENDAR,
BasicVCalendar.END_VCALENDAR,
BasicVCalendar.BEGIN_VEVENT,
BasicVCalendar.END_VEVENT,
BasicVCalendar.BEGIN_VTODO,
BasicVCalendar.END_VTODO,
BasicVCalendar.SUMMARY,
BasicVCalendar.LOCATION,
BasicVCalendar.DTSTART,
BasicVCalendar.DTEND,
BasicVCalendar.DUE,
BasicVCalendar.DURATION,
BasicVCalendar.DESCRIPTION,
BasicVCalendar.CLASS,
BasicVCalendar.LAST_MODIFIED,
BasicVCalendar.UID,
BasicVCalendar.ATTENDEE,
BasicVCalendar.VERSION};
public XVCalendarEndToEndTest(String name) {
super(name);
Log.initLog(new ConsoleAppender());
Log.setLogLevel(Log.TRACE);
}
public void setUp() {
Log.setLogLevel(Log.TRACE);
}
public void tearDown() {
}
/**
* Iterate all the iCalendar test cases
*/
public void testEndToEnd() throws Throwable {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("--- endToEndTest ---");
}
InputStream eventsStream = getClass().getResourceAsStream(
"/res/xvcalendar/vcalendar-events.vcs");
InputStream todosStream = getClass().getResourceAsStream(
"/res/xvcalendar/vcalendar-todos.vcs");
EventList eList = (EventList) PIM.getInstance().openPIMList(PIM.EVENT_LIST,PIM.READ_WRITE);
ToDoList tList = (ToDoList) PIM.getInstance().openPIMList(PIM.TODO_LIST,PIM.READ_WRITE);
String vcal;
do {
vcal = CalendarTestUtils.getNextCalendarItem(eventsStream);
vcal = removeUnsupportedFields(vcal, eventSuppFields);
if (vcal.length() > 0) {
Event item = eList.createEvent();
singleVCalTest(vcal, item);
}
} while (vcal.length() > 0);
do {
vcal = CalendarTestUtils.getNextCalendarItem(todosStream);
vcal = removeUnsupportedFields(vcal, taskSuppFields);
if (vcal.length() > 0) {
ToDo item = tList.createToDo();
singleVCalTest(vcal, item);
}
} while (vcal.length() > 0);
}
/**
* Test flow for each test case:
* 1 - parse iCalendar item -> PIMItem
* 2 - format PIMItem -> iCalendar item
* 3 - order iCalendar items fields of the items coming from points 1 and 3
* 4 - compare the iCalendar strings
*/
public void singleVCalTest(String originalVcal, PIMItem pimItem) throws Throwable {
// parsing vCalendar
ByteArrayInputStream is = new ByteArrayInputStream(originalVcal.getBytes());
XVCalendarSyntaxParserListener lis = new XVCalendarParserListener(pimItem);
XVCalendarSyntaxParser parser = new XVCalendarSyntaxParser(is);
parser.setListener(lis);
parser.parse();
// formatting iCalendar
XVCalendarFormatter formatter = new XVCalendarFormatter();
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
formatter.format(pimItem, ostream, false);
String formattedVcal = ostream.toString();
formattedVcal = removeAllDayAndEmptyFields(formattedVcal, originalVcal);
originalVcal = removeEmptyFields(originalVcal);
// ordering
formattedVcal = orderXVCalendar(formattedVcal);
originalVcal = orderXVCalendar(originalVcal);
// comparing
if(!formattedVcal.equals(originalVcal)) {
if (Log.isLoggable(Log.TRACE)) {
Log.trace("[XVCalendarEndToEndTest.singleVCalTest] formattedVcal: " + formattedVcal);
}
if (Log.isLoggable(Log.TRACE)) {
Log.trace("[XVCalendarEndToEndTest.singleVCalTest] originalVcal: " + originalVcal);
}
}
assertTrue(formattedVcal.equals(originalVcal));
}
/**
* Order the iCalendar item fields alphabetically.
*/
private String orderXVCalendar(String ical) {
String sep[] = {"\r\n"};
String lines[] = StringUtil.split(ical, sep);
ArrayList fields_al = new ArrayList();
String field = "";
for(int i=0;i<lines.length;++i) {
String line = lines[i];
if(line.length() > 0 && line.charAt(0) == com.funambol.common.pim.Utils.FOLDING_INDENT_CHAR) {
// this is a multi line field
field += line.substring(1); // cut the indent char
} else {
if(!field.equals("")) {
fields_al.add(field);
}
field = line;
}
}
// add the latest field
fields_al.add(field);
// order the fields array list
String result = "";
String[] fields = StringUtil.getStringArray(fields_al);
for(int i=0; i<fields.length; i++) {
for(int j=fields.length-1; j>i; j--) {
if(fields[j].compareTo(fields[j-1])<0) {
String temp = fields[j];
fields[j] = fields[j-1];
fields[j-1] = temp;
}
}
result += fields[i] + "\r\n";
}
return result;
}
protected String removeUnsupportedFields(String val, String suppFields[]) {
String sep[] = {"\r\n"};
String lines[] = StringUtil.split(val, sep);
StringBuffer vcalOut = new StringBuffer();
for(int i=0;i<lines.length;++i) {
String line = lines[i];
boolean supported = false;
for(int j=0;j<suppFields.length;++j) {
String supp = suppFields[j];
if (line.startsWith(supp)) {
supported = true;
break;
}
}
if (supported) {
vcalOut.append(line).append("\r\n");
}
}
return vcalOut.toString();
}
protected String removeAllDayAndEmptyFields(String formattedVcal, String originalVcal) {
int pos = originalVcal.indexOf("X-FUNAMBOL-ALLDAY");
if (pos != -1) {
return formattedVcal;
} else {
String sep[] = {"\r\n"};
String lines[] = StringUtil.split(formattedVcal, sep);
StringBuffer vcalOut = new StringBuffer();
for(int i=0;i<lines.length;++i) {
String line = lines[i];
if (!line.startsWith("X-FUNAMBOL-ALLDAY")) {
if (!line.endsWith(":")) {
vcalOut.append(line).append("\r\n");
}
}
}
return vcalOut.toString();
}
}
protected String removeEmptyFields(String vcal) {
String sep[] = {"\r\n"};
String lines[] = StringUtil.split(vcal, sep);
StringBuffer vcalOut = new StringBuffer();
for(int i=0;i<lines.length;++i) {
String line = lines[i];
if (!line.endsWith(":")) {
vcalOut.append(line).append("\r\n");
}
}
return vcalOut.toString();
}
}